v0.1.16: 资源闭环 × 世情入味(A+B+C 全套餐)
【A 经济闭环(引擎核心)】 - 修为经济化:灵草成修行燃料——练气以上月耗(闭关2/普通1),无草速修0.6~1.0衰减+缺草每年一告 - 炼丹等级化:bonusOf 白名单表达式求值器(extra 单源);craftChance 转正(L5=100%),丹方 PILL_RECIPES(破境丹需丹房3级),失败折半退料 - 炼器筑宝:FORGE_RECIPES(法器/灵器/法宝)灵矿兽核入炉 - 秘灵灵脉:secretQiOf→rollWarbooty 收益门槛(0.5~1.5 折算),掉落按秘境 techGrades 过滤(暗墨林不再出仙典) - NPC 实力参战:raid 强度/风险随 npc.power 浮动(0.5~1.8)——外交成长期博弈 - 灾年落效:CALAMITY_FAMILY 月度减产乘子 + 疫病/兽潮一次性体感 【B 世界活在 UI】 - 天下面板:快讯时间线/行情/灾年/群雄谱(宗主详情+换代数)+ 结盟(setAlliance,关联岁差不降+年利) - 结盟引擎 truly 落地:allied 字段第一次被写值 【C 写史与治理】 - 手写史书:ChroniclePanel 输入条(misc 入史)+ MemberModal 册立家主 + 年报要闻/辞世讣告 + EventModal 收益预览 - C13 破境丹渡劫修正:需渡劫大境界时药力→tribBoost(+12%)入渡劫事件,不再跳三选 - C14 存储防线:loadState 过 migrate;MigrationError(TOO_NEW/未知版本) 醒目提示 - C15 registry 补全(aspirations/formations/season)+ startNewGame facade 复用 bug 【测试】986 全绿(37 套件,新增经济闭环 7 例);金钟罩 0.1.16 基线固化(修为耗草/炼丹/灾害序列变更受控)
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
import { useGameStore } from '../store'
|
||||
import { npcById } from '../../game/data/npcs'
|
||||
import { MAJOR_NAMES } from '../../game/data/realms'
|
||||
import { POOL_BASE } from '../../game/engine/sim/worldsim-data'
|
||||
import { useState } from 'react'
|
||||
|
||||
const RES_LABEL: Record<string, string> = { lingcao: '灵草', lingkuang: '灵矿', beastcore: '兽核' }
|
||||
const REALM_IDX = ['mortal', 'qi', 'foundation', 'core', 'nascent', 'spirit'] as const
|
||||
|
||||
export default function WorldPanel() {
|
||||
const world = useGameStore((s) => s.world)
|
||||
const bump = useGameStore((s) => s.bump)
|
||||
const revision = useGameStore((s) => s.revision)
|
||||
void revision
|
||||
const [view, setView] = useState<string | null>(null)
|
||||
if (!world) return null
|
||||
const w = world
|
||||
const ws = w.state.worldSim
|
||||
if (!ws) return <div className="help-text">天机未显……(世界演化尚未展开)</div>
|
||||
|
||||
const tide = ws.tide ?? 0.5
|
||||
const tideLabel = tide > 0.9 ? '灵涨' : tide < 0.7 ? '灵衰' : '汐平'
|
||||
const pool = ws.marketPool ?? {}
|
||||
const news = [...(ws.newsFeed ?? [])].slice().reverse()
|
||||
const calamity = ws.calamity
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="help-text">
|
||||
天下时局尽览:行市起落、宗主更替、灾年吉凶——皆随世界自演化而来。
|
||||
结盟后岁差不降,每年另有盟利奉上(需关系≥40)。
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 12, flexWrap: 'wrap', marginBottom: 10 }}>
|
||||
<span className="tag gold-t">灵潮 {tideLabel}({Math.round(tide * 100)}%)</span>
|
||||
{calamity
|
||||
? <span className="tag" style={{ borderColor: '#a33', color: '#e8a08a' }}>灾年·{calamity}(灵植减产,市价将行)</span>
|
||||
: <span className="tag">风调雨顺</span>}
|
||||
{Object.entries(pool).map(([k, v]) => {
|
||||
const base = POOL_BASE[k as keyof typeof POOL_BASE] ?? 100
|
||||
const r = ((v as number) / base) * 100
|
||||
const cls = r > 115 ? 'bad' : r < 85 ? 'good' : 'dim'
|
||||
const dir = r > 105 ? '↑' : r < 95 ? '↓' : '→'
|
||||
return (
|
||||
<span key={k} className="tag"><span className={cls}>{RES_LABEL[k] ?? k}{dir}{Math.round(r)}%</span></span>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
|
||||
<div>
|
||||
<h4 className="dim" style={{ margin: '4px 0 6px' }}>天下快讯</h4>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 6, maxHeight: 380, overflowY: 'auto' }}>
|
||||
{news.length === 0 && <div className="dim2">风平浪静,尚无消息。</div>}
|
||||
{news.map((row, i) => (
|
||||
<div key={i} className="ch-item" style={{ padding: '5px 8px' }}>
|
||||
<span className="ch-month">{row.year}年{row.month}月</span>
|
||||
<span>{row.text}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="dim" style={{ margin: '4px 0 6px' }}>群雄谱(点开宗主详情)</h4>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
||||
{Object.values(w.state.npcFamilies).map((npc) => {
|
||||
const def = npcById(npc.id)
|
||||
const dyn = (ws.npcDyn ?? {})[npc.id]
|
||||
const open = view === npc.id
|
||||
return (
|
||||
<div key={npc.id} style={{ border: '1px solid rgba(140,100,50,0.3)', borderRadius: 6, padding: '7px 10px', background: 'rgba(18,14,9,0.4)' }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', cursor: 'pointer' }} onClick={() => setView(open ? null : npc.id)}>
|
||||
<span>
|
||||
<b>{npc.name}</b> <span className="dim">{def.style}</span>
|
||||
{npc.allied && <span className="tag gold-t" style={{ marginLeft: 6 }}>盟</span>}
|
||||
</span>
|
||||
<span className="dim">势力{npc.power} · 关系{npc.relation}</span>
|
||||
</div>
|
||||
{open && (
|
||||
<div style={{ marginTop: 6, fontSize: '0.86rem', color: '#c8b285', borderTop: '1px solid rgba(140,100,50,0.25)', paddingTop: 6 }}>
|
||||
<div className="dim">宗主:{dyn?.leaderName ?? '未知'}({dyn ? MAJOR_NAMES[REALM_IDX[dyn.leaderRealmIdx] ?? 'qi'] : ''} · {dyn?.leaderAge ?? '?'}岁)</div>
|
||||
<div className="dim2" style={{ margin: '2px 0 6px' }}>{def.desc}</div>
|
||||
<div className="dim">新近之事:{dyn?.lastEvent ? `${dyn.lastEvent}(${dyn.lastEventYear ?? '?'}年)` : '暂无'}</div>
|
||||
{npc.alliedSinceYear && <div className="dim">同盟自 {npc.alliedSinceYear} 年</div>}
|
||||
<div style={{ marginTop: 6, display: 'flex', gap: 6 }}>
|
||||
<button
|
||||
className="btn btn-sm"
|
||||
disabled={npc.allied || npc.relation < 40}
|
||||
title={npc.relation < 40 ? '关系不足(需40)' : ''}
|
||||
onClick={() => { w.setAlliance(npc.id, true); bump() }}
|
||||
>缔盟</button>
|
||||
{npc.allied && (
|
||||
<button
|
||||
className="btn btn-sm"
|
||||
onClick={() => { w.setAlliance(npc.id, false); bump() }}
|
||||
>解盟(关系-20)</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user