feat: add age-based event gating, event timer, and UI polish

- Add minAge/maxAge to events so infants can't go treasure hunting
- Cache event panel DOM to prevent high-speed button destruction
- Add 10s auto-select countdown for choice events
- Fix event title/text field mapping (name/description → title/text)
- Add rotating clock icon for time flow feedback
- Fix speed/pause button active states
- Fix shop affordability check (disable + show insufficient money)
- Add red styling for unmet choice requirements
- Fix log re-rendering on every tick
This commit is contained in:
2026-05-13 09:09:42 +00:00
parent a05225b514
commit 3f741b4f0a
41 changed files with 9847 additions and 651 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,496 @@
# 轮回录 - 配置驱动重构设计文档
## 目标
将《轮回录》从硬编码内容重构为**纯配置驱动框架**。
- `engine/` 中零游戏内容字符串,全部从 `config/*.json` 加载
- 职业系统改为**网状并行非转职**结构
- 外族入侵从进度条改为**年龄触发剧情事件**
- 新增银两经济 + 系统商铺(3 Tab)
- 前几世不可能破局,通过多轮积累解锁后期内容
---
## 一、目录结构
```
rebirth-incremental/
├── index.html
├── src/
│ ├── main.js # 入口:加载配置 → 初始化引擎 → 启动
│ ├── engine/ # 纯框架,零硬编码内容
│ │ ├── state.js # 状态管理 + 序列化
│ │ ├── tickLoop.js # 游戏主循环(speed控制、每日结算)
│ │ ├── conditionEvaluator.js # 统一条件解析器(所有系统共用)
│ │ ├── effectApplier.js # 统一效果执行器(所有系统共用)
│ │ ├── careerEngine.js # 职业网引擎(解锁判定、经验计算、收入结算)
│ │ ├── eventEngine.js # 事件引擎(触发判定、选择处理)
│ │ ├── shopEngine.js # 商铺引擎(购买、生效、重置逻辑)
│ │ ├── moneySystem.js # 银两系统(收入、支出、余额)
│ │ ├── invasionEngine.js # 外族入侵引擎(年龄触发、破局判定)
│ │ └── deathEngine.js # 死亡与重生引擎
│ ├── config/ # 所有内容JSON
│ │ ├── careers.json # 职业定义(含解锁条件、收入、经验曲线)
│ │ ├── events.json # 普通事件 + 入侵事件
│ │ ├── shop.json # 物品 + Buff + 神器
│ │ ├── identities.json # 开局身份
│ │ └── talents.json # 词条
│ └── ui/
│ ├── renderer.js # 主渲染调度
│ └── components/ # UI组件(后续拆分)
```
---
## 二、状态结构(state.js
```javascript
state = {
// 时间
day: 0,
year: 0,
age: 0,
reincarnation: 0,
// 玩家基础
alive: true,
identity: null, // 开局身份ID
stats: {
body: 0, // 体质
wisdom: 0, // 悟性
charm: 0, // 魅力
destiny: 0, // 天命
business: 0, // 经商
intelligence: 0 // 智力
},
talents: [], // 本轮抽到的词条ID数组
// 职业网(并行,每个职业独立等级)
careers: {
// "student": { level: 150, exp: 1234 }
},
// 经济
money: 0,
// 商铺(本轮有效)
shopItems: {}, // { itemId: quantity }
activeBuffs: {}, // { buffId: { expiresDay } }
// 神器(跨轮永久)
artifacts: {}, // { artifactId: level }
// 世界状态
worldFlags: {},
npcs: {},
// 入侵状态
invasionsTriggered: {
military_40: false,
spiritual_45: false,
political_50: false,
final_60: false
},
breakthroughRoute: null, // 'general' | 'minister' | 'immortal' | null
// 运行状态
speed: 0,
paused: false,
logs: [],
triggeredEvents: new Set(),
// 跨轮永久积累
metaExp: {}, // { careerId: amount }
memories: [],
unlockedEntries: new Set(),
history: [],
unlockedShopPool: new Set(), // 已解锁的商铺物品资格(永久)
};
```
---
## 三、6属性加成系统
每点属性提供 **1%** 对应加成,**上限100%**(即属性最多生效到100点)。
| 属性 | 加成目标 |
|------|---------|
| 体质(body) | 军事/武将类职业经验获取 |
| 悟性(wisdom) | 修仙/书生类职业经验获取 |
| 魅力(charm) | 江湖/社交类职业经验获取 |
| 天命(destiny) | 事件收益/随机暴击概率 |
| 经商(business) | 银两收入 |
| 智力(intelligence) | 政治/策略类职业经验获取 |
**计算公式:**
```
加成倍率 = 1 + min(属性值, 100) * 0.01
职业经验 = 基础每日经验 * (1 + 对应属性 * 0.01) * 元经验倍率 * 其他倍率
银两收入 = 基础收入 * (1 + 经商 * 0.01) * 其他倍率
```
---
## 四、职业系统
### 4.1 核心规则
- **并行非转职**:所有已解锁职业同时存在,各自独立等级,不是替换关系
- **解锁驱动**:满足条件后新职业加入可练池,旧职业继续升级
- **收入/支出**:每日tick时所有已解锁职业的收入求和(负数为支出)
- **经验曲线**`needExp = base * factor^level`,无硬上限,等级可无限升
- **属性加成**:职业JSON中标记`type`,对应属性提供经验加成
### 4.2 careers.json 格式
```json
{
"careers": [
{
"id": "student",
"name": "学童",
"type": "scholar",
"unlockConditions": [
{ "type": "age", "op": ">=", "value": 6 }
],
"dailyIncome": 1,
"expCurve": { "base": 10, "factor": 1.15 }
},
{
"id": "book_boy",
"name": "书童",
"type": "scholar",
"unlockConditions": [
{ "type": "careerLevel", "careerId": "student", "op": ">=", "value": 100 }
],
"dailyIncome": 2,
"expCurve": { "base": 50, "factor": 1.2 }
},
{
"id": "scholar",
"name": "书生",
"type": "scholar",
"unlockConditions": [
{ "type": "careerLevel", "careerId": "student", "op": ">=", "value": 250 },
{ "type": "age", "op": ">=", "value": 14 }
],
"dailyIncome": -3,
"expCurve": { "base": 100, "factor": 1.25 }
}
]
}
```
### 4.3 职业type与属性映射
| type | 对应属性 |
|------|---------|
| military | 体质 |
| scholar | 悟性 |
| jianghu | 魅力 |
| political | 智力 |
| immortal | 悟性 |
| business | 经商 |
---
## 五、系统商铺(3 Tab
### 5.1 设计原则
| Tab | 是否重置 | 说明 |
|-----|---------|------|
| 物品 | 每轮清零 | 属性增强、解锁职业资格(如道碟)、消耗品 |
| Buff | 每轮清零 | 属性加成、经验倍率加成,限时或永久(本轮) |
| 神器 | 跨轮永久 | 属性+特殊效果,可用银两升级,需解锁资格后才能升级 |
### 5.2 shop.json 格式
```json
{
"items": [
{
"id": "taoist_license",
"name": "道碟",
"tab": "item",
"cost": 500,
"unlockConditions": [
{ "type": "age", "op": ">=", "value": 10 },
{ "type": "or", "conditions": [
{ "type": "identity", "value": "taoist_orphan" },
{ "type": "worldFlag", "flag": "met_taoist_priest", "value": true }
]}
],
"effects": [
{ "type": "unlockCareer", "careerId": "taoist_priest" },
{ "type": "setFlag", "flag": "has_taoist_license", "value": true }
],
"resetOnRebirth": true
}
],
"buffs": [
{
"id": "clever_brush",
"name": "妙笔生花",
"tab": "buff",
"cost": 200,
"unlockConditions": [],
"effects": [
{ "type": "addStat", "stat": "wisdom", "value": 5 }
],
"resetOnRebirth": true
}
],
"artifacts": [
{
"id": "immortal_sword",
"name": "仙剑",
"tab": "artifact",
"baseCost": 1000,
"costGrowth": 1.5,
"unlockConditions": [
{ "type": "careerLevel", "careerId": "sword_immortal", "op": ">=", "value": 100 }
],
"effects": [
{ "type": "addStat", "stat": "body", "value": 10 },
{ "type": "multiplyExp", "careerType": "immortal", "value": 1.2 }
],
"resetOnRebirth": false,
"maxLevel": 10
}
]
}
```
### 5.3 神器升级公式
```
升级所需银两 = baseCost * costGrowth^(当前等级)
例:baseCost=1000, costGrowth=1.5
1→2级:1000
2→3级:1500
3→4级:2250
```
---
## 六、统一条件与效果系统
### 6.1 条件格式(conditionEvaluator.js
所有条件统一JSON格式,引擎提供 `evaluateCondition(condition, state)` → boolean。
```json
// 基础条件
{ "type": "age", "op": ">=", "value": 14 }
{ "type": "careerLevel", "careerId": "student", "op": ">=", "value": 100 }
{ "type": "stat", "stat": "wisdom", "op": ">=", "value": 20 }
{ "type": "money", "op": ">=", "value": 500 }
{ "type": "hasItem", "itemId": "taoist_license" }
{ "type": "identity", "value": "taoist_orphan" }
{ "type": "worldFlag", "flag": "met_taoist", "value": true }
{ "type": "reincarnation", "op": ">=", "value": 3 }
{ "type": "hasArtifact", "artifactId": "immortal_sword", "op": ">=", "value": 3 }
// 组合条件
{ "type": "and", "conditions": [...] }
{ "type": "or", "conditions": [...] }
{ "type": "not", "condition": {...} }
```
### 6.2 效果格式(effectApplier.js
所有效果统一JSON格式,引擎提供 `applyEffect(effect, state)` → 修改state。
```json
{ "type": "addMoney", "value": 100 }
{ "type": "addExp", "careerId": "student", "value": 10 }
{ "type": "addStat", "stat": "wisdom", "value": 5 }
{ "type": "setFlag", "flag": "x", "value": true }
{ "type": "unlockCareer", "careerId": "taoist_priest" }
{ "type": "addItem", "itemId": "taoist_license" }
{ "type": "addBuff", "buffId": "clever_brush", "duration": -1 }
{ "type": "upgradeArtifact", "artifactId": "immortal_sword" }
{ "type": "addLog", "text": "...", "logType": "major" }
{ "type": "die", "reason": "..." }
```
---
## 七、事件系统
### 7.1 事件分类
| 类型 | 触发方式 | 说明 |
|------|---------|------|
| random | 权重随机池 | 普通日常事件,按密度触发 |
| age | 到达指定年龄 | 入侵事件、关键剧情 |
| flag | 世界Flag变化 | 连锁剧情 |
### 7.2 events.json 格式
```json
{
"events": [
{
"id": "daily_training",
"trigger": { "type": "random", "pool": "normal", "weight": 100 },
"text": "你进行了日常训练...",
"effects": [
{ "type": "addExp", "careerId": "student", "value": 5 }
]
},
{
"id": "invasion_military_40",
"trigger": { "type": "age", "value": 40 },
"isChoice": true,
"title": "蛮族入侵",
"text": "北方蛮族大举南下...",
"choices": [
{
"text": "组织乡勇抵抗",
"requirements": [
{ "type": "careerLevel", "careerId": "soldier", "op": ">=", "value": 50 }
],
"effects": [
{ "type": "setFlag", "flag": "invasion_military_resisted", "value": true },
{ "type": "unlockCareer", "careerId": "general" }
]
},
{
"text": "逃往南方",
"effects": [
{ "type": "addStat", "stat": "body", "value": -2 }
]
}
]
}
]
}
```
---
## 八、外族入侵与破局系统
### 8.1 入侵事件时间表
| 年龄 | 事件 | 入侵类型 |
|------|------|---------|
| 40岁 | 第一次入侵 | 军事(蛮族) |
| 45岁 | 第二次入侵 | 精神(天魔) |
| 50岁 | 第三次入侵 | 政治(王朝崩坏) |
| 60岁 | 最终危机 | 三相汇聚,必须破局 |
### 8.2 破局路线
破局路线是极后期的职业系统,需要多世积累才能达到解锁条件:
| 破局路线 | 前置条件示例 | 对应入侵 |
|---------|------------|---------|
| 将军破局 | 士兵职业500级 + 体质>80 + 神器"虎符"Lv5 | 军事 |
| 宰相破局 | 政治职业500级 + 智力>80 + 神器"玉玺"Lv5 | 政治 |
| 修仙破局 | 修仙职业500级 + 悟性>80 + 神器"飞剑"Lv5 | 精神 |
**前几世不可能破局**——即使运气极好,属性和职业等级也远远不够。
---
## 九、游戏循环(每tick
```
for each day:
1. 年龄增长检查(day % 365 === 0
2. 银两结算(所有已解锁职业的 dailyIncome 求和 * 经商加成)
3. 职业经验结算(所有已解锁职业的每日经验 * 对应属性加成 * 元经验倍率)
4. 检查职业解锁(遍历careers.json,条件满足则加入state.careers
5. 检查年龄触发事件(40/45/50/60岁入侵,只触发一次)
6. 随机事件触发(按密度池抽取)
7. 检查死亡条件(年龄、属性、入侵后果)
8. 渲染UI
```
---
## 十、前几世节奏设计
| 轮次 | 目标 | 解锁内容 |
|------|------|---------|
| 第1世 | 熟悉系统,解锁基础职业,赚少量银两买小Buff | 学童、农夫等 |
| 第2世 | 词条/记忆开局+属性,解锁中级职业,买第一件神器 | 书童、猎户等 |
| 第3世 | 神器等级提升,属性更高,解锁高级职业 | 书生、侠客等 |
| 第4世+ | 接近破局门槛,尝试解锁破局职业 | 将军、宰相、修仙 |
---
## 十一、存档与跨轮继承
### 11.1 每轮重置
- 年龄、天数、银两、世界Flag、NPC关系
- 已购买的物品和Buff
- 职业等级(但元经验保留,下轮加速)
### 11.2 跨轮永久保留
- 元经验(metaExp
- 记忆(memories
- 已解锁词条池(unlockedEntries
- 神器及等级(artifacts
- 历史记录(history
- 解锁的商铺资格(unlockedShopPool
---
## 十二、UI 面板
| 面板 | 内容 |
|------|------|
| 顶部信息 | 第N世、年月日、年龄、银两、6属性值 |
| 入侵进度 | 三次入侵触发状态、破局路线 |
| 控制按钮 | 速度、暂停、存档、读档 |
| 事件面板 | 当前事件标题、文本、倒计时、选择按钮 |
| 商铺面板 | 3 Tab(物品/Buff/神器),显示解锁状态和价格 |
| 职业面板 | 所有已解锁职业列表,显示等级、经验条、每日收入 |
| 日志流 | 事件记录 |
| 词条面板 | 本轮抽到的词条 |
| 神器面板 | 已拥有神器及等级 |
| 死亡结算 | 死亡原因、本轮成就、新解锁内容、历史对比 |
---
## 十三、实现优先级
1. **Phase 1:框架搭建**
- 新建目录结构
- 实现 `conditionEvaluator.js` + `effectApplier.js`
- 实现 `state.js` + 序列化
- 实现 `tickLoop.js`
2. **Phase 2:职业网 + 经济**
- 实现 `careerEngine.js` + `moneySystem.js`
- 编写 `careers.json` 框架(少量示例职业)
3. **Phase 3:事件系统**
- 实现 `eventEngine.js`
- 编写 `events.json` 框架
4. **Phase 4:商铺系统**
- 实现 `shopEngine.js`
- 编写 `shop.json` 框架(3 Tab示例)
5. **Phase 5:入侵与破局**
- 实现 `invasionEngine.js`
- 编写入侵事件JSON
6. **Phase 6:死亡重生 + 跨轮继承**
- 实现 `deathEngine.js`
- 完成跨轮状态过滤
7. **Phase 7UI**
- 重写 `renderer.js`
- 新增商铺面板、神器面板
8. **Phase 8:内容填充**
- 大量编写JSON内容(职业、事件、商铺、身份、词条)