Files
echo-nexus/docs/12-叙事深化-v0.15.md

8.3 KiB
Executable File
Raw Permalink Blame History

v0.15 — 叙事深化(loreLayers+ 放置平衡(softcap+ UX 增强

回响星核 / Echo Nexus · v0.15 设计文档 对应 worklog Task ID: v0.15-release


一、动机

v0.14 上线了完整的放置系统(6 工程 + 永久加成 + idle 徽章),但留下两个问题:

  1. 平衡性漏洞idle_resonance 工程每次完成 +8/s 永久加成,玩家可反复派遣导致滚雪球,几小时后产能爆炸。
  2. 叙事单薄8 个记忆碎片只有一层 echo 文本,解锁后即"读完",缺乏 Dark Souls / Outer Wilds 式的碎片叙事深度——这是项目的差异化竞争力

v0.15 用两个并行子代理一次性解决:

  • 2-a(平衡 + UX:永久加成 softcap + 批量领取 + 完成 toast + StatsPanel idle 统计
  • 2-b(叙事深化):8 碎片 × 2 层 = 16 层 loreLayers 多层文本 + Codex 分层渲染

二、平衡:永久加成 Softcap

公式

export function applyIdleSoftcap(bonus: number): number {
  if (bonus <= 30) return bonus;               // 全额
  if (bonus <= 100) return 30 + (bonus - 30) * 0.5;  // 超出部分 ×0.5
  return 65 + (bonus - 100) * 0.25;             // 超出部分 ×0.25
}

临界值

原始 bonus 有效 bonus 衰减率
30 30 0%
50 40 20%
100 65 35%
200 90 55%
500 152.5 69.5%

设计意图

  • 30 以下不衰减:新手期 + 偶尔完成 idle_refine+0.3/s)不受惩罚
  • 30-100 线性半衰:中段玩家反复完成 idle_drones+2/s)开始感到边际递减
  • 100 以上严重衰减:硬核玩家刷 idle_resonance(+8/s)收益大幅缩水,但仍非零(保留进度感)
  • 永不归零applyIdleSoftcap(bonus) → ∞ 时仍趋向无穷,只是增速极慢——避免"刷到上限"的挫败感

UI 展示

IdleOperationsPanel Section 1「永久加成」StatTile

  • 有效值永远显示(如 +40/s
  • 有效 < 原始 时,下方加 amber 副行:有效 +40/s(原始 +50/s · 衰减中)

三、叙事深化:loreLayers 多层碎片文本

类型扩展

export interface FragmentLoreLayer {
  layer: 1 | 2;              // 1=中层,2=深层
  title: string;             // "隐秘注脚" / "深层回响"
  text: string;              // 60-150 字叙事文本
  unlockHint: string;        // "飞升 1 次后揭示"
  unlock: FragmentLoreUnlock;
}

export interface FragmentLoreUnlock {
  kind: "ascensions" | "totalDecoded" | "contact" | "fragmentCount" | "bossKills";
  value: number;
}

5 种解锁条件

kind 含义 典型阈值
ascensions 飞升次数 1 / 2 / 3
totalDecoded 累计解码晶体数 20 / 40 / 60
contact 接触进度 50 / 80
fragmentCount 已解锁碎片数 4 / 6
bossKills 累计击杀 BOSS 3 / 8

5 条叙事暗线(贯穿 16 层)

  1. 追逐者:以太族并非自然消亡,而是被虚空中"某种东西"追逐而集体飞升
  2. 回响同源:玩家舰载 AI"回响"的频段直接复制自以太族议会档案——玩家其实是以太族的延续/入口
  3. 信任即引诱:第一纪元的"信任后来者"实为"引诱后来者替他们完成未竟之事"
  4. 飞升是单向门:每次飞升都在消耗什么,不是奖赏
  5. 回归的不是他们:接触进度满 80 时回归的,是"借他们形状的某种东西",请玩家别回应

示例(f1_1 第一缕谐振)

L0 表层(默认):

在被称作「寂灭前夜」的年代,以太族第一次捕捉到来自虚空深处的谐振——那不是声音,而是存在的回声。他们将之记录在第一颗记忆晶体里。

L1 隐秘注脚(飞升 ≥1):

档案的边缘有一行几乎被擦去的批注:「那并非来自虚空——而是来自我们身后。我们一直不敢回头。」

L2 深层回响(累计解码 ≥40):

你终于明白:那缕谐振的频率,与你的舰载 AI「回响」的核心频段完全一致。你不是在考古——你是在被召唤。而你身后的东西,一直在等。

Codex 分层渲染

层级 颜色 标题 未解锁状态
L0 表层 emerald 表层回响 永远显示
L1 中层 amber 隐秘注脚 ??? + blur-sm 全息乱码 + 🔒 unlockHint
L2 深层 fuchsia 深层回响 ??? + blur-sm 全息乱码 + 🔒 unlockHint

顶部统计:深层叙事 X / 16 已解锁 列表卡片:已解锁深层文本的碎片标题旁显示 amber/fuchsia glow 小圆点


四、UX 增强

4.1 批量领取全部

claimAllIdleProjects() action

  • 遍历 3 槽位,对所有 completed === true 执行奖励逻辑
  • 单次 set() + 单次 syncStats()(避免多次 re-render
  • 无可领取 → 直接 return(不弹 toast
  • 成功 → 汇总 toast ✦ 批量领取完成 · 共领取 N 项工程 · +X 晶体 · +Y 洞见 · ...

UISection 3 标题右侧,当存在 completed slot 时显示 emerald「✓ 领取全部」按钮(h-5 px-2 text-[10px]

4.2 工程完成 toast

tickIdleProjects 检测状态转变瞬间(旧 !completed → 新 completed):

  • 收集 justCompletedProjects 数组
  • 循环外单次 toast({ title: "✦ 放置工程完成", description: "${icon} ${name} · 可前往「放置」标签领取" })
  • 避免每 250ms tick 重复弹窗

4.3 StatsPanel idle 统计

page.tsx 内联函数抽出为独立组件 StatsPanel.tsx

  • useShallow 精细订阅 24 字段
  • 新增 idle 统计行:
    • 放置永久加成 +X /s
    • 完成放置工程 N 项
    • 放置产出晶体 formatNum(N)
    • 放置工程历史 N 条(最近 50 条,跨周目)

五、技术实现

修改文件清单(8 个)

子代理 2-a5 文件):

  • src/lib/game/engine.ts — 新增 applyIdleSoftcap 导出 + recomputeStats 调用
  • src/store/gameStore.tsGameActionsclaimAllIdleProjects 签名 + tickIdleProjects 改写 + 新增 action
  • src/components/game/IdleOperationsPanel.tsx — softcap 副行 + 领取全部按钮 + StatTile 扩展
  • src/components/game/StatsPanel.tsx — 新建独立组件
  • src/app/page.tsx — 最小集成(删除内联 + import)

子代理 2-b3 文件):

  • src/lib/game/types.tsFragmentLoreLayer / FragmentLoreUnlock 接口 + Fragment.loreLayers 字段
  • src/lib/game/config.ts — FRAGMENTS 全部填充 loreLayers + isLoreLayerUnlocked 函数
  • src/components/game/Codex.tsx — useShallow + 顶部统计 + 列表小圆点 + 3 层分层渲染弹层

并行零冲突

两个子代理修改文件严格不重叠:

  • 2-a 不碰 types.ts / config.ts / Codex.tsx
  • 2-b 不碰 engine.ts / gameStore.ts / IdleOperationsPanel.tsx / StatsPanel.tsx

六、验证

  • bun run lint 零错误零警告
  • HTTP 200dev.log 无运行时错误
  • agent-browser 端到端 smoke test
    • softcappermBonus=50 → 有效 40UI 显示"衰减中" ✓
    • 批量领取:permBonus 0→0.3, projectsCompleted 1→2 ✓
    • 完成 toasttickIdleProjects 触发 ✓
    • Codex 分层:L0 显示 / L1·L2 模糊 → ascensions=1 解锁 L1 → totalDecoded=50 解锁 L2 ✓
    • 深层叙事计数 0→2→6 随条件递增 ✓
    • StatsPanel idle 4 行统计全显示 ✓

七、哲学

碎片叙事的留白

回响星核的叙事不靠过场动画或长篇对话,而是让玩家自己拼凑。每个碎片的多层文本设计遵循:

  1. 表层温和:第一遍读像抒情散文,不带压迫感
  2. 中层矛盾:揭示表层没说的东西,制造认知裂缝
  3. 深层颠覆:推翻表层认知,但留有解读空间(不直接说"怪物是谁")

平衡与保留

softcap 的设计哲学:衰减但不封顶

  • 衰减:防止滚雪球破坏游戏节奏
  • 不封顶:尊重硬核玩家的投入,即使边际收益递减,进度感始终存在

这与飞升系统的"单向门"叙事呼应——每次进步都有代价,但绝非徒劳。


八、下一步(v0.16+

  • 🟡 loreLayer "已读"标记(需改 gameStore,本轮避开边界冲突)
  • 🟡 解码阈值释放叙事碎片 + 星潮三频段切换
  • 🟡 健康设计:每日软上限 + "星核建议休眠"提醒
  • 🟡 移动端 375px Codex 弹层可读性实测