Files
ChronicleOfTheImmortalClan/AGENTS.md
T
thzxx b7c5f1eca0 docs: 四文档同步 0.1.33 补全——金钟罩基线注释/生态闭环纪律/符箓坊建筑/动效纪律
AGENTS:金钟罩 0.1.32→0.1.33 基线注释(符箓战力/灾年入产/tribBoost 清零时序内变更)+ 新增生态闭环纪律段
MOD_GUIDE:建筑 id 表补 fuzhifang(消耗式产符)
MEMORY:新增 8c 决策(闭环三问/动效空态纪律/测试编写教训)
README:确认 0.1.33 沿革已在
2026-08-23 21:47:38 +08:00

14 KiB
Raw Blame History

AGENTS.md

仙途家族志 · Chronicle of the Immortal Clan — Electron + React + TS 家族修仙模拟器。全部 UI 与文案为中文。 当前版本 0.1.33(《闭环·流光》:物品生态闭环(符箓生效/僵尸物品归位/灾年入产)+ 引擎止血 15 项 + UI 动效打磨(Modal 过渡/停帧节能/季节对齐)+ 测试 5230)。

命令

npm test              # vitest 引擎测试(tests/*.test.ts,全部纯 Node,无 DOM/DB 依赖)
npx vitest run tests/world.test.ts     # 单文件测试
npm run typecheck     # tsc --noEmitnpm 会打 warn 噪音,用 npx tsc --noEmit 更干净)
npm run dev           # electron-vite 热更新(开发期 origin 为 http://localhost:5173
npm run build         # 产物到 out/
npm run package       # build + electron-builderLinux 打 win 包可出产物,但未签名,Windows 上可能被 SmartScreen 静默拦截;正规发布在 Windows 侧重跑此命令)

端到端冒烟(渲染 + OPFS 数据库 + 开局推进 + 可选截图):

npm run build
SMOKE_TEST=1 npx electron out/main/index.js --no-sandbox --disable-gpu   # 退出码 0 = 全通
SMOKE_TEST=1 SMOKE_SHOTS_DIR=/tmp/opencode/shots npx electron ...        # 附带 UI 截图

冒烟会写真实用户数据目录(Linux 下 ~/.config/Electron),跑完删掉该目录以免脏数据。 无显示环境(WSLg 掉线)会打印 [SMOKE-TIMEOUT] 退出码 2 而非卡死;GUI 冒烟必须在 X11/WSLg 在线时跑(引擎与存储逻辑的回归请靠 vitest,不要依赖 GUI)。另注意:npx electron 可能拉取新版 electron 缓存版(与本仓库 33.x 不同),二进制安装以 node_modules/electron 为准。

环境坑

  • npm 12 拦截 postinstall:首次安装后须 npm install-scripts approve electron esbuild,否则 Electron 二进制缺失。
  • npm rebuild electron esbuild 会删掉 node_modules/electron/dist/electronLinux 二进制),修复: ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ node node_modules/electron/install.js
  • 私有 registry 的 _auth token 只在用户级 ~/.npmrc;项目 .npmrc 只保留 registry 映射,不要把 token 写进仓库。
  • Linux 无 emoji 字体:所有图标一律用汉字印章字符renderer/ui/styles.css 的 .s-icon/.res-icon/.bld-icon),不要引入 emoji。

架构(0.1.14 大重构后,目录与路径以此为准)

src/renderer/game/                  纯 TS 引擎(无 React/DOMvitest 直测)
  engine/GameEngine.ts              唯一门面:advance/restore/engineFromSnapshot/about/resolvePending
  engine/kernel/                    Kernel.ts(三合一:Clock+Rng+BUS 转发)+ clock/rng/plugin/names/format/guide/urgency/timesense
  engine/runtime/                   World.ts(大对象+normalize+bus 转发)/clocks.ts(时轮注册)/ApiFacade.tsGameFacade
                                    Systems/production/aging/cultivation/missions/events/diplomacy/marriage/tribulation/tournament/lifecycle/combat
                                    capabilities.ts12 能力卡)/pluginManager.ts/plugin-bootstrap.ts/modSchema.tsMOD 范式)/modManager.tsMOD→插件)/creation.ts/pcgen.ts
  engine/sim/                       世界自进化: WorldSim.ts / worldsim-data.ts(数值与常量唯一权威)/worldgen.ts(世界种子生成器)/Market.ts / worldsim-brief.ts
  engine/narrative/                 族谱/传记/定鼎/年轴/百年报告
  data/                             balance 表 + DataPackRegistryregistry.ts pack() 单源;PILL_RECIPES/FORGE_RECIPES/ERA_CONF 等)
  storage/                          db(MetonaSqlark 单例)/slots(槽+快照+exportEnvelope)/migrate(CURRENT_SCHEMA=2,即 normalize 即迁移)
  types/domain.ts                   全量领域类型(改状态结构先看它)
