- AGENTS.md:新增「数值基调(0.1.10 重建后,勿当 bug 调回去)」红线段—— pacing 因子唯一生效、月率公式、寿元梯度、渡劫三选、事件优先级、flag 剪枝 - docs/PROJECT_MEMORY.md:项目叙事记忆新档案——设计哲学/版本为什么/架构决策/ 踩坑六记/平衡哲学/已知技术债/开始修改路径 - README:版本沿革补 0.1.10/0.1.11,测试规模 923+
74 lines
6.8 KiB
Markdown
74 lines
6.8 KiB
Markdown
# AGENTS.md
|
||
|
||
仙途家族志 · Chronicle of the Immortal Clan — Electron + React + TS 家族修仙模拟器。全部 UI 与文案为中文。
|
||
|
||
## 命令
|
||
|
||
```bash
|
||
npm test # vitest 引擎测试(tests/*.test.ts,全部纯 Node,无 DOM/DB 依赖)
|
||
npx vitest run tests/world.test.ts # 单文件测试
|
||
npm run typecheck # tsc --noEmit(npm 会打 warn 噪音,用 npx tsc --noEmit 更干净)
|
||
npm run dev # electron-vite 热更新(开发期 origin 为 http://localhost:5173)
|
||
npm run build # 产物到 out/
|
||
npm run package # build + electron-builder(Linux 打 win 包可出产物,但未签名,Windows 上可能被 SmartScreen 静默拦截;正规发布在 Windows 侧重跑此命令)
|
||
```
|
||
|
||
端到端冒烟(渲染 + OPFS 数据库 + 开局推进 + 可选截图):
|
||
|
||
```bash
|
||
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/electron`**(Linux 二进制),修复:
|
||
`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.8 起)
|
||
|
||
- **万物皆是插件**:`core/plugin.ts` 协议(Manifest/dependencies/conflicts/install/uninstall);`engine/pluginManager.ts` 安装管线(依赖校验、异常回滚、追踪型上下文——插件注册的时轮钩子卸载时全摘);`engine/plugin-bootstrap.ts` 三枚常驻核心插件(core-systems/core-data/core-events,protected 不可卸)。
|
||
- **事件多池**:`world.eventPools`(core 池受保护不可删)+ `eventRoll` 聚合抽样;第三方插件 `ctx.addEventPool(id, events)` 即注入内容。**注意 findEvent 必须带 world 参数查池**(`events.ts`),否则未知事件直接软锁。
|
||
- **能力卡**:`engine/capabilities.ts` 12 张;时钟注册走 `viaCap`;tournament/tribulation/apprentice/season 四卡各自内部检查 `w.sysEnabled`(无独立钩子,勿直接调)。
|
||
- **门面**:`engine/api.ts` GameFacade(act 24 项/query 6 类/subscribe 退订协议/about);**UI store 的 `facade` 字段必须通过 openState/startNewGame 赋值**,否则 act 走残缺 fallback。
|
||
- **金钟罩**:0.1.11 审计修复批次后三档指纹(47/100/180 年 × 3 seed = 9 值,见 tests/clock.test.ts;0.1.8 基线已作废、0.1.10 重建后再次漂移、0.1.11 大比 once/prune 后重固化)。
|
||
|
||
## 架构要点
|
||
|
||
- `src/renderer/game/`:纯 TS 游戏引擎(无 React/DOM import),可被 vitest 直接测试;`types/domain.ts` 是全量领域类型,改状态结构先看它。
|
||
- **统一时轮 `core/clock.ts`**:月度 phase(production/aging/cultivation/missions/events/diplomacy/epilogue)+ 年首钩子均注册于 `engine/clocks.ts`。**新增系统 = 注册一行,禁止手改 `advanceMonth` 本体**。
|
||
- **统一随机 `core/rng.ts`**:引擎只经 `World.rng`(含 `nextCount` 审计);UI 播种用 `RngHub.rollSeed()`、音效白噪用 `RngHub.audioNoise01()`,**不要**在逻辑里引入 Math.random()。
|
||
- **金钟罩 `tests/clock.test.ts`**:3 枚固定 seed × 三档月数(560/1200/2160 = 47/100/180 年)指纹常驻(0.1.11 基线:见注释 GOLDEN)。任何改动若破坏确定性立即红;**有意变更时序/数值时**指纹一并重算并在注释注明原因。
|
||
- `src/renderer/ui/`:React + zustand(`ui/store.ts`)。World 的 game state 是 mutable,advance 后 `revision++` 触发重渲染;订阅 `revision` 是面板刷新惯例。
|
||
- 引擎 = 种子随机数(sfc32,`core/rng.ts`)+ 不可变快照存 `state.rng`,同 seed 全程可重放(tests/world.test.ts 有确定性用例,改任何 tick 顺序都要保证仍然确定性)。
|
||
- 建档开头成员 id 硬编码 `x1`~`x5`,tests 依赖。
|
||
|
||
## MetonaSqlark(@metona-team/metona-sqlark 0.7.4)陷阱
|
||
|
||
- SQL 方言为自有实现(不是 SQLite):建表列类型写 `string|number|boolean|date|json`,**不是** `INTEGER/TEXT`;不支持 `INSERT OR REPLACE`、`ON CONFLICT`、`rowid`——用「SELECT 判断 → UPDATE/INSERT」或直接 `DELETE+INSERT`。
|
||
- aria 引擎同库一个 tab 只能建一次连接(Web Locks → `ARIA_LOCKED`):存取必须走 `game/storage/db.ts` 的单例 `getSaveSlot(slot)` / `getSlotManager()`,**禁止**每次 `new SaveSlot/new SlotManager`。
|
||
- 数据在浏览器 OPFS(renderer 内),main 进程无 DB 逻辑;开发与打包后的 origin 不同,两环境存档不互通。
|
||
- 存储层通过 `SaveDbDriver` 抽象(`storage/slots.ts`),node 环境下无 OPFS,测试只覆盖引擎不覆盖 DB。
|
||
|
||
## 数值基调(0.1.10 重建后,勿当 bug 调回去)
|
||
|
||
- **pacing.ts 的 MAJOR_RATE 是进度唯一生效因子**(qi 2.2、foundation 1.4、core 0.95、nascent 0.68、spirit 0.48);`realms.ts` 的 `expBase/expGrowth/maxRealmExp` 曲线**未参与计算**(历史摆设,勿以它反推)。
|
||
- 月率 base = `1.0 + perception*0.18`;65 岁起 ×0.72 衰减、8 岁以下 ×0.65(圣者护族 60+ 高境界者存在时乘 1.3)。
|
||
- 寿元:75/150/220/340/520/850(凡人→化神,0.1.10 放宽的梯度)。
|
||
- 大境界晋升走**渡劫事件三选**(硬渡/护法/压制);玩家 12 月不应会**自动压制一年**(防挂机软锁,events.ts 计数 `tribPendingMonths`)。
|
||
- 周期事件优先级:命运(百年/飞升)> 大比 > 传薪 > 拍卖 > 岁祷 > 回声;大比有 `once`,一年一届。
|
||
- `family.flag` 年份键(auction-/prayerDone-/echoDone-/recruitDone-)每 tick 剪 3 年前旧键。
|
||
|
||
## 工作流约定
|
||
|
||
- 提交前:`npm test` + `npm run typecheck` 必须绿(改存储/SQL 后再跑一次冒烟)。
|
||
- 平衡数值集中在 `game/data/`(realms/pacing/buildings/events...),调平衡不改引擎流程。
|
||
- 发布产物 `release/`、`out/` 均已 gitignore,勿入库。
|