№ · 活跃设计 CH.07.13
主页 · 📐 活跃设计 · 跨周目 v1.1 锚点 v1

跨周目 v1.1 锚点 v1

📅 2026-07-13 · 📦 13.7 KB · 📐 活跃设计 · 📝 MD 文档 🆕 14d 🏷️ 时间🏷️ Buff🏷️ 设计

状态: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、旧存档迁移、GlobalProfile schema 扩展或存档写回。 文件边界: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 4 purchasedLegacies 触发)
  • 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.md v1.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.purchasedLegaciesmode='legacy' 项)触发。

2.4 上限(WEEKLY_LEGACY_CAP)

  • WEEKLY_LEGACY_CAP = 1000
  • 防止极端通关/刷数据导致单周目折算阅历失控
  • 应用位置:折算结果最终min(折算结果, 1000)

2.5 跨年周目边界衰减(由 LegacyDecayNote 锁定)

  • 规则:weekNum >= 2baseScore *= 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.1Phase 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 = 95min(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] 6 场景 R9 回归测试保留且全过(calculateInheritedLegacyPoints 6 测)
  • [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
  • ❌ 改 GlobalProfile schema
  • ❌ 改 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.purchasedLegacies schema 验证(本卡改 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)