src/renderer/ui/                    React + zustandstore.ts 主状态;面板按 panel switch 挂载;订阅 revision 刷新)
src/main/                           Electron 主进程(app:// 协议 + IPC 存档导出/导入)

引擎红线(0.1.14+

  • 统一时轮:月度 phaseproduction/aging/cultivation/missions/events/diplomacy/epilogue+ 年首钩子均注册于 runtime/clocks.ts新增系统 = 注册一行,禁止手改 advanceMonth 本体World.ts:329)。
  • 统一随机:引擎只经 World.rngUI 播种用 RngHub.rollSeed()、音效白噪用 RngHub.audioNoise01()不要在逻辑里引入 Math.random()(引擎域 0 处)。渲染层 canvas 粒子散布的 Math.random 不进引擎状态,可留。
  • 引擎单线程定论0.1.21 实测):advanceMonth 2160 月平均 0.05ms/tick——无 Worker 化必要;拖帧热点在 UI 渲染与存档(都已在 0.1.20/0.1.21 修)。
  • 事件闸pending 事件唯一写入入口是 events.tsfire(w, id, priority)(0=普通遇占放弃 / 1=高优 raid·渡劫·大比可顶替被占者,被顶替者入 eventQueue 下月兑现不丢失)。禁止直写 state.pendingEvent(渡劫/破境丹均已改走 fire)。applyEventChoice 前置校验 pendingEvent===id(陈旧 Modal 二次结算防线)。
  • 特效/音效语义出口WorldEventBus.onFx?(em: FxEmit) + World.emitFx(kind, source?)(零 rng 消耗);UI 的 makeBus 桥接 FxGateonLog kind 音效映射保留作兜底。
  • 插件架构0.1.8 起,路径已迁 engine/runtime|kernel):kernel/plugin.ts 协议、runtime/pluginManager.ts 安装管线(依赖校验/异常回滚/追踪型上下文——注销时钩子全摘)、plugin-bootstrap.ts 三核心插件(core-systems/core-data/core-events protected)。事件多池 world.eventPoolsfindEvent 必须带 world 参数查池。能力卡 12 张经 viaCap 注册;tournament/tribulation/apprentice/season 四卡各自内部检查 w.sysEnabled(无独立钩子)。
  • MOD 红线(0.1.26 规范)MOD 只含 JSON 零脚本(docs/MOD_GUIDE.md);禁止新增 rng 消耗(全局随机序列);不改核心时序(扩展走五口:事件池/模板/词库/灾因/配方);modPreflight+validateMod 先行(id 冲突用 findEvent 权威检测);MOD 只影响新开档世界(老档世界变形成档记录);MOD 状态随 state.plugins 持久化(对应 worldGen.npcs 落档保证读档永稳)。
  • 门面runtime/ApiFacade.ts GameFacadeact 24 项/query 6 类/subscribe 退订协议/about);UI 面板事实走 world 直调act 为测试/自动化通道),actDirect 与 ACT_CATALOG 语义对齐(0.1.20 补齐全 24 项);store 的 facade 必须在 openState/startNewGame 赋值。
  • 金钟罩(双基线)tests/clock.test.tsGOLDEN(直调冻结世界)+ GOLDEN_RESOLVED(自动 resolve 长跑"现实"世界),各 3 seed × 三档月数(560/1200/2160)共 18 值。当前为 0.1.33 基线0.1.25/0.1.26/0.1.31 零漂;0.1.27/0.1.28/0.1.29/0.1.30/0.1.32/0.1.33 受控变更重算——0.1.33 符箓战力乘量/灾年因子入产/tribBoost 清零均为时序内更改)。指纹 tests/fingerprint.helper.ts 含全世界轴(era/stance/prosperity/relationsWithOthers/calamity/pendingEvent/eventQueue/worldAnnals/worldGen 摘要/sagaAnnals/distress/declineYears 汇总——0.1.27 生灭/世鉴路径真实锁定)。有意变更时序/数值时:指纹一并重算并在注释注明原因。

