Files
ChronicleOfTheImmortalClan/src/renderer/ui/panels/TerritoryPanel.tsx
T
thzxx fefbe1f1d7 v0.1.2: 宗族气韵(职事/悟道/谱系/碑录/庆典/飞升/音效)
- 职事任命:长老(+修炼)/供奉(+战力)/执事(+坊市)/掌教(+闭关) 每人加成叠加,上限管理
- 功法悟道:修习积悟性点,小成/大成逐级+战力,大境界突破传道于直系晚辈
- 宗族谱系页:世代分带 + 夫妻同卷 + 子孙连枝,点节点开详情,先人簿可查
- 功德碑:宗主辞世即勒石,宗祠页碑录永存
- 百年庆典 + 飞升之路事件链(留世坐镇/仙风入体 或 云游飞升/宗族蒙荫)
- 合成音效(WebAudio):鼓点/钟鸣/纸韵/磬响,设置页开关
- 单测 29→38;typecheck/build 通过;GUI 冒烟受限于 WSLg 掉线(SMOKE-TIMEOUT 优雅退出)
2026-08-23 08:28:49 +08:00

136 lines
5.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useGameStore } from '../store'
import { BUILDINGS } 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
if (!world) return null
const fam = world.state.family
const ids = useMemo(() => Object.keys(BUILDINGS), [])
const levelLabel = (l: number) => '·'.repeat(l) + '。'.repeat(5 - l)
return (
<div>
<div className="help-text">
族产经营:建造建筑后每月自动产出;坊市、丹房等基础设施可解锁功能。
灵石与灵矿不足则无法建造或升级。
</div>
<div className="bld-grid">
{ids.map((id) => {
const def = BUILDINGS[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
return (
<div key={id} className={`bld-card ${built ? '' : 'dim2'}`}>
<div className="bld-head">
<span className="bld-icon">{def.icon}</span>
<div>
<div>{def.name}</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>
{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 === '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>
)}
<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
}