update design

This commit is contained in:
2026-04-30 10:36:25 +08:00
parent 55506eff9a
commit a38a8d4b39
25 changed files with 999 additions and 113 deletions
+153 -1
View File
@@ -581,4 +581,156 @@ Boss 识别规则:
4. 敌人事件处理是否仍然只做公共副作用,而不直接切状态?
5. 状态迁移是否仍然统一走 `ChangeState(...)`?
6. `MapEntity` 是否仍然只通过 `MapData + Event` 获取战斗上下文?
7. 清理是否仍按“敌人 / 地图基础 UI / 结算 UI / 运行时数据”分工执行?
7. 清理是否仍按”敌人 / 地图基础 UI / 结算 UI / 运行时数据”分工执行?
---
## 12. Combat Economy(战斗经济)
> **Status**: 新增段落 — GDD 一致性审查中发现 Coin 经济在实现中存在但未写入文档
> **最后更新**: 2026-04-30
本段说明战斗域内双货币体系的完整设计依据。所有数值均来自数据表驱动,无需代码硬编码。
---
### 12.1 双货币架构概述
游戏使用两层货币,分属不同生命周期:
| 货币 | 生命周期 | 存储位置 | 描述 |
|------|----------|----------|------|
| **Coin** | 单次战斗内 | `CombatRunResourceStore.CurrentCoin` | 战斗内部经济,用于建塔/升级/拆除 |
| **Gold** | 整局运行 | `PlayerInventoryComponent.Gold` | 跨节点持久,用于商店购买、出售 |
**设计意图**: Coin 创造战斗内的即时决策张力(”我现在花 Coin 建塔还是留着防万一?”);Gold 创造跨节点的战略张力(”我是现在买还是等下一个商店?”)。两层经济分离,防止任一层的决策深度被稀释。
**关键约束**: Coin 不跨战斗持久,战斗结束时清零。战斗胜利后 `GainedGold` 入账,`GainedCoin` 不入账。
---
### 12.2 Coin(战斗内部货币)
#### 12.2.1 来源
| 来源 | 字段 | 说明 |
|------|------|------|
| 关卡初始 Coin | `DRLevel.StartCoin` | 战斗开始时发放,来源于 `CombatRunResourceStore.InitializeForCombat()` |
| 敌人击杀奖励 | `DREnemy.DropCoin` | 每次击杀敌人时发放,来源于 `CombatRunResourceStore.AddEnemyDefeatedReward()` |
`StartCoin` 按关卡难度配置,确保玩家在战斗开始时有足够的决策空间。建议低难度关卡 `StartCoin` 较低,高难度/Boss 关卡较高。
`DropCoin` 按敌人类型配置。建议普通敌人 `DropCoin` 较低(5–15),精英敌人较高(20–50),与 `DropGold` 分开计算。
#### 12.2.2 消耗(Sinks)
| 操作 | 消耗方式 | 触发时机 |
|------|----------|----------|
| **BuildTower** | `TryConsumeCoin(buildTowerCost[i])` | 玩家在战斗内点击建塔位,每槽位独立计费 |
| **UpgradeTower** | `TryConsumeCoin(upgradeCost)` | 玩家升级已有塔 |
| **DestroyTower** | 无消耗;返回 `destroyGain` Coin | 玩家拆除已有塔,Coin 返还 |
建塔槽位共 4 个(对应 `BuildOptionCount = 4`),每个槽位可独立消耗 Coin。每槽位的 `buildTowerCost[i]` 由关卡或战斗阶段配置。
#### 12.2.3 追踪
`CombatRunResourceStore.GainedCoin` 记录本场战斗累计获得的 Coin。战斗结束时 `GainedCoin` 计入 `RunStats.coinsEarned`(跨战斗累计),但 Coin 本身不清零重置于下一个战斗,而是**每个新战斗重新从 `DRLevel.StartCoin` 开始**。
```
// 每场新战斗开始时
CurrentCoin = DRLevel.StartCoin // 从关卡配置重置,非累加
GainedCoin = 0 // 重置,用于本场统计
```
#### 12.2.4 数据驱动约束
| 约束 | 值 | 说明 |
|------|---|------|
| `DRLevel.StartCoin` | ≥ 0 | 建议普通关卡 50–200,Boss 关卡 100–300 |
| `DREnemy.DropCoin` | ≥ 0 | 建议普通敌人 5–15,精英 20–50,Boss 0(避免重复计费) |
| `buildTowerCost[i]` | ≥ 0 | 由 `CombatSelectFormUseCase` 从关卡配置读取,建议 20–80 每槽位 |
| `upgradeCost` | ≥ 0 | 由 `CombatSelectFormUseCase` 从关卡配置读取,建议 50–150 |
| `destroyGain` | ≥ 0 | 建议 = `buildTowerCost * 0.5`(返还 50%),与商店售价机制一致 |
---
### 12.3 Gold(战斗内获取部分)
战斗过程中 Gold 获取有两个来源:
#### 12.3.1 敌人击杀掉落
```
// DREnemy 字段
DropGold // 掉落金币数额
DropPercent // 掉落概率 [0.0, 1.0]
```
战斗胜利时(敌人被击杀或波次结束),若随机值 `≤ DropPercent`,则 `AddEnemyDefeatedReward(gainedCoin, gainedGold)` 被调用,`gainedGold = DropGold`。
#### 12.3.2 关卡胜利奖励
战斗胜利结算时(`CombatSettlementState`),`CombatSettlementCalculator` 计算本场战斗总 Gold 奖励:`DRLevel.RewardGold`,通过 `CombatRunResourceStore.AddSettlementGold()` 入账。
战斗失败时:无 `RewardGold`,`GainedGold = 0`。
#### 12.3.3 追踪
`CombatRunResourceStore.GainedGold` 记录本场战斗累计获得的 Gold(击杀掉落 + 胜利奖励)。战斗结束时通过 `GetRewardInventorySnapshot()` 合并至玩家主背包(`PlayerInventoryComponent.MergeInventory()`),触发 `MaxPlayerGold = 9999` 上限检查(溢出部分丢弃)。
---
### 12.4 Coin 与 Gold 的运行时边界
```
战斗开始 → InitializeForCombat(level)
└→ CurrentCoin = level.StartCoin // Coin 重置
└→ GainedCoin = 0, GainedGold = 0 // 本场统计重置
战斗过程中(敌人死亡)→ AddEnemyDefeatedReward(dropCoin, dropGold)
└→ CurrentCoin += dropCoin
└→ CurrentGold (奖励库存) += dropGold // 不影响主背包
└→ GainedCoin += dropCoin
└→ GainedGold += dropGold
战斗胜利 → AddSettlementGold(RewardGold)
└→ GainedGold += RewardGold
战斗结束 → GetRewardInventorySnapshot() → MergeInventory()
└→ 主背包 Gold += min(rewardGold, MaxPlayerGold - currentGold)
└→ 主背包组件 += 奖励组件
└→ RunStats.coinsEarned += GainedCoin // 仅统计,不持久化 Coin
```
---
### 12.5 Progression 的 coinEarned 字段
| 字段 | 类型 | 说明 |
|------|------|------|
| `RunStats.coinsEarned` | int | **跨战斗累计**:本 run 所有战斗累计获得的 Coin 总和(战斗失败也计入) |
`coinsEarned` 用于统计目的,不产生任何游戏内效果。它记录玩家在一局 run 中总共获得了多少 Coin — 可用于未来功能(如”累计获得 10000 Coin”成就),但当前无对应解锁或奖励。
---
### 12.6 与商店经济的隔离
Coin 仅在战斗域内流通。商店系统(`design/gdd/shop.md`)处理的 buy/sell 交易仅涉及 Gold,不涉及 Coin。
- 战斗内**不会**触发商店交易
- 商店内**不会**消耗或获得 Coin
- 战斗结束时,Coin 不转换为 Gold(`GainedCoin` 仅计入 `RunStats`,不进入玩家背包)
---
### 12.7 调试与一致性检查
| 检查项 | 预期结果 |
|--------|----------|
| 新战斗开始时 `CurrentCoin == DRLevel.StartCoin` | 相等 |
| 战斗结束时 `GainedCoin ≥ 0` | 大于等于零 |
| 战斗失败时 `GainedGold == 0` | 战斗失败不发放 Gold 奖励 |
| `MaxPlayerGold` 上限检查 | `MergeInventory` 后背包 Gold ≤ 9999 |
| Coin 消耗后 `CurrentCoin ≥ 0` | `TryConsumeCoin` 失败时 `CurrentCoin` 不变 |