世界自演进模型(0.1.14 sim/ 层,改数值勿动流程)

  • 市场实体化marketPool 是真库存——世界月供给(worldSupplyRate × 潮汐span × era.supplyMultvs 常驻需求(worldDemandRate);NPC 按 def.sells/buys 逐月入池出池(姿态/区域灵势/灾年调制);玩家买卖经 WorldSim.tradeSettle 回写(买浅价涨/卖盈价跌);池深 < tradeFloorPct(0.35) 断供拒单(与 poolFloorPct 单常量一致)。driftMarket rebalance=0.025 弱锚定(让供需真实撬动价格,勿调回 0.1+)。
  • 世纪弧 era:盛世/平世/乱世/末法,ERA_CONF(灾年概率/供需/拍卖/tideBias+ ERA_DURA(续航窗口);flow 权重和 <1(留白=延续档);转移权重受世界温度调制worldTemperature() + tempEraK)——时代由天下兴衰孕育、又重塑天下。
  • NPC 定力npcDyn.prosperity/stance/relationsWithOthers——姿态(守成/扩张/隐忍/结盟)年首重估,调制 raid/互攻/贸量/外交;互攻按 power 加权 + 打残广播;玩家劫掠/结盟写关系网(蝴蝶效应)。
  • 灾年:持续 6 月(calamityLeft),减产乘子(CALAMITY_FAMILY+ 疫病/兽潮体感 + NPC 贸易 ×0.7 + 群雄相噬 ×1.5;世界侧 CALAMITY_EFFECT 冲击池。
  • 灾年 补充:灾因注册表已聚合(calamityNames/calamityFamilyOf/calamityEffectOf + addCalamity——默认表不变,MOD/插件可注入灾因全链生效)。
  • 家族生灭npcDyn.declineYearspower<58 且 景气<38 连续 8 年)→ 覆灭(最强邻分食+史官广播);家族数 < 开局目标(4~8)年首新贵补位(greatNewbornChance,乱世×2)。
  • 世界种子0.1.24):sim/worldgen.ts generateWorld(seed)——NPC 4~8 家随机/初始关系网(1-2 世仇+1 盟友)/开局 era/市场 ±15% 偏移;独立派生 rngseed::worldgenWorld.rng 主序列零消耗);state.worldGen 摘要落档(含 npcs defs——读档幂等重注册,存档世界永稳)。
  • 天下史表worldSim.worldAnnals[](十年一鉴 80 页留档,免 newsFeed 裁剪丢失)。
  • 物品六类生态纪律0.1.32):resource/material/pill/talisman/brew/artifact——新资源必须四同步ITEMS 定义 + POOL_BASE + MARKET_IDS + worldgen marketOffset)才入世界循环(呼吸/NPC 贸易/灾年超卖);消耗类物品效果(符箓/灵酿)零 rng 消耗(自动消耗实现——金钟罩安全)。
  • 生态闭环纪律0.1.33):丹方/铸器产出物必须先在 ITEMS 立名(幽灵物品不可见不可卖——A2 教训);灾年减产因子必须真正乘入生产(compute-and-drop 是 A3 教训);符箓坊为"消耗式"建筑(扣料产符——零 rng),新增消耗式生产勿用 produceTable 泛化(泛化只做纯产出)。

