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

215 lines
8.3 KiB
Markdown
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
### 公式
```ts
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 多层碎片文本
### 类型扩展
```ts
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.ts``GameActions``claimAllIdleProjects` 签名 + `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.ts``FragmentLoreLayer` / `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 弹层可读性实测