207 lines
8.9 KiB
TypeScript
207 lines
8.9 KiB
TypeScript
import { useGameStore } from '../store'
|
||
import { BUILDINGS, buildingById, listBuildingDefs, SPECIALTY_MULT, bonusOf, isSpecialty } from '../../game/data/buildings'
|
||
import { useMemo } from 'react'
|
||
|
||
export default function TerritoryPanel() {
|
||
const world = useGameStore((s) => s.world)
|
||
const bump = useGameStore((s) => s.bump)
|
||
const revision = useGameStore((s) => s.revision)
|
||
void revision
|
||
const ids = useMemo(() => listBuildingDefs().map((d) => d.id), [])
|
||
if (!world) return null
|
||
const fam = world.state.family
|
||
|
||
const levelLabel = (l: number) => '·'.repeat(l) + '。'.repeat(5 - l)
|
||
|
||
return (
|
||
<div>
|
||
<div className="help-text">
|
||
族产经营:建造建筑后每月自动产出;坊市、丹房等基础设施可解锁功能。
|
||
灵石与灵矿不足则无法建造或升级。
|
||
</div>
|
||
<MonumentList />
|
||
<div className="bld-grid">
|
||
{ids.map((id) => {
|
||
const def = buildingById(id)
|
||
if (!def) return null
|
||
const level = fam.buildings[id] ?? 0
|
||
const built = level > 0
|
||
const cost = built && level >= def.maxLevel ? null : def.upgradeCost(built ? level + 1 : 1)
|
||
const produce = def.produceTable ? def.produceTable(built ? level : 1) : null
|
||
const canUpgrade =
|
||
!!cost &&
|
||
fam.stones >= cost.stones &&
|
||
fam.inventory['lingkuang'] >= cost.lingkuang
|
||
const isSpecialtyBuilding = fam.flag['buildingSpecialty'] === id
|
||
return (
|
||
<div key={id} className={`bld-card ${built ? '' : 'uc'} ${isSpecialtyBuilding ? 'bld-specialty' : ''}`}>
|
||
<div className="bld-head">
|
||
<span className="bld-icon">{def.icon}</span>
|
||
<div>
|
||
<div>{def.name}{isSpecialtyBuilding && <span className="gold" style={{ marginLeft: 4, fontSize: '0.75rem' }}>【专精】</span>}</div>
|
||
<div className="dim2" style={{ fontSize: '0.75rem' }}>{def.kind === 'produce' ? '产出' : '功效'}</div>
|
||
</div>
|
||
<span className="bld-lv">{built ? `${level}级` : ''}</span>
|
||
</div>
|
||
<div className="bld-desc">{def.desc}</div>
|
||
{/* 0.1.41 建筑专精加成预览 */}
|
||
{built && def.extra && Object.keys(def.extra).length > 0 && (() => {
|
||
const specialtyId = fam.flag['buildingSpecialty'] as string | undefined
|
||
const currentLevel = level
|
||
const isThisSpecialty = isSpecialty(id, specialtyId)
|
||
const bonusLabels: { name: string; base: number; specialty: number }[] = []
|
||
for (const [key, expr] of Object.entries(def.extra)) {
|
||
void expr
|
||
const base = bonusOf(id, key, currentLevel, undefined)
|
||
const spec = bonusOf(id, key, currentLevel, id)
|
||
bonusLabels.push({ name: extraLabel(key), base, specialty: spec })
|
||
}
|
||
return (
|
||
<div className="bld-prod" style={{ fontSize: '0.72rem', color: '#a89878' }}>
|
||
{bonusLabels.map((b, i) => (
|
||
<span key={i} style={{ marginRight: 8 }}>
|
||
{b.name}: {b.base.toFixed(2)}
|
||
{isThisSpecialty && <span className="gold"> →x{SPECIALTY_MULT} = {(b.base * SPECIALTY_MULT).toFixed(2)}</span>}
|
||
{!isThisSpecialty && specialtyId && <span className="dim2"> (设为专精可至 {(b.base * SPECIALTY_MULT).toFixed(2)})</span>}
|
||
</span>
|
||
))}
|
||
</div>
|
||
)
|
||
})()}
|
||
{produce && Object.keys(produce).length > 0 && (
|
||
<div className="bld-prod">
|
||
{Object.entries(produce)
|
||
.filter(([k]) => k !== 'beastcore' || (built && level >= 3))
|
||
.map(([k, v]) => `${itemLabel(k)} ×${v}`)
|
||
.join(' / ')}
|
||
/月
|
||
</div>
|
||
)}
|
||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 'auto' }}>
|
||
{built ? (
|
||
<>
|
||
{level < def.maxLevel ? (
|
||
<button
|
||
className="btn btn-sm"
|
||
disabled={!canUpgrade}
|
||
onClick={() => {
|
||
world.upgrade(id)
|
||
bump()
|
||
}}
|
||
>
|
||
升至{level + 1}级({cost?.stones ?? 0}灵石 + {cost?.lingkuang ?? 0}灵矿)
|
||
</button>
|
||
) : (
|
||
<span className="gold">已臻极致</span>
|
||
)}
|
||
{id === 'cangshu' && (
|
||
<button
|
||
className="btn btn-sm"
|
||
disabled={(fam.buildings['cangshu'] ?? 0) < 3 || fam.stones < 150 || world.state.year - ((fam.flag['sutraCD'] as number) ?? -9) < 2}
|
||
title={((fam.buildings['cangshu'] ?? 0) < 3 ? '需藏书阁3级' : fam.stones < 150 ? '需150灵石' : '两年一访')}
|
||
onClick={() => {
|
||
world.seekSutra()
|
||
bump()
|
||
}}
|
||
>
|
||
求经
|
||
</button>
|
||
)}
|
||
{id === 'zongci' && (
|
||
<button
|
||
className="btn btn-sm btn-primary"
|
||
disabled={world.state.year - ((fam.flag['lastRiteYear'] as number) ?? -9) < 2 || fam.stones < 150}
|
||
title="耗灵石150 · 每两年一回 · 声望+6 · 全族修为气血微升"
|
||
onClick={() => {
|
||
world.ancestralRite()
|
||
bump()
|
||
}}
|
||
>
|
||
祭祖{world.state.year - ((fam.flag['lastRiteYear'] as number) ?? -9) < 2 ? '(冷却)' : '150灵石'}
|
||
</button>
|
||
)}
|
||
{built && def.extra && Object.keys(def.extra).length > 0 && (
|
||
<button
|
||
className="btn btn-sm"
|
||
style={{ marginLeft: 4, fontSize: '0.7rem' }}
|
||
disabled={isSpecialtyBuilding ? false : fam.stones < 200}
|
||
title={isSpecialtyBuilding ? '取消专精(免费)' : `设为专精(耗200灵石)· extra 加成 ×${SPECIALTY_MULT}`}
|
||
onClick={() => {
|
||
if (isSpecialtyBuilding) {
|
||
delete fam.flag['buildingSpecialty']
|
||
} else {
|
||
if (fam.stones < 200) return
|
||
fam.stones -= 200
|
||
fam.flag['buildingSpecialty'] = id
|
||
}
|
||
bump()
|
||
}}
|
||
>
|
||
{isSpecialtyBuilding ? '取消专精' : '设为专精'}
|
||
</button>
|
||
)}
|
||
<span className="dim2" style={{ marginLeft: 'auto' }}>{levelLabel(level)}</span>
|
||
</>
|
||
) : (
|
||
<button
|
||
className="btn btn-sm btn-primary"
|
||
disabled={!canUpgrade}
|
||
onClick={() => {
|
||
world.build(id)
|
||
bump()
|
||
}}
|
||
>
|
||
营建({cost?.stones ?? 0}灵石 + {cost?.lingkuang ?? 0}灵矿)
|
||
</button>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)
|
||
})}
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
function MonumentList() {
|
||
const world = useGameStore((s) => s.world)
|
||
const revision = useGameStore((s) => s.revision)
|
||
void revision
|
||
if (!world) return null
|
||
const monuments = world.state.chronicle.filter((e) => e.text.startsWith('功德碑')).slice().reverse()
|
||
if (monuments.length === 0) return null
|
||
return (
|
||
<div className="monument-strip">
|
||
<div className="card-title" style={{ fontSize: '0.98rem' }}>宗祠碑录</div>
|
||
{monuments.slice(0, 4).map((m) => (
|
||
<div key={m.id} className="dim" style={{ lineHeight: 1.7 }}>
|
||
<span className="ch-year" style={{ marginRight: 8 }}>{m.year}年</span>
|
||
{m.text.slice(3)}
|
||
</div>
|
||
))}
|
||
</div>
|
||
)
|
||
}
|
||
|
||
function itemLabel(k: string): string {
|
||
const map: Record<string, string> = {
|
||
stone: '灵石',
|
||
lingcao: '灵草',
|
||
lingkuang: '灵矿',
|
||
beastcore: '兽核'
|
||
}
|
||
return map[k] ?? k
|
||
}
|
||
|
||
/** 0.1.41 建筑加成属性中文名 */
|
||
function extraLabel(key: string): string {
|
||
const map: Record<string, string> = {
|
||
craftChance: '成丹率',
|
||
unlockGrade: '解锁品阶',
|
||
expBonus: '修炼加成',
|
||
powerBonus: '战力加成',
|
||
repBonus: '声望'
|
||
}
|
||
return map[key] ?? key
|
||
}
|