v0.15: 叙事深化 + 放置平衡 + UX 增强
- loreLayers 多层碎片文本(8 碎片 × 2 层 = 16 层深层叙事,5 条暗线交织) - 永久加成 softcap(≤30 全额 / 30-100 ×0.5 / >100 ×0.25,防滚雪球) - 批量领取全部工程 + 工程完成 toast 通知 - StatsPanel 独立组件 + idle 统计行(永久加成/完成工程/产出晶体/历史) - Codex 分层渲染(L0 emerald 表层 / L1 amber 隐秘注脚 / L2 fuchsia 深层回响) - 修复 v0.14 双重计算 bug(idleTotalPerSec) - 严格 4 色全息,lint 零错误,agent-browser 端到端验证通过
This commit is contained in:
@@ -0,0 +1,214 @@
|
||||
# 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 洞见 · ...`
|
||||
|
||||
UI:Section 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-a(5 文件):**
|
||||
- `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-b(3 文件):**
|
||||
- `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 200,dev.log 无运行时错误
|
||||
- ✅ agent-browser 端到端 smoke test:
|
||||
- softcap:permBonus=50 → 有效 40,UI 显示"衰减中" ✓
|
||||
- 批量领取:permBonus 0→0.3, projectsCompleted 1→2 ✓
|
||||
- 完成 toast:tickIdleProjects 触发 ✓
|
||||
- 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 弹层可读性实测
|
||||
Reference in New Issue
Block a user