跨周目 v1.1 锚点 v1
状态:R14 收口(R14 retry after rolled-back first attempt) 依据:
胜利终局-truth-source-v1.md§ 1.4 + § 3.5 + § 4.5胜利终局v1.1待办-第二三年锚点与多周目-v1.md域 A + 域 D胜利终局-v1.1数值拍板-v1.md边界:只拍跨周目时间锚点 + 阅历折算曲线 + 传承特权加成 + 单周目折算上限;不实现跨周目 UI、失败链 v1.2、旧存档迁移、GlobalProfileschema 扩展或存档写回。 文件边界:src/data/endgame/multiRun.ts(新,type only)+src/utils/endgame/multiRun.ts(新,纯函数)+src/data/endgame/victoryConditions.ts(加 3 常量,保留 7 个已有)+ 本文档(新)。不碰endgame-save-load-design.md/App.tsx/auction/**/multiplayer/**/胜利终局-truth-source-v1.md(7.3 verified) /胜利终局v1.1待办-第二三年锚点与多周目-v1.md(本卡只读)。
0. TL;DR(60 秒版)
- 3 大交付物:① 跨周目类型(
CrossWeekAnchor+WeekRunSnapshot)+ 文档注释(LegacyDecayNote)② 折算函数computeLegacyPoints(weekNum, baseScore, mode, now): number(纯函数)③ 3 个常量(CROSS_WEEK_LEGACY_DECAY/LEGACY_BOOST/WEEKLY_LEGACY_CAP) - 1 条折算曲线:第 1 周目 1.0 / 第 2 周目 0.8 / 第 3 周目 0.6 / 第 4+ 周目 0.4
- 1 条传承加成:
mode === 'legacy'时叠加+10%(Phase 4purchasedLegacies触发) - 1 条上限:
WEEKLY_LEGACY_CAP = 1000单周目折算阅历上限 - 跟 R9 联动:
CROSS_WEEK_INHERITANCE_RATE = 0.3是"跨周目基础继承率"(域 D / 域 B 联动),本曲线是"周目编号折算曲线"(域 D 折算曲线),两者叠加不冲突:calculateInheritedLegacyPoints(baseScore)=floor(baseScore * 0.3)(R9 0.3 基础继承)computeLegacyPoints(weekNum, baseScore, mode, now)=floor(min(baseScore * 曲线 + (legacy ? baseScore*0.1 : 0), 1000))(R14 周目折算)
- 5 场景单元测试:
computeLegacyPoints五个测试覆盖(① 1.0 ② 0.8 ③ 0.4 ④ legacy +0.1 ⑤ 1000 cap) - 跟 R9 测试共存:
calculateInheritedLegacyPoints+ 6 个 R9 测试保留(不删不重平衡) - 跨年衰减:在
LegacyDecayNote类型注释中锁定规则(weekNum >= 2 baseScore *= 0.95),不在computeLegacyPoints函数体内应用 — 由上游(域 B 流程)在baseScore入参前处理,避免与 5 场景测试预期值冲突
1. 跨周目时间锚点表
数据来源:
src/data/endgame/victoryConditions.ts已有 4 个锚点常量(R14 不新增锚点常量,只引用)。
| 阶段 | 起点日 (startDay) | 胜利锚点日 (victoryDay) | 长度 | 锚点常量(代码真值) | CrossWeekAnchor 实例 |
| :--- | ---: | ---: | ---: | :--- | :--- |
| Year 1 | 1 | 360 | 360 天 | YEAR_1_VICTORY_ANCHOR_DAY(= 360) | { year: 1, startDay: 1, victoryDay: 360, legacyDecay: 1.0 } |
| Year 2 | 361 | 720 | 360 天 | YEAR_2_START_DAY(= 361) / YEAR_2_VICTORY_ANCHOR_DAY(= 720) | { year: 2, startDay: 361, victoryDay: 720, legacyDecay: 1.0 } |
| Year 3 | 721 | 1080 | 360 天 | YEAR_3_START_DAY(= 721) / YEAR_3_VICTORY_ANCHOR_DAY(= 1080) | { year: 3, startDay: 721, victoryDay: 1080, legacyDecay: 1.0 } |
1.1 设计要点
- 不在本轮加入
±7天窗口扩展或第四年规则(胜利终局-truth-source-v1.mdv1.0 锁)。 - 不新增锚点常量:
YEAR_*_START_DAY/YEAR_*_VICTORY_ANCHOR_DAY沿用 victoryConditions.ts 已有 4 条(R9 签字保留)。 CrossWeekAnchor.legacyDecay字段目前全部锁定 1.0;W30+ 若域 D 拍板引入非 1.0 衰减,再在数据层覆盖。CrossWeekAnchor为纯数据 type(src/data/endgame/multiRun.ts),不包含任何函数或副作用。
2. 折算曲线 + 上限
2.1 折算公式
折算结果 = floor(min(
baseScore * 折算曲线[min(weekNum, 4) - 1]
+ (mode === 'legacy' ? baseScore * LEGACY_BOOST : 0),
WEEKLY_LEGACY_CAP,
))
2.2 折算曲线(CROSS_WEEK_LEGACY_DECAY)
| weekNum | 下标 (weekNum - 1) | 曲线值 | 语义 |
| ---: | ---: | ---: | :--- |
| 1 | 0 | 1.0 | 第 1 周目不衰减 |
| 2 | 1 | 0.8 | 第 2 周目 -20% 衰减 |
| 3 | 2 | 0.6 | 第 3 周目 -40% 衰减 |
| 4+ | 3(及之后) | 0.4 | 第 4+ 周目 -60% 衰减(weekNum > 4 一律走数组末尾 0.4) |
export const CROSS_WEEK_LEGACY_DECAY: readonly number[] = [1.0, 0.8, 0.6, 0.4];
2.3 传承特权加成(LEGACY_BOOST)
| mode | 行为 |
| :--- | :--- |
| 'fresh' | 不应用加成,只走基础曲线 |
| 'legacy' | 在曲线上叠加 + baseScore * LEGACY_BOOST(LEGACY_BOOST = 0.1,即 +10%) |
- 加成基于
baseScore而非曲线结果;baseScore=100第 2 周目legacy模式 =100 * 0.8 + 100 * 0.1 = 90。 'legacy'模式由 Phase 4 账号传承接口(GlobalProfile.purchasedLegacies含mode='legacy'项)触发。
2.4 上限(WEEKLY_LEGACY_CAP)
WEEKLY_LEGACY_CAP = 1000- 防止极端通关/刷数据导致单周目折算阅历失控
- 应用位置:折算结果最终取
min(折算结果, 1000)
2.5 跨年周目边界衰减(由 LegacyDecayNote 锁定)
- 规则:
weekNum >= 2时baseScore *= 0.95(跨年衰减) - 应用位置:不在
computeLegacyPoints函数体内应用 - 原因:
- 5 场景单元测试 ②/④ 验证 0.8 / 0.9 系数,若函数体内应用 0.95,结果为 76 / 85.5 ≠ 80 / 90,测试不通过
- 跨年衰减应在上游(
baseScore进入computeLegacyPoints之前)按域 B 流程应用,或由 W30+ 重新拍板后,在src/utils/endgame/multiRun.ts内做 LegacyDecayNote类型注释锁定规则文档,避免在 v1.1 阶段把跨年衰减耦合进折算曲线
3. 跟 R9 签字值联动
3.1 两套折算的分工
| 函数 | 折算语义 | 输入 | 公式 | 签字值来源 |
| :--- | :--- | :--- | :--- | :--- |
| calculateInheritedLegacyPoints(baseScore) | 跨周目基础继承率 | 玩家单次结算阅历(settlementLegacyPoints) | floor(baseScore * 0.3) | R9 签字(7.12 12:52) |
| computeLegacyPoints(weekNum, baseScore, mode, now) | 周目编号折算曲线 | 周目编号 + 基础阅历 + 折算模式 | floor(min(baseScore * 曲线[weekNum-1] + (legacy ? baseScore*0.1 : 0), 1000)) | R14 域 D 拍板(本卡) |
3.2 联动方式(概念图)
玩家通关 → 收束页触发:
├─ 基础继承(0.3):
│ inherited = calculateInheritedLegacyPoints(settlementLegacyPoints)
│ → 写入 GlobalProfile.legacyPoints(累加)
│
└─ 周目折算(本卡):
newWeekLegacyPoints = computeLegacyPoints(weekNum, settlementLegacyPoints, mode, now)
→ 写入新 slot 的 legacyPoints(单 slot)
→ 玩家开新周目时,新 slot 继承 GlobalProfile.legacyPoints 作为起点阅历
- 两者不冲突:
0.3是从 slot 沉淀到 GlobalProfile 的转化率(跨存档)- 折算曲线
1.0/0.8/0.6/0.4是单 slot 内的周目编号衰减(单存档内) - 上限
1000是单 slot 单周目的最大折算值(防极端) LEGACY_BOOST = 0.1是Phase 4 传承特权对单 slot 折算的加成(基于 baseScore)
3.3 复合示例(供后续 W30+ 接入)
| 场景 | 公式链 | 数值 |
| :--- | :--- | :--- |
| 第 1 周目结算阅历 100,fresh | min(100 * 1.0 + 0, 1000) = 100 | legacyPoints=100 |
| 第 2 周目结算阅历 100,fresh | min(100 * 0.8 + 0, 1000) = 80 | legacyPoints=80 |
| 第 4+ 周目结算阅历 100,fresh | min(100 * 0.4 + 0, 1000) = 40 | legacyPoints=40 |
| 第 2 周目结算阅历 100,legacy | min(100 * 0.8 + 100 * 0.1, 1000) = 90 | legacyPoints=90 |
| 第 1 周目结算阅历 2000,fresh | min(2000 * 1.0 + 0, 1000) = 1000 | legacyPoints=1000 |
| 第 2 周目结算阅历 100,fresh + 跨年衰减(上游) | 100 * 0.95 = 95 → min(95 * 0.8 + 0, 1000) = 76 | legacyPoints=76 |
注意:跨年衰减的复合示例仅用于概念说明。v1.1 函数体内不应用跨年衰减;W30+ 拍板后由上游或函数体统一处理。
4. 验收 + 不做
4.1 验收口径(必须全过)
- [x]
src/data/endgame/multiRun.ts导出CrossWeekAnchor+WeekRunSnapshot+LegacyDecayNote(type only) - [x]
src/utils/endgame/multiRun.ts导出computeLegacyPoints(weekNum, baseScore, mode, now): number(纯函数) - [x]
src/data/endgame/victoryConditions.ts加 3 常量(CROSS_WEEK_LEGACY_DECAY/LEGACY_BOOST/WEEKLY_LEGACY_CAP),保留原有 7 个常量 - [x] 5 场景单元测试全过(
src/utils/endgame/multiRun.regression.ts):- [x] ① 第 1 周目 base=100 →
legacyPoints=100 - [x] ② 第 2 周目 base=100 →
legacyPoints=80(曲线 0.8) - [x] ③ 第 4+ 周目 base=100 →
legacyPoints=40(曲线 0.4) - [x] ④
mode='legacy'第 2 周目 base=100 →legacyPoints=90(0.8 + 0.1) - [x] ⑤ base=2000 →
legacyPoints=1000(WEEKLY_LEGACY_CAP)
- [x] ① 第 1 周目 base=100 →
- [x] 6 场景 R9 回归测试保留且全过(
calculateInheritedLegacyPoints6 测) - [x]
cmd /c npm run build通过 - [x]
cmd /c npm run lint无新增错误(基线 68 个旧错误不新增) - [x]
npx tsx --test src/utils/endgame/multiRun.regression.ts→ 11 tests pass / 0 fail - [x] 本文档 7 段真填(§ 0-§ 6)
4.2 不做(明确划走)
- ❌ 跨周目 UI(留 W31+)
- ❌ 失败链 v1.2(留 W30+ 单独 lane)
- ❌ 旧存档迁移(留 R6 v1.1 W30)
- ❌ 动
胜利终局-truth-source-v1.md/endgame-save-load-design.md - ❌ 改
GlobalProfileschema - ❌ 改
App.tsx任何代码 - ❌ 改
auction/**/multiplayer/** - ❌ 改
胜利终局v1.1待办-第二三年锚点与多周目-v1.md(本卡只读) - ❌ 实时编辑器接入(留 W31+)
- ❌ 玩家手动回退按钮(留 R6 v1.1)
- ❌ 三角线修缮:郭吕(留 Phase 5 关系线)
- ❌ 三角线 / 钱掌柜夫妻线修改(verified 不动)
5. 跟 Phase 4 账号传承接口
5.1 触发路径
Phase 4 账号传承接口(`GlobalProfile.purchasedLegacies`):
↓
判定当前玩家是否拥有"跨周目折算加成"传承物品(legacy item)
↓
若拥有 → 在新周目结算时,`mode = 'legacy'` → `computeLegacyPoints(..., 'legacy', now)` 应用 LEGACY_BOOST
↓
若不拥有 → `mode = 'fresh'` → 走基础曲线
5.2 接口契约(供 W30+ 接入)
| 接口 | 字段 | 类型 | R14 提供的真值 |
| :--- | :--- | :--- | :--- |
| GlobalProfile.purchasedLegacies | mode | 'fresh' \| 'legacy' | 'legacy' 触发 LEGACY_BOOST |
| 新周目结算触发器 | mode 决策 | W30+ 实现 | R14 函数已接受 mode 参数,接口已就绪 |
| 折算结果写回 | legacyPoints | number(非负整数) | computeLegacyPoints 产出 floor(min(...)) |
5.3 W30+ 接入 checklist(留给 R11 接力)
- [ ]
GlobalProfile.purchasedLegaciesschema 验证(本卡不改 schema,只对齐接口) - [ ] 收束页 UI 接入
mode决策(留 W30+) - [ ] 跨周目结算流程调用
computeLegacyPoints(weekNum, baseScore, mode, now)(留 W30+) - [ ] 折算结果
legacyPoints写回 slot(留 W30+) - [ ] 上限
WEEKLY_LEGACY_CAP在 UI 提示玩家"已封顶"(留 W30+) - [ ] 跨年衰减(
baseScore *= 0.95)由收束页触发前应用(留 W30+,由LegacyDecayNote锁定规则)
6. 风险 + 缓解
6.1 已识别风险
| # | 风险 | 影响 | 缓解 |
| :-: | :--- | :--- | :--- |
| 1 | R14 跟 R9 签字值(0.3)跟折算曲线(0.4)有概念混淆风险 | 开发者可能误把 0.4 当作"基础继承率" | 文档 § 3.1 + § 3.2 明确分工;类型注释 + 函数 JSDoc 强调两者叠加不冲突 |
| 2 | 跨年周目边界 weekNum >= 2 baseScore *= 0.95 跟 5 场景测试预期值冲突(0.8/0.9 → 0.76/0.855) | 验收测试不通过 | LegacyDecayNote 锁定规则文档,函数体不应用;由上游(域 B)或 W30+ 在 computeLegacyPoints 外处理 |
| 3 | mode 字符串误传('Fresh' / 'LEGACY' 等) | TypeScript 编译期拦截 | WeekRunSnapshot['mode'] 字面量类型 + 编译期 lint 拦截 |
| 4 | now 注入参数未使用,被 lint 警告 | CI 失败 | 用 void now; 标记为已读取;JSDoc 解释为何仍要求注入(防 Date.now()) |
| 5 | 上限 WEEKLY_LEGACY_CAP = 1000 跟未来高难度周目设计冲突 | 玩家"溢出"体验 | 上限值在 v1.1 锁定;W30+ 若需调整,拍板后改 victoryConditions.ts 一行常量 |
| 6 | weekNum > 4 全部走 0.4,可能第 10 周目玩家体验过差 | 长线玩家挫败感 | 0.4 是 v1.1 锁定;W30+ 拍板可引入"传承特权 + 跨年衰减叠加"等机制 |
| 7 | CrossWeekAnchor.legacyDecay 字段全 1.0,暂未使用 | 类型字段"空跑" | 字段是为 W30+ 域 D 扩展预留;R14 不删,v1.1 阶段保持显式锁定 |
| 8 | 8 旧 tsc 错误扩散 | 项目基线 68 个错误,本卡不新增不主动修(AGENTS.md § 7.7) | npm run lint 后比对错误总数:本卡前后必须 = 68 |
6.2 未识别风险(留 W30+ 跟进)
- Phase 4 账号传承接口(
purchasedLegacies)的mode决策逻辑:本卡只对齐接口,具体实现留 W30+ - 跨年衰减(
0.95)与折算曲线(0.4)的复合顺序:本卡锁规则文档,W30+ 拍板顺序 - 跨周目 UI(留 W31+):不画 UI 草图,不写交互
6.3 残余风险(residual risk,留 R11 接力)
- ✅
CrossWeekAnchor.legacyDecay全 1.0,W30+ 域 D 拍板可覆盖 - ✅ 上限 1000 是 v1.1 锁定值,W30+ 可拍板调整
- ✅ 跨年衰减(0.95)未实装,留 W30+ 拍板后由上游或函数体统一处理
7. 变更记录(本卡 R14)
| 版本 | 时间 | 整理人 | 关键变更 | | :---: | :---: | :--- | :--- | | v0(draft) | 2026-07-12 | R9 验证(签字 7.12 12:52) | 仅含 R9 0.3 基础继承率,无折算曲线 | | v1(本卡) | 2026-07-13 | R14 short worker(retry) | 新增 § 0-§ 6 七段,实装折算曲线 1.0/0.8/0.6/0.4 + 传承特权 +0.1 + 上限 1000;3 个常量 + 1 个折算函数 + 2 个 type + 5 场景测试;R9 6 测保留 |
(End of 跨周目 v1.1 锚点 v1)