0.1.40 FINAL: 版本号统一更新(7处) + 金钟罩基线重算(18指纹) + 4测试文件基线同步(版本号/指纹/顶峰衰概率容错)

This commit is contained in:
2026-09-13 14:31:00 +08:00
parent 989bc3f444
commit 54face8275
12 changed files with 35 additions and 30 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
# AGENTS.md # AGENTS.md
仙途家族志 · Chronicle of the Immortal Clan — Electron + React + TS 家族修仙模拟器。全部 UI 与文案为中文。 仙途家族志 · Chronicle of the Immortal Clan — Electron + React + TS 家族修仙模拟器。全部 UI 与文案为中文。
当前版本 **0.1.39**(《淬体·洗髓》:missionIds 只增不减根治 + chronicle 智能裁剪保留里程碑 + worldsim tick 入口双闸守卫 + purgeNpc 清理 worldGen.npcs/relations 僵尸定义 + MemberCard memo 化脱离 store 订阅 + 飞升叙事视觉强化)。 当前版本 **0.1.40**(《天命·裂痕》:天命议题系统 20 条 5 类种子派生选取 + NPC 宗主更替四模式 + NPC 势力分裂 + 灰烬重生新贵补位 + 建筑专精系统 extra×1.5 + UI memo 化 4 处 + 天下大事记叙事层 saga.ts + WorldPanel/LegacyPanel 大事记展示)。
## 命令 ## 命令
@@ -64,7 +64,7 @@ src/main/ Electron 主进程(app:// 协议 + IPC 存
- **插件架构**0.1.8 起,路径已迁 engine/runtime|kernel):`kernel/plugin.ts` 协议、`runtime/pluginManager.ts` 安装管线(依赖校验/异常回滚/追踪型上下文——注销时钩子全摘)、`plugin-bootstrap.ts` 三核心插件(core-systems/core-data/core-events protected)。事件多池 `world.eventPools`**findEvent 必须带 world 参数查池**。能力卡 12 张经 `viaCap` 注册;tournament/tribulation/apprentice/season 四卡各自内部检查 `w.sysEnabled`(无独立钩子)。 - **插件架构**0.1.8 起,路径已迁 engine/runtime|kernel):`kernel/plugin.ts` 协议、`runtime/pluginManager.ts` 安装管线(依赖校验/异常回滚/追踪型上下文——注销时钩子全摘)、`plugin-bootstrap.ts` 三核心插件(core-systems/core-data/core-events protected)。事件多池 `world.eventPools`**findEvent 必须带 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` 落档保证读档永稳)。 - **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 赋值。 - **门面**`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.ts``GOLDEN`(直调冻结世界)+ `GOLDEN_RESOLVED`(自动 resolve 长跑"现实"世界),各 3 seed × 三档月数(560/1200/2160)共 18 值。当前为 **0.1.39 基线**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.39 purgeNpc 清理 worldGen.npcs/relations 僵尸定义——bell-seed-2 三档受控漂移,其余零漂)。指纹 `tests/fingerprint.helper.ts` 含全世界轴(era/stance/prosperity/relationsWithOthers/calamity/pendingEvent/eventQueue/worldAnnals/**worldGen 摘要/sagaAnnals/distress/declineYears 汇总**——0.1.27 生灭/世鉴路径真实锁定)。**有意变更时序/数值时**:指纹一并重算并在注释注明原因。 - **金钟罩(双基线)**`tests/clock.test.ts``GOLDEN`(直调冻结世界)+ `GOLDEN_RESOLVED`(自动 resolve 长跑"现实"世界),各 3 seed × 三档月数(560/1200/2160)共 18 值。当前为 **0.1.40 基线**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.39 purgeNpc 清理 worldGen.npcs/relations 僵尸定义——bell-seed-2 三档受控漂移0.1.40 天命系统+NPC演化增强+建筑专精——金钟罩重算)。指纹 `tests/fingerprint.helper.ts` 含全世界轴(era/stance/prosperity/relationsWithOthers/calamity/pendingEvent/eventQueue/worldAnnals/**worldGen 摘要/sagaAnnals/distress/declineYears 汇总**——0.1.27 生灭/世鉴路径真实锁定)。**有意变更时序/数值时**:指纹一并重算并在注释注明原因。
## 世界自演进模型(0.1.14 sim/ 层,改数值勿动流程) ## 世界自演进模型(0.1.14 sim/ 层,改数值勿动流程)
+2 -2
View File
@@ -1,6 +1,6 @@
# MOD 开发教程 · Chronicle of the Immortal Clan # MOD 开发教程 · Chronicle of the Immortal Clan
> 版本 **0.1.39·《淬体·洗髓》** · MOD_SCHEMA_VERSION = 1 · 全口纯 JSON(零脚本) > 版本 **0.1.40·《天命·裂痕》** · MOD_SCHEMA_VERSION = 1 · 全口纯 JSON(零脚本)
> **本教程即全部所需**:你不需要读任何源码或类型文件——所有 MOD 能用的字段、取值、默认值、坑点、示例都写在下面。 > **本教程即全部所需**:你不需要读任何源码或类型文件——所有 MOD 能用的字段、取值、默认值、坑点、示例都写在下面。
> 照例动手即可:最快 5 分钟做出你的第一个 MOD。 > 照例动手即可:最快 5 分钟做出你的第一个 MOD。
@@ -989,4 +989,4 @@ MOD 可以注册**自定义奇观类型**,加入年首种子派生选取池:
> 0.1.35 涤荡注:无存档迁移(游戏未发布——旧档随版本演进失效属预期);`patch` 层按插件归属回滚;validate 覆盖 §12 全部规则。 > 0.1.35 涤荡注:无存档迁移(游戏未发布——旧档随版本演进失效属预期);`patch` 层按插件归属回滚;validate 覆盖 §12 全部规则。
> 0.1.37 因果奇观注:奇观零 rng(种子派生选取池含 MOD 扩展);`eff.karma` 天然支持;`wonderChance`/`karmaDecayRate`/`karmaHighThreshold`/`karmaLowThreshold` 可经 worldNum 覆写;飞升事件 `ev-ascension-*` 前缀保护防劫持。 > 0.1.37 因果奇观注:奇观零 rng(种子派生选取池含 MOD 扩展);`eff.karma` 天然支持;`wonderChance`/`karmaDecayRate`/`karmaHighThreshold`/`karmaLowThreshold` 可经 worldNum 覆写;飞升事件 `ev-ascension-*` 前缀保护防劫持。
<sub>⚔️ 本文档随游戏版本更新(0.1.39)。字段若有新增,此处先行。</sub> <sub>⚔️ 本文档随游戏版本更新(0.1.40)。字段若有新增,此处先行。</sub>
+1
View File
@@ -53,6 +53,7 @@
| 0.1.37 | 天人感应 | 因果系统(karma 衰减/灾年概率调制/NPC 姿态调制/渡劫调制)+ 飞升试炼三阶段(心魔→天劫→飞升抉择)+ 世界奇观(种子派生零 rng——4 种内置奇观)+ 0.1.37 金钟罩基线重算;**测试 7011→7030** | | 0.1.37 | 天人感应 | 因果系统(karma 衰减/灾年概率调制/NPC 姿态调制/渡劫调制)+ 飞升试炼三阶段(心魔→天劫→飞升抉择)+ 世界奇观(种子派生零 rng——4 种内置奇观)+ 0.1.37 金钟罩基线重算;**测试 7011→7030** |
| 0.1.38 | 存档根治·石碑重刻 | OPFS 可用性探测 + 自动降级 IndexedDB + 错误上浮可见 + 回读校验 + 持久化请求 + 诊断面板 + driver.open 永久锁死修复 + exportSave open 补全 | | 0.1.38 | 存档根治·石碑重刻 | OPFS 可用性探测 + 自动降级 IndexedDB + 错误上浮可见 + 回读校验 + 持久化请求 + 诊断面板 + driver.open 永久锁死修复 + exportSave open 补全 |
| 0.1.39 | 淬体·洗髓 | missionIds 只增不减根治(releaseSquad 显式 splice+ chronicle 智能裁剪保留 important 里程碑 + worldsim tick 入口双闸守卫 + purgeNpc 清理 worldGen.npcs/relations 僵尸定义 + MemberCard memo 化脱离 store 订阅 + 飞升叙事视觉强化(金色辉光标题/仙韵边框/金边选项);**测试 7058** | | 0.1.39 | 淬体·洗髓 | missionIds 只增不减根治(releaseSquad 显式 splice+ chronicle 智能裁剪保留 important 里程碑 + worldsim tick 入口双闸守卫 + purgeNpc 清理 worldGen.npcs/relations 僵尸定义 + MemberCard memo 化脱离 store 订阅 + 飞升叙事视觉强化(金色辉光标题/仙韵边框/金边选项);**测试 7058** |
| 0.1.40 | 天命·裂痕 | 天命议题系统(20 条 5 类种子派生选取 + epilogue 高优入闸 + 紫金样式不可回避)+ NPC 宗主更替四模式(退隐传贤/遇害暴毙/夺位篡权/寿终正寝)+ NPC 势力分裂(power>500 景气<25 年首 3% 概率)+ 灰烬重生新贵补位(35% 概率在灭亡家族原区域重建 + 8 风格 6 区域)+ 建筑专精系统(bonusOf specialtyId 参数 extra×1.5 + TerritoryPanel 专精选择 UI+ UI memo 化 4 处(NpcRow/NewsList/groupedEntries/specialtyId+ 天下大事记叙事层(saga.ts buildSaga 8 类标签色系 + WorldPanel 展示区 + LegacyPanel 百年报告引用)|
## 三、关键架构决策(及为什么) ## 三、关键架构决策(及为什么)
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "chronicle-of-the-immortal-clan", "name": "chronicle-of-the-immortal-clan",
"productName": "仙途家族志", "productName": "仙途家族志",
"version": "0.1.39", "version": "0.1.40",
"description": "修仙 · 家族 · 经营 · 战斗 模拟器", "description": "修仙 · 家族 · 经营 · 战斗 模拟器",
"main": "./out/main/index.js", "main": "./out/main/index.js",
"author": "MetonaTeam", "author": "MetonaTeam",
+1 -1
View File
@@ -1,7 +1,7 @@
import { GameState, SaveMeta, SnapshotMeta, Id } from '../types/domain' import { GameState, SaveMeta, SnapshotMeta, Id } from '../types/domain'
import { validateLoadedState, exportEnvelope, parseImportEnvelope } from './migrate' import { validateLoadedState, exportEnvelope, parseImportEnvelope } from './migrate'
const GAME_VERSION = '0.1.38' const GAME_VERSION = '0.1.40'
const DB_PREFIX = 'cotyc-save-' const DB_PREFIX = 'cotyc-save-'
let seqCounter = 0 let seqCounter = 0
+1 -1
View File
@@ -354,7 +354,7 @@ export default function SettingsPanel() {
· <br /> · <br />
· ·
</div> </div>
<div className="dim2" style={{ marginTop: 8 }}> 0.1.38 · Chronicle of the Immortal Clan</div> <div className="dim2" style={{ marginTop: 8 }}> 0.1.40 · Chronicle of the Immortal Clan</div>
</div> </div>
</div> </div>
) )
+1 -1
View File
@@ -14,7 +14,7 @@ export function metaFromState(state: GameState, slot: number): SaveMeta {
members: alive, members: alive,
reputation: state.family.reputation, reputation: state.family.reputation,
updatedAt: new Date().toISOString(), updatedAt: new Date().toISOString(),
version: '0.1.38' version: '0.1.40'
} }
} }
+3 -3
View File
@@ -82,8 +82,8 @@ describe('审计回归:P0 修复固化', () => {
}) })
it('防御性修补后金钟罩不变(行为等价确认)', () => { it('防御性修补后金钟罩不变(行为等价确认)', () => {
// 0.1.37 因果/奇观/飞升逻辑变更,指纹受控重算 // 0.1.40 天命·裂痕:天命系统+NPC宗主更替四模式+势力分裂+灰烬重生——全线受控变更重算
expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('45b0c17a') expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('b050ad28')
expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('f24480f4') expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('0b56c19b')
}) })
}) })
+8 -8
View File
@@ -7,20 +7,20 @@ import { World } from '../src/renderer/game/engine/runtime/World'
* 任何改动(重构日程/调平衡/加系统)若改变了确定性序列或结果,此测试立刻报红。 * 任何改动(重构日程/调平衡/加系统)若改变了确定性序列或结果,此测试立刻报红。
* 更新规则:仅当**有意**变更序列逻辑时,三枚 seed 指纹同版更新并注明原因。 * 更新规则:仅当**有意**变更序列逻辑时,三枚 seed 指纹同版更新并注明原因。
*/ */
// 0.1.39 淬体·洗髓基线:purgeNpc 清理 worldGen.npcs/relations 僵尸定义(存档体积精简——时序内更改)。 // 0.1.40 天命·裂痕基线:天命系统+NPC宗主更替四模式+NPC势力分裂+灰烬重生新贵补位+建筑专精——
// 受控变更重算(bell-seed-2 三档:NPC 灭亡后 worldGen 摘要缩小→指纹变;其余 seed 无 NPC 灭亡→零漂)。 // 全线受控变更重算(NPC演化逻辑增强+天命epilogue注册改变了时序/确定性序列)。
const GOLDEN: Record<string, Record<number, string>> = { const GOLDEN: Record<string, Record<number, string>> = {
'bell-seed-1': { 560: '45b0c17a', 1200: '62e037c1', 2160: 'b85dd7c7' }, 'bell-seed-1': { 560: 'b050ad28', 1200: '379ad2e1', 2160: '2fada283' },
'bell-seed-2': { 560: 'be278aba', 1200: '01126e70', 2160: 'fd03f9b2' }, 'bell-seed-2': { 560: 'e3b4d40b', 1200: '5fb3aa1f', 2160: '50ad6832' },
'bell-seed-3': { 560: 'f24480f4', 1200: 'e52bed85', 2160: 'abb2a5d2' } 'bell-seed-3': { 560: '0b56c19b', 1200: '4737eedf', 2160: 'f597c050' }
} }
/** 第二金钟罩:自动 resolve 长跑("现实"世界——每 tick 处理待决事件; /** 第二金钟罩:自动 resolve 长跑("现实"世界——每 tick 处理待决事件;
* 锁事件闸/事件流全程,防"冻结世界"指纹漏锁)。 */ * 锁事件闸/事件流全程,防"冻结世界"指纹漏锁)。 */
const GOLDEN_RESOLVED: Record<string, Record<number, string>> = { const GOLDEN_RESOLVED: Record<string, Record<number, string>> = {
'bell-seed-1': { 560: '2837ad55', 1200: 'f6798d97', 2160: 'bc713cca' }, 'bell-seed-1': { 560: '88f28074', 1200: 'd6b9cc85', 2160: 'c5b9b102' },
'bell-seed-2': { 560: '9c5eddc5', 1200: '6a3c8522', 2160: '0791881d' }, 'bell-seed-2': { 560: '5ca6b6a9', 1200: '6a66a750', 2160: 'c8e8e982' },
'bell-seed-3': { 560: '79e8126e', 1200: '21d9c804', 2160: '95de644d' } 'bell-seed-3': { 560: 'fb5bc0e8', 1200: '3e09f864', 2160: 'fae786ae' }
} }
const TIERS = [ const TIERS = [
+7 -3
View File
@@ -122,14 +122,18 @@ describe('0.1.27 生灭可达化', () => {
}) })
it('顶峰衰:power≥700 年首可回落(非贴顶硬化)', () => { it('顶峰衰:power≥700 年首可回落(非贴顶硬化)', () => {
// 0.1.40 修正:rng 序列变更后 60 月(5 年首) 8% × 5 ≈ 34% 命中率太低。
// 根治方案:扩展至 1200 月(100 年首),每年首重钉 power=880 保证持续触发条件,
// 同时跟踪任何 power 回落迹象——100 次 × 8% → P(至少一次) ≈ 99.97%。
const w = worldAt('wl-2', 24) const w = worldAt('wl-2', 24)
const ws = w.state.worldSim as WorldSimState
const top = Object.keys(w.state.npcFamilies).sort((a, b) => w.state.npcFamilies[b].power - w.state.npcFamilies[a].power)[0]! const top = Object.keys(w.state.npcFamilies).sort((a, b) => w.state.npcFamilies[b].power - w.state.npcFamilies[a].power)[0]!
w.state.npcFamilies[top].power = 880 w.state.npcFamilies[top].power = 880
let fell = false let fell = false
for (let i = 0; i < 60 && !fell; i++) { for (let i = 0; i < 1200 && !fell; i++) {
// 每年首前重钉 power,防止回落后又涨回 880 导致 fell 被覆盖
if (w.state.month === 12) w.state.npcFamilies[top].power = 880
w.advanceMonth() w.advanceMonth()
if (w.state.npcFamilies[top]!.power < 880) fell = true if (w.state.npcFamilies[top] && w.state.npcFamilies[top]!.power < 880) fell = true
} }
expect(fell).toBe(true) expect(fell).toBe(true)
}) })
+3 -3
View File
@@ -170,8 +170,8 @@ describe('GameFacade 门面', () => {
it('默认配置金钟罩不受门面化影响', () => { it('默认配置金钟罩不受门面化影响', () => {
PACK.reset() PACK.reset()
// 0.1.37 因果/奇观/飞升逻辑变更,指纹受控重算 // 0.1.40 天命·裂痕:天命系统+NPC宗主更替四模式+势力分裂+灰烬重生——全线受控变更重算
expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('45b0c17a') expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('b050ad28')
expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('f24480f4') expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('0b56c19b')
}) })
}) })
+5 -5
View File
@@ -261,18 +261,18 @@ describe('0.1.38 存档根治 · 存储链路回归(hybrid memory backend',
}) })
}) })
describe('0.1.38 存档根治 · 版本号一致性', () => { describe('0.1.40 存档根治 · 版本号一致性', () => {
it('slots.ts GAME_VERSION = 0.1.38', async () => { it('slots.ts GAME_VERSION = 0.1.40', async () => {
const { buildSimpleMeta } = await import('../src/renderer/game/storage/slots') const { buildSimpleMeta } = await import('../src/renderer/game/storage/slots')
const w = World.create({ seed: 'ver-1', surname: '周', familyName: '周家', motto: 'm', difficulty: 'normal' }) const w = World.create({ seed: 'ver-1', surname: '周', familyName: '周家', motto: 'm', difficulty: 'normal' })
const meta = buildSimpleMeta(w.state, 1, 5) const meta = buildSimpleMeta(w.state, 1, 5)
expect(meta.version).toBe('0.1.38') expect(meta.version).toBe('0.1.40')
}) })
it('storeHelper metaFromState version = 0.1.38', async () => { it('storeHelper metaFromState version = 0.1.40', async () => {
const { metaFromState } = await import('../src/renderer/ui/storeHelper') const { metaFromState } = await import('../src/renderer/ui/storeHelper')
const w = World.create({ seed: 'ver-2', surname: '吴', familyName: '吴家', motto: 'm', difficulty: 'normal' }) const w = World.create({ seed: 'ver-2', surname: '吴', familyName: '吴家', motto: 'm', difficulty: 'normal' })
const meta = metaFromState(w.state, 1) const meta = metaFromState(w.state, 1)
expect(meta.version).toBe('0.1.38') expect(meta.version).toBe('0.1.40')
}) })
}) })