MetonaSqlark@metona-team/metona-sqlark 0.7.4)陷阱

  • SQL 方言为自有实现(不是 SQLite):建表列类型写 string|number|boolean|date|json不是 INTEGER/TEXT;不支持 INSERT OR REPLACEON CONFLICTrowid——用「SELECT 判断 → UPDATE/INSERT」或直接 DELETE+INSERT
  • aria 引擎同库一个 tab 只能建一次连接(Web Locks → ARIA_LOCKED):存取必须走 game/storage/db.ts 的单例 getSaveSlot(slot) / getSlotManager()禁止每次 new SaveSlot/new SlotManager
  • 数据在浏览器 OPFSrenderer 内),main 进程无 DB 逻辑;开发与打包后的 origin 不同,两环境存档不互通。
  • 存储层通过 SaveDbDriver 抽象(storage/slots.ts),node 环境下无 OPFS,测试只覆盖引擎不覆盖 DB。
  • 存档防线:loadState/importAll 都过 migrateIfNeededTOO_NEW/未知版本 → MigrationError);normalizeGameState 是幂等兜底(0.1.14+ 全部新字段靠它补worldSim 全套/era/prosperity/stance/worldAnnals 等,改字段记得同步补兜底);暂无 v3(需转换内容时才升)。

数值基调(勿当 bug 调回去)

  • pacing.ts 的 MAJOR_RATE 是进度唯一生效因子qi 2.2、foundation 1.4、core 0.95、nascent 0.68、spirit 0.48);realms.tsexpBase/expGrowth/maxRealmExp 曲线未参与计算(历史摆设,勿以它反推)。
  • 月率 base = 1.0 + perception*0.18;65 岁起 ×0.72 衰减、8 岁以下 ×0.65(圣者护族 60+ 高境界者存在时乘 1.3)。
  • 0.1.16 修为经济化:练气以上修行者月耗灵草(闭关2/普通1),无草速修 ×0.6~1.0 衰减——灵草是第一战略资源。
  • 寿元:75/150/220/340/520/850(凡人→化神)。
  • 大境界晋升走渡劫事件三选(硬渡/护法/压制,12 月不应自动压制);破境丹在需渡劫时药力转 tribBoost(+12%) 入渡劫,不可跳过
  • 周期事件优先级:命运(百年/飞升)> 大比 > 传薪(年锚 flag> 拍卖(era 概率调制)> 岁祷 > 回声。
  • family.flag 年份键(auction-/prayerDone-/echoDone-/recruitDone-/legacyDone-)每 tick 剪 3 年前旧键。

修改纪律(防覆盖——血泪教训,务必遵守)

背景:曾多次出现「大改后我之前的修复被覆盖」:① 批量 python 替换命中旧文本、② git checkout 回滚临时参数时误恢复了已修复文件、③ 编辑器链式 replace 生成重复属性/孤行。均靠 tsc/vitest 事后兜住,但应防于未然。

硬性规则

  1. 改动前git status 确认工作区干净(或先提交当前成果,原子提交,一功一提交)。
  2. 改动中:批量替换一律用「先 assert 旧文本存在 → 替换 → 再全局 grep 断言新文本唯一」三步;禁止无检查的 sed/python 盲替。写盘脚本严禁惰性路径0.1.32 教训:open('p','w') 撇笔把模块源码碎片写进根目录并被 git add 入库)——写文件必须用显式路径变量且写后立即 git status 检查工作区异常新文件。
  3. 禁止 git checkout -- <file> / 整文件回滚(这是历史事故元凶)。确需回滚时:git show HEAD:<file> > /tmp/bak 手动 diff 恢复,恢复后跑全量测试确认无伤。
  4. 优先小步快跑:每个功能块改完立即 npm test(引擎)或 tsc(UI 类型),绿了再动下一处——绝不在错误状态上加新修改。
  5. 高危文件白名单world.ts / events.ts / store.ts / styles.css / cultivation.ts / WorldSim.ts / worldsim-data.ts)改动后必须 git diff --stat 核对改动面与预期一致。
  6. 每次迭代收尾:git status 应干净 + git log --oneline 确认每步都有提交记录;凡 "git stash/checkout/rm" 类命令,执行前后都 git status 留痕。
  • 提交前:npm test(当前 89 套件 5230 例全绿)+ npm run typecheck 必须绿(改存储/SQL 后再跑一次冒烟)。
  • 平衡数值集中在 game/data/engine/sim/worldsim-data.ts(世界循环参数唯一权威),调平衡不改引擎流程。
  • 发布产物 release/out/ 均已 gitignore,勿入库。