# Mana 完整化实现计划（递增蓝耗 + infinite_spells + 魔力虹吸 + UI + 平衡）

> **For agentic workers:** 用 superpowers:subagent-driven-development（推荐）逐任务实现。步骤用 `- [ ]` 复选框。

**Goal:** 把 Mana MVP 补成可发布完整功能：递增蓝耗防机枪、infinite_spells 卖血续命、多来源魔力虹吸、HUD 打磨、平衡。

**Architecture:** 扣蓝统一走 `SpellEvaluator._charge_mana()`（热值乘算 + infinite 转 HP 代价）；heat/leech 状态在 PlayerStats，配置在 balance.json（SettingsManager 加载）；leech 换杖重算；HP 代价累加于 SpellContext、施法末结算（15% 上限）。

**Tech Stack:** Godot 4.6 GDScript · 纯 JSON 数据驱动 · 验证用 Godot MCP，无单元测试框架。

> **验证约定**：每任务 MCP 运行时断言。前置 Godot+MCP 连通。改码后 `reload_project`+`get_editor_errors`（0；新 autoload/新常量引用误报以 `play_scene` 运行时为准）+ `play_scene(main)` 后 `execute_game_script`；@tool 编辑器改动用 `execute_editor_script`+`CACHE_MODE_IGNORE` fresh-load。跑完 `stop_scene`。

---

### Task 1: balance.json 配置 + SettingsManager getters

**Files:** `data/balance.json`, `scripts/autoloads/settings_manager.gd`

- [ ] **Step 1: balance.json 加配置**

在 `data/balance.json` 顶层对象里（`aether_thresholds` 之后、末尾 `}` 前）加两段（记得前一行补逗号）：
```json
  "mana_heat": { "per_cast": 0.01, "decay_per_sec": 0.1, "max": 1.0 },
  "infinite_spells": { "hp_per_shot": 1.0, "hp_cost_cap_ratio": 0.15 }
```

- [ ] **Step 2: SettingsManager 加载 + getters**

在 `settings_manager.gd` 的 `var _AETHER_THRESHOLDS: Array = []` 之后加：
```gdscript
var _MANA_HEAT: Dictionary = {"per_cast": 0.01, "decay_per_sec": 0.1, "max": 1.0}
var _INFINITE: Dictionary = {"hp_per_shot": 1.0, "hp_cost_cap_ratio": 0.15}
```
在 `_load_balance()` 里 `if data.has("aether_thresholds") ...` 块之后加：
```gdscript
	if data.has("mana_heat") and data["mana_heat"] is Dictionary:
		for k in data["mana_heat"]:
			_MANA_HEAT[k] = data["mana_heat"][k]
	if data.has("infinite_spells") and data["infinite_spells"] is Dictionary:
		for k in data["infinite_spells"]:
			_INFINITE[k] = data["infinite_spells"][k]
```
在 `aether_for_wave()` 之后加 getters：
```gdscript

func mana_heat_per_cast() -> float:  return float(_MANA_HEAT.get("per_cast", 0.01))
func mana_heat_decay() -> float:     return float(_MANA_HEAT.get("decay_per_sec", 0.1))
func mana_heat_max() -> float:       return float(_MANA_HEAT.get("max", 1.0))
func infinite_hp_per_shot() -> float: return float(_INFINITE.get("hp_per_shot", 1.0))
func hp_cost_cap_ratio() -> float:   return float(_INFINITE.get("hp_cost_cap_ratio", 0.15))
```

- [ ] **Step 3: 验证（MCP）** — `play_scene(main)`：
```gdscript
_mcp_print("heat per=%s decay=%s max=%s | inf hp=%s cap=%s (expect 0.01/0.1/1/1/0.15)" % [str(SettingsManager.mana_heat_per_cast()), str(SettingsManager.mana_heat_decay()), str(SettingsManager.mana_heat_max()), str(SettingsManager.infinite_hp_per_shot()), str(SettingsManager.hp_cost_cap_ratio())])
```
Expected: 0.01/0.1/1.0/1.0/0.15。

- [ ] **Step 4: Commit**
```bash
git add data/balance.json scripts/autoloads/settings_manager.gd
git commit -m "feat(mana): balance.json mana_heat/infinite_spells 配置 + SettingsManager getters"
```

---

### Task 2: PlayerStats + SpellContext 新状态

**Files:** `scripts/autoloads/player_stats.gd`, `scripts/domain/spell_system/spell_context.gd`

- [ ] **Step 1: PlayerStats 加字段**

在 `player_stats.gd` 的 `var mana_regen: float = 5.0` 之后加：
```gdscript
var mana_heat:  float = 0.0    # 递增蓝耗热值（0=无，1=蓝耗翻倍）
var mana_leech: float = 0.0    # 击杀回蓝量（换杖/换牌重算）
```

- [ ] **Step 2: PlayerStats 加方法**

在 `regen_mana()` 之后加：
```gdscript

## 击杀回蓝（魔力虹吸）
func gain_mana(n: float) -> void:
	if n <= 0.0:
		return
	mana = min(mana_max, mana + n)
	stats_changed.emit()

## 主动 HP 代价（infinite_spells / heavy_cost）——直接扣血，不走难度减伤
func spend_hp_cost(amount: float) -> void:
	if amount <= 0.0:
		return
	hp = max(0.0, hp - amount)
	stats_changed.emit()
	if hp <= 0.0:
		EventBus.emit(EventID.PLAYER_DIED, {})
```

- [ ] **Step 3: PlayerStats 订阅击杀回蓝**

`_ready()` 当前为：
```gdscript
func _ready() -> void:
	EventBus.subscribe(EventID.PLAYER_DAMAGED, _on_player_damaged)
```
改为：
```gdscript
func _ready() -> void:
	EventBus.subscribe(EventID.PLAYER_DAMAGED, _on_player_damaged)
	EventBus.subscribe(EventID.ENEMY_KILLED, _on_enemy_killed)

func _on_enemy_killed(_payload: Dictionary) -> void:
	gain_mana(mana_leech)
```
（`reset_for_run()` 已置 mana=mana_max；无需动 mana_heat/leech，它们由 PlayerManager/combat_manager 管。）

- [ ] **Step 4: SpellContext 加 hp_cost_accum**

`spell_context.gd`：在 `var registers: PackedFloat32Array = PackedFloat32Array()` 之后加：
```gdscript
var hp_cost_accum:   float = 0.0    # 本次施法累积 HP 代价（heavy_cost + infinite），施法末结算
```
在 `reset()` 里 `if stats: stats.reset() ...` 之后加：
```gdscript
	hp_cost_accum = 0.0
```

- [ ] **Step 5: 验证（MCP）**
```gdscript
PlayerStats.set_mana_pool(100.0, 5.0); PlayerStats.mana = 50.0
PlayerStats.mana_leech = 4.0
PlayerStats.gain_mana(PlayerStats.mana_leech)
_mcp_print("gain_mana 4 -> mana=%s (expect 54)" % str(PlayerStats.mana))
PlayerStats.hp = 100.0; PlayerStats.hp_max = 100.0
PlayerStats.spend_hp_cost(11.0)
_mcp_print("spend_hp 11 -> hp=%s (expect 89, 直接扣不减伤)" % str(PlayerStats.hp))
var c = SpellContext.new(); c.hp_cost_accum = 5.0; c.reset()
_mcp_print("ctx reset -> hp_cost_accum=%s (expect 0)" % str(c.hp_cost_accum))
```
Expected: 54 / 89 / 0。`get_editor_errors` 运行时无（play_scene 能跑）。

- [ ] **Step 6: Commit**
```bash
git add scripts/autoloads/player_stats.gd scripts/domain/spell_system/spell_context.gd
git commit -m "feat(mana): PlayerStats mana_heat/leech+gain_mana/spend_hp_cost+击杀回蓝；SpellContext hp_cost_accum"
```

---

### Task 3: PlayerManager 每帧衰减 heat + 换杖清零

**Files:** `scripts/domain/player_manager.gd`

- [ ] **Step 1: 每帧衰减**

`_physics_process()` 当前首行是 `PlayerStats.regen_mana(delta)`。在其后加：
```gdscript
	PlayerStats.mana_heat = maxf(0.0, PlayerStats.mana_heat - SettingsManager.mana_heat_decay() * delta)
```

- [ ] **Step 2: 换杖清零 heat**

`equip_wand()` 里 `PlayerStats.set_mana_pool(...)` 分支之后加（函数末尾）：
```gdscript
	PlayerStats.mana_heat = 0.0
```

- [ ] **Step 3: 验证（MCP，需战斗自动施法或手动）**
```gdscript
PlayerStats.mana_heat = 0.5
_mcp_print("set heat 0.5")
```
下一次调用（间隔真实时间，_physics_process 衰减）：
```gdscript
_mcp_print("heat after wait=%s (expect < 0.5，衰减中)" % str(PlayerStats.mana_heat))
```
Expected: 第二次 < 0.5（趋向 0）。

- [ ] **Step 4: Commit**
```bash
git add scripts/domain/player_manager.gd
git commit -m "feat(mana): PlayerManager 每帧衰减 mana_heat + 换杖清零"
```

---

### Task 4: SpellEvaluator — 扣蓝 helper（热值乘算 + infinite 扣血）+ heavy_cost + HP 结算

**Files:** `scripts/domain/spell_system/spell_evaluator.gd`

- [ ] **Step 1: 加 `_charge_mana` helper**

在 `_apply_modifier` 函数之前（`## ── 内部方法` 区）加：
```gdscript
## 统一扣蓝：base_cost>0 时按热值乘算扣蓝；infinite Core 蓝空转累加 HP 代价并继续
## 返回 true=可执行该节点；false=蓝不足且非 infinite → 停施
func _charge_mana(base_cost: float, ctx: SpellContext, infinite: bool, heat_mult: float) -> bool:
	if base_cost <= 0.0:
		return true
	if PlayerStats.spend_mana(base_cost * heat_mult):
		return true
	if infinite:
		ctx.hp_cost_accum += SettingsManager.infinite_hp_per_shot()
		return true
	return false
```

- [ ] **Step 2: execute_compiled — 计算 infinite/heat + 改门控 + 结算**

将 `execute_compiled` 从 `var ctx: SpellContext = _acquire_ctx(...)` 到 while 循环体，改为如下（新增 infinite/heat_mult/cast_fired，替换门控行 333-335，并把 loop/branch 调用加参数）：
```gdscript
	var ctx: SpellContext = _acquire_ctx(caster_id, persistent)
	ctx.hp_cost_accum = 0.0
	var infinite: bool = compiled.feature_tags == CoreFeatureTag.INFINITE_SPELLS
	var heat_mult: float = 1.0 + PlayerStats.mana_heat
	var cast_fired: bool = false
	var deck: SpellDeck   = compiled.make_runtime_deck()
	var ops_count:         int = 0
	var max_ops:           int = MAX_OPS_PER_CPU * 5
	var actions_remaining: int = 1
	while deck.has_next() and ops_count < max_ops:
		ops_count += 1
		var node: SpellNode = deck.pop()
		var mana_cost: float = float(node.meta.get("mana_cost", 0.0))
		if not _charge_mana(mana_cost, ctx, infinite, heat_mult):
			break  # 蓝不足且非 infinite → 停施
		if mana_cost > 0.0:
			cast_fired = true
		match node.type:
			SpellNode.SpellType.ACTION:
				_push_projectile(node, ctx, spawn_pos, -1)
				actions_remaining -= 1
				if actions_remaining <= 0:
					break
			SpellNode.SpellType.MODIFIER:
				_apply_modifier(node, ctx)
				actions_remaining = 1 + ctx.stats.multicast_count
			SpellNode.SpellType.TRIGGER:
				_push_trigger(node, ctx, spawn_pos)
				actions_remaining -= 1
				if actions_remaining <= 0:
					break
			SpellNode.SpellType.LOGIC:
				if node.meta.has("fork_branch_ids"):
					for bid in node.meta["fork_branch_ids"]:
						_run_branch_payload(int(bid), ctx, spawn_pos, infinite, heat_mult)
				elif String(node.meta.get("logic_op", "")) == "loop":
					ops_count = _run_logic_loop(node, ctx, spawn_pos, deck, ops_count, max_ops, infinite, heat_mult)
					break
				elif _eval_logic(node, ctx, spawn_pos) == _LOGIC_SKIP_REST:
					break
```

- [ ] **Step 3: execute_compiled — 施法末结算 HP + heat**

在 while 循环之后、`if ops_count >= max_ops:` 之前插入：
```gdscript
	if ctx.hp_cost_accum > 0.0:
		PlayerStats.spend_hp_cost(minf(ctx.hp_cost_accum, PlayerStats.hp_max * SettingsManager.hp_cost_cap_ratio()))
	if cast_fired:
		PlayerStats.mana_heat = minf(SettingsManager.mana_heat_max(), PlayerStats.mana_heat + SettingsManager.mana_heat_per_cast())
```

- [ ] **Step 4: _apply_modifier — heavy_cost 累加**

在 `_apply_modifier` 末尾 `if meta.has("multicast"): ...` 之后加：
```gdscript
	if meta.has("heavy_cost"):
		ctx.hp_cost_accum += float(meta["heavy_cost"])
```

- [ ] **Step 5: _run_logic_loop — 加参数 + 改门控**

签名改为：
```gdscript
func _run_logic_loop(node: SpellNode, ctx: SpellContext, spawn_pos: Vector2,
		deck: SpellDeck, ops_count: int, max_ops: int, infinite: bool, heat_mult: float) -> int:
```
把内部门控（`var n_cost ...` / `if n_cost > 0.0 and not PlayerStats.spend_mana(n_cost): return ops_count`）替换为：
```gdscript
			var n_cost: float = float(n.meta.get("mana_cost", 0.0))
			if not _charge_mana(n_cost, ctx, infinite, heat_mult):
				return ops_count
```

- [ ] **Step 6: _run_branch_payload — 加参数 + 改门控 + 递归传参**

签名改为：
```gdscript
func _run_branch_payload(payload_id: int, ctx: SpellContext, spawn_pos: Vector2, infinite: bool, heat_mult: float) -> void:
```
门控替换为：
```gdscript
		var node_cost: float = float(node.meta.get("mana_cost", 0.0))
		if not _charge_mana(node_cost, ctx, infinite, heat_mult):
			break
```
LOGIC 分支里的递归调用 `_run_branch_payload(int(bid), ctx, spawn_pos)` 改为 `_run_branch_payload(int(bid), ctx, spawn_pos, infinite, heat_mult)`。
> 不要改 `execute_sub`（on-hit 子荷载）——它保持不扣蓝。

- [ ] **Step 7: 验证（MCP）**
```gdscript
var core = WandPreset.make_core_by_id("wand_basic")
# 热值乘算：heat=0.5 时 spark(5) 实扣 7.5
PlayerStats.set_mana_pool(100.0, 5.0); PlayerStats.mana = 100.0; PlayerStats.mana_heat = 0.5
BulletManager.reset()
SpellEvaluator.execute_compiled(SpellEvaluator.compile_wand(core, [SpellRegistry.get_spell("action_spark_bolt")]), 0, Vector2(0,0))
_mcp_print("[heat] mana=%s (expect 92.5=100-7.5), bullets=%d(1)" % [str(PlayerStats.mana), BulletManager.get_active_count()])
# 堆热：cast_fired 后 heat 上升
_mcp_print("[heat] mana_heat=%s (expect 0.51=0.5+0.01)" % str(PlayerStats.mana_heat))
# 回归：heat=0 行为不变
PlayerStats.mana_heat = 0.0; PlayerStats.mana = 100.0; BulletManager.reset()
SpellEvaluator.execute_compiled(SpellEvaluator.compile_wand(core, [SpellRegistry.get_spell("modifier_damage_plus"), SpellRegistry.get_spell("action_spark_bolt")]), 0, Vector2(0,0))
_mcp_print("[regr] mana=%s(80) bullets=%d(1)" % [str(PlayerStats.mana), BulletManager.get_active_count()])
```
Expected: mana≈92.5/1, heat 0.51, 回归 80/1。`get_editor_errors` 运行时无。

- [ ] **Step 8: Commit**
```bash
git add scripts/domain/spell_system/spell_evaluator.gd
git commit -m "feat(mana): _charge_mana 统一门控（热值乘算+infinite扣血）+heavy_cost+HP结算"
```

---

### Task 5: 魔力虹吸运行时（CoreDefinition + WandPreset + combat_manager 重算）

**Files:** `scripts/domain/spell_system/core_definition.gd`, `scripts/autoloads/wand_preset.gd`, `scripts/domain/combat/combat_manager.gd`

- [ ] **Step 1: CoreDefinition 加字段**

`core_definition.gd` 的 `@export var base_mana_regen: float = 5.0` 之后加：
```gdscript
@export var base_mana_leech: float = 0.0    # 击杀回蓝基础值（可叠加卡组内 meta.mana_leech）
```

- [ ] **Step 2: WandPreset 读取**

`wand_preset.gd` `_core_from_dict` 里 `c.base_mana_regen = float(d.get("mana_regen", 5.0))` 之后加：
```gdscript
	c.base_mana_leech = float(d.get("mana_leech", 0.0))
```

- [ ] **Step 3: combat_manager 重算 leech**

`combat_manager.gd` `_rebuild_wand()` 里，构建 `nodes` 之后、`_equipped_compiled = SpellEvaluator.compile_wand(...)` 之前（或函数末尾）加：
```gdscript
	var leech: float = _equipped_core.base_mana_leech if _equipped_core else 0.0
	for n in nodes:
		if n != null:
			leech += float(n.meta.get("mana_leech", 0.0))
	PlayerStats.mana_leech = leech
```

- [ ] **Step 4: 验证（MCP）**
```gdscript
var core = WandPreset.make_core_by_id("wand_basic")
core.base_mana_leech = 2.0
# 模拟 _rebuild_wand 的求和逻辑
var nodes = [SpellRegistry.get_spell("action_spark_bolt")]
var leech = core.base_mana_leech
for n in nodes:
	leech += float(n.meta.get("mana_leech", 0.0))
_mcp_print("core leech 2 + spark(0) = %s (expect 2.0)" % str(leech))
_mcp_print("wand_basic 默认 base_mana_leech=%s (expect 0)" % str(WandPreset.make_core_by_id("wand_basic").base_mana_leech))
```
Expected: 2.0 / 0.0。（求和 + 击杀回蓝的端到端在 Task 10 战斗内验。）

- [ ] **Step 5: Commit**
```bash
git add scripts/domain/spell_system/core_definition.gd scripts/autoloads/wand_preset.gd scripts/domain/combat/combat_manager.gd
git commit -m "feat(mana): 魔力虹吸多来源（Core base + 卡组 meta.mana_leech 求和，换杖重算）"
```

---

### Task 6: 数据内容（cores.json wand_eternal / spells.json heavy_cost·mana_leech）

**Files:** `data/cores.json`, `data/spells.json`

- [ ] **Step 1: cores.json 加 wand_eternal + 续航 Core leech**

在 `data/cores.json` 的 `circuit_fork` 条目之后（末尾 `}` 前，补逗号）加：
```json
  "wand_eternal": { "display_name": "永恒法杖", "slot_count": 8, "topology": 0, "cpu_limit": 6, "cast_interval": 0.5, "feature_tags": 5, "mana_max": 40, "mana_regen": 4, "mana_leech": 2 }
```
（`feature_tags: 5` = INFINITE_SPELLS；低蓝逼卖血；自带 base_mana_leech 2 作续航向。其余 Core 不加 mana_leech = 默认 0。）

- [ ] **Step 2: spells.json 加 heavy_cost + mana_leech 修正器**

在 `data/spells.json` 的 `modifier_pierce_plus` 条目之后加两条（补逗号，JSON 合法）：
```json
  "modifier_heavy_cost":  { "type": 1, "display_name": "Blood Sacrifice", "description": "鲜血献祭：伤害+50，但每次施法扣 10 HP。", "element_tags": [], "meta": { "damage_add": 50.0, "heavy_cost": 10, "mana_cost": 0, "shop_cost": 26 } },
  "modifier_mana_leech":  { "type": 1, "display_name": "Mana Leech",      "description": "魔力虹吸：击杀回复 2 魔力（放入卡组即生效）。", "element_tags": [], "meta": { "mana_leech": 2, "mana_cost": 0, "shop_cost": 22 } }
```

- [ ] **Step 3: 验证（MCP）**
```gdscript
var e = WandPreset.make_core_by_id("wand_eternal")
_mcp_print("wand_eternal feat=%d(5) mana_max=%s(40) leech=%s(2)" % [e.feature_tags, str(e.base_mana_max), str(e.base_mana_leech)])
var h = SpellRegistry.get_spell("modifier_heavy_cost")
var l = SpellRegistry.get_spell("modifier_mana_leech")
_mcp_print("heavy_cost meta.heavy_cost=%s(10) dmg=%s(50) | mana_leech meta.mana_leech=%s(2)" % [str(h.meta.get("heavy_cost")), str(h.meta.get("damage_add")), str(l.meta.get("mana_leech"))])
```
Expected: feat=5/40/2；heavy 10/50；leech 2。`get_editor_errors` 运行时无（JSON 合法）。

- [ ] **Step 4: Commit**
```bash
git add data/cores.json data/spells.json
git commit -m "feat(mana): wand_eternal(infinite)+modifier_heavy_cost/mana_leech 数据"
```

---

### Task 7: Core 编辑器加 mana_leech 栏（编辑器可调）

**Files:** `addons/game_designer/core_tab.gd`

- [ ] **Step 1: 加成员 + 表单栏 + 读写**

（a）成员区 `var _mana_regen: SpinBox` 之后加：
```gdscript
var _mana_leech: SpinBox
```
（b）`_ready()` 里 `_mana_regen = _row("回蓝/秒(Mana Regen)", UI.spin(0, 100, 0.5, 5))` 之后加：
```gdscript
	_mana_leech = _row("击杀回蓝(Mana Leech)", UI.spin(0, 100, 0.5, 0))
```
（c）`_on_select()` 里 `_mana_regen.value = float(d.get("mana_regen", 5))` 之后加：
```gdscript
	_mana_leech.value = float(d.get("mana_leech", 0))
```
（d）`_on_apply()` 的字典里，`"mana_max": int(_mana_max.value), "mana_regen": _mana_regen.value,` 之后加：
```gdscript
		"mana_leech": _mana_leech.value,
```
（e）`_on_new()` 的默认字典里 `"mana_max": 100, "mana_regen": 5,` 之后加 `"mana_leech": 0,`。

- [ ] **Step 2: 验证（编辑器 MCP fresh-load 往返）**
```gdscript
var Tab = ResourceLoader.load("res://addons/game_designer/core_tab.gd", "GDScript", ResourceLoader.CACHE_MODE_IGNORE)
var t = Tab.new(); EditorInterface.get_base_control().add_child(t)
for i in t._list.item_count:
	if String(t._list.get_item_metadata(i)) == "wand_eternal":
		t._on_select(i); break
_mcp_print("wand_eternal 表单 leech=%s (expect 2)" % str(t._mana_leech.value))
t._on_apply()
_mcp_print("apply 后 data.mana_leech=%s (expect 2 保留)" % str(t._data.get("wand_eternal", {}).get("mana_leech", "DROP")))
t.free()
```
Expected: 2 / 2。`get_editor_errors` 清 stale 后 0。

- [ ] **Step 3: Commit**
```bash
git add addons/game_designer/core_tab.gd
git commit -m "feat(mana): Core 编辑器加击杀回蓝(mana_leech)栏"
```

---

### Task 8: HUD 打磨（蓝条数字 + 低蓝 + 过热）+ UI_MANA i18n

**Files:** `scenes/main/combat_s2.gd`, `translations/*.po`

- [ ] **Step 1: i18n UI_MANA（4 语言末尾追加）**

zh_CN: `msgid "UI_MANA"` / `msgstr "蓝 %d/%d"`；zh_TW: `藍 %d/%d`；en: `MP %d/%d`；ja: `MP %d/%d`。（各文件加一条，前空行分隔。）

- [ ] **Step 2: 蓝条叠数字 Label**

`combat_s2.gd` 成员区 `var _mana_bar: ProgressBar = null` 之后加 `var _mana_lbl: Label = null`。
`_setup_hud()` 里创建 `_mana_bar` 并 `add_child` 之后加：
```gdscript
	_mana_lbl = Label.new()
	_mana_lbl.position = Vector2(200, 86)
	_mana_lbl.size = Vector2(170, 16)
	_mana_lbl.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
	_mana_lbl.add_theme_font_size_override("font_size", 11)
	_hud_layer.add_child(_mana_lbl)
```

- [ ] **Step 3: _refresh_hud 更新数字 + 低蓝 + 过热**

`_refresh_hud()` 里现有 `if _mana_bar: _mana_bar.max_value = ...; _mana_bar.value = ...` 块替换为：
```gdscript
	if _mana_bar:
		_mana_bar.max_value = PlayerStats.mana_max
		_mana_bar.value = PlayerStats.mana
		var frac: float = PlayerStats.mana / maxf(1.0, PlayerStats.mana_max)
		var fill := _mana_bar.get_theme_stylebox("fill") as StyleBoxFlat
		if fill:
			if frac < 0.2:
				var a: float = 0.6 + 0.4 * absf(sin(float(Time.get_ticks_msec()) / 150.0))
				fill.bg_color = Color(0.8, 0.2, 0.2, a)   # 低蓝暗红闪烁
			else:
				fill.bg_color = Color(0.3, 0.55, 1.0)     # 正常蓝
		# 过热：mana_heat>0 时整条泛红
		_mana_bar.modulate = Color(1.0, 1.0 - clampf(PlayerStats.mana_heat, 0.0, 0.7), 1.0 - clampf(PlayerStats.mana_heat, 0.0, 0.7))
	if _mana_lbl:
		_mana_lbl.text = tr("UI_MANA") % [int(PlayerStats.mana), int(PlayerStats.mana_max)]
```

- [ ] **Step 4: 验证（MCP，进战斗 + 截图）**

`play_scene(main)`→`SceneManager.start_new_game()`；下一调用：
```gdscript
var cs = get_tree().current_scene
_mcp_print("mana_lbl exists=%s" % str(cs._mana_lbl != null))
PlayerStats.mana = PlayerStats.mana_max * 0.1  # 低蓝
PlayerStats.mana_heat = 0.5                    # 过热
_mcp_print("set low mana + heat; label=%s" % cs._mana_lbl.text)
```
`get_game_screenshot` 确认蓝条有数字、低蓝暗红、泛红。Expected: label 类似 "蓝 10/100"。

- [ ] **Step 5: Commit**
```bash
git add scenes/main/combat_s2.gd translations/zh_CN.po translations/zh_TW.po translations/en.po translations/ja.po
git commit -m "feat(mana): HUD 蓝条数字+低蓝暗红+过热泛红 + UI_MANA 键"
```

---

### Task 9: 平衡调数值（基础杖续航）

**Files:** `data/cores.json`, `data/spells.json`

- [ ] **Step 1: 提高基础回蓝 + 降 spark 蓝耗**

`data/cores.json`：`wand_basic` 的 `"mana_regen": 5` 改为 `"mana_regen": 8`；`wand_memory`/`matrix_board`/`circuit_fork` 的 `mana_regen` 相应 +3（保持相对关系：memory 5→8、matrix 6→9、circuit 5→8；wand_fast 4→6）。
`data/spells.json`：`action_spark_bolt` 的 `"mana_cost": 5` 改为 `"mana_cost": 3`。

- [ ] **Step 2: 验证（MCP 手感 + 数据）**
```gdscript
_mcp_print("basic regen=%s(8) spark cost=%s(3)" % [str(WandPreset.make_core_by_id("wand_basic").base_mana_regen), str(SpellRegistry.get_spell("action_spark_bolt").meta.get("mana_cost"))])
```
起战斗观测：基础杖只放 spark 时蓝条应稳定不见底（cost3×2/s=6 vs regen8 → 净回）；连打贵法术/堆射速仍会憋蓝。用 `get_game_screenshot` 记录蓝条状态。Expected: regen=8 / cost=3；战斗中基础突突不空蓝。

- [ ] **Step 3: Commit**
```bash
git add data/cores.json data/spells.json
git commit -m "balance(mana): 提高基础回蓝(→8)+降 spark 蓝耗(→3)，基础杖能持续突突"
```

---

### Task 10: 更新文档 callout

**Files:** `docs/design/game_design.md`, `docs/design/numerical_design.md`

- [ ] **Step 1: game_design §4.1 callout**

把 §4.1 现有 Mana callout 的「暂缺」句改为反映已实现：将其中 `**暂缺**：递增蓝耗、\`infinite_spells\` 扣血、全局+法杖取 Min、cast_delay。` 替换为 `**已补全**：递增蓝耗、\`infinite_spells\` 扣血、魔力虹吸（多来源）、HUD 打磨。**仍暂缺**：全局+法杖取 Min、cast_delay。`。

- [ ] **Step 2: numerical_design §5.A callout**

在 §5.A「递增蓝耗」条目末尾加一行：
```markdown
> **⚠️ 实现现状 (2026-07-21 → 已实现)**：递增蓝耗已落地（`PlayerStats.mana_heat`，`balance.json.mana_heat` 可调，平衡页签）；未做「最小帧限制/过热强制冷却」（用递增蓝耗替代）。
```

- [ ] **Step 3: Commit**
```bash
git add docs/design/game_design.md docs/design/numerical_design.md
git commit -m "docs(mana): §4.1/§5.A 现状更新为 Mana 完整化已实现"
```

---

### Task 11: 全量回归验收

**Files:** 无（纯验证）

- [ ] **Step 1: 重载 + 0 错误**：`clear_output`→`reload_project`→`get_editor_errors`=0。

- [ ] **Step 2: 各机制 + 端到端**（`play_scene(main)`）：
```gdscript
var core = WandPreset.make_core_by_id("wand_basic")
# 递增蓝耗 + 回归
PlayerStats.set_mana_pool(100,8); PlayerStats.mana=100; PlayerStats.mana_heat=0.5
BulletManager.reset(); SpellEvaluator.execute_compiled(SpellEvaluator.compile_wand(core,[SpellRegistry.get_spell("action_spark_bolt")]),0,Vector2(0,0))
_mcp_print("[heat] mana=%s (expect 95.5 = 100 - spark3 * heat1.5) bullets=%d(1)" % [str(PlayerStats.mana), BulletManager.get_active_count()])
# infinite 扣血
var e = WandPreset.make_core_by_id("wand_eternal"); PlayerStats.set_mana_pool(e.base_mana_max, e.base_mana_regen); PlayerStats.mana=0; PlayerStats.hp=100; PlayerStats.hp_max=100; PlayerStats.mana_heat=0
BulletManager.reset(); SpellEvaluator.execute_compiled(SpellEvaluator.compile_wand(e,[SpellRegistry.get_spell("action_spark_bolt")]),0,Vector2(0,0))
_mcp_print("[inf] 蓝空施法 hp=%s(<100 扣血) bullets=%d(1)" % [str(PlayerStats.hp), BulletManager.get_active_count()])
# heavy_cost + 15%上限
PlayerStats.hp=100; PlayerStats.mana=100; PlayerStats.mana_heat=0
BulletManager.reset(); SpellEvaluator.execute_compiled(SpellEvaluator.compile_wand(core,[SpellRegistry.get_spell("modifier_heavy_cost"),SpellRegistry.get_spell("action_spark_bolt")]),0,Vector2(0,0))
_mcp_print("[heavy] hp=%s(90=100-10) bullets=%d(1)" % [str(PlayerStats.hp), BulletManager.get_active_count()])
# 持久内存回归
var mc = WandPreset.make_core_by_id("wand_memory"); var cA=SpellEvaluator.compile_wand(mc,[SpellRegistry.get_spell("logic_every_n_shots"),SpellRegistry.get_spell("action_spark_bolt")])
var r=[]; PlayerStats.mana_heat=0
for i in 4:
	PlayerStats.mana=100; BulletManager.reset(); SpellEvaluator.execute_compiled(cA,0,Vector2(0,0)); r.append(BulletManager.get_active_count())
_mcp_print("[regr] every_n=%s (expect [0,0,1,0])" % str(r))
```
Expected: 各机制符合注释；回归 [0,0,1,0]。

- [ ] **Step 3: 击杀回蓝 + UI（战斗内）**：`start_new_game`，装 wand_eternal 或塞 mana_leech 卡，杀怪观察 mana 回升；截图 HUD。

- [ ] **Step 4: DoD 核对**（spec §10）。`stop_scene`。

- [ ] **Step 5: Commit**
```bash
git commit --allow-empty -m "chore(mana): 完整化验收通过（Godot 4.6 运行时实测各机制+回归全绿）"
```

---

## 自检结论
- **Spec 覆盖**：P1→T1/T2/T3/T4；P2→T1/T2/T4/T6；P3→T2/T5/T6/T7；P4→T8；P5→T9；docs→T10；测试→各任务+T11。全覆盖。
- **无占位**：每步精确文件/完整代码/期望值。
- **类型一致**：`PlayerStats.mana_heat/mana_leech`+`gain_mana/spend_hp_cost`、`SpellContext.hp_cost_accum`、`SpellEvaluator._charge_mana(base_cost,ctx,infinite,heat_mult)`+`_run_logic_loop/_run_branch_payload` 加 `infinite,heat_mult`、`CoreDefinition.base_mana_leech`、`SettingsManager.mana_heat_*/infinite_*/hp_cost_cap_ratio`、`balance.json.mana_heat/infinite_spells`、`cores.json.mana_leech`、`spells.json meta.heavy_cost/mana_leech` 全程一致。
