From 4725c57c7271740887e07489f5ee39e1a9f394a6 Mon Sep 17 00:00:00 2001 From: Joywayer Date: Tue, 21 Jul 2026 15:35:11 +0800 Subject: [PATCH] =?UTF-8?q?fix(mana):=20heat=20=E4=BE=9D=E7=9C=9F=E5=AE=9E?= =?UTF-8?q?=E6=89=A3=E8=93=9D(ctx=E6=A0=87=E8=AE=B0)=E8=A6=86=E7=9B=96=20L?= =?UTF-8?q?OOP/CIRCUIT=20=E5=AD=90=E8=B7=AF=E5=BE=84+=E5=8D=96=E8=A1=80?= =?UTF-8?q?=E4=B8=8D=E5=8D=87=E7=83=AD=EF=BC=9B=E6=AD=BB=E4=BA=A1=E9=98=B2?= =?UTF-8?q?=E9=87=8D=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 复审 nit: - heat 之前仅在主循环 cast_fired 记录,LOOP 体/CIRCUIT 分支扣蓝漏记 → 递增蓝耗对这两类拓扑失效。 改为 _charge_mana 真实 spend_mana 成功时置 ctx.mana_spent_this_cast,施法末据此升 heat。 - 顺带修正 infinite 卖血(未真实扣蓝)也升 heat 的问题:卖血不升热。 - combat_manager._on_player_died 加 GAME_OVER 防重入(子弹命中+卖血同帧双死不重复结算)。 MCP 实测:LOOP heat 0.010(修复前0)、卖血 heat 0、线性回归 0.010。 Co-Authored-By: Claude Opus 4.8 (1M context) --- scripts/domain/combat/combat_manager.gd | 2 ++ scripts/domain/spell_system/spell_context.gd | 2 ++ scripts/domain/spell_system/spell_evaluator.gd | 7 +++---- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/scripts/domain/combat/combat_manager.gd b/scripts/domain/combat/combat_manager.gd index ed8e797..09528b3 100644 --- a/scripts/domain/combat/combat_manager.gd +++ b/scripts/domain/combat/combat_manager.gd @@ -228,6 +228,8 @@ func close_shop_and_next_wave() -> void: _transition_to_battle(next_wave) func _on_player_died(_payload: Dictionary) -> void: + if state == GameState.GAME_OVER: + return # 防重入:同帧内多路死亡(子弹命中 + 卖血施法)不重复结算战绩/发以太 state = GameState.GAME_OVER # 先采集本局战绩(reset 前),写入排行榜 var wave_reached: int = max(1, WaveManager.current_wave) diff --git a/scripts/domain/spell_system/spell_context.gd b/scripts/domain/spell_system/spell_context.gd index af78787..4fd599f 100644 --- a/scripts/domain/spell_system/spell_context.gd +++ b/scripts/domain/spell_system/spell_context.gd @@ -10,6 +10,7 @@ var stats: CastStats = null # 本次施法的修改层 ## R1-R4:供 LOGIC_EVERY_N_SHOTS 等指令跨帧计数 var registers: PackedFloat32Array = PackedFloat32Array() var hp_cost_accum: float = 0.0 # 本次施法累积 HP 代价(heavy_cost + infinite),施法末结算 +var mana_spent_this_cast: bool = false # 本次施法是否真实扣过蓝(含 LOOP/CIRCUIT 子路径)→ 决定 heat 上升 func _init() -> void: stats = CastStats.new() @@ -23,6 +24,7 @@ func reset() -> void: else: stats = CastStats.new() hp_cost_accum = 0.0 + mana_spent_this_cast = false # registers 小心:仅当 Core 没有 PERSISTENT_MEMORY Feature 时才清零 # 具体清空逻辑在 SpellContextPool.acquire_for_wand() 中处理 diff --git a/scripts/domain/spell_system/spell_evaluator.gd b/scripts/domain/spell_system/spell_evaluator.gd index e2376b3..4ac3bb2 100644 --- a/scripts/domain/spell_system/spell_evaluator.gd +++ b/scripts/domain/spell_system/spell_evaluator.gd @@ -324,9 +324,9 @@ func execute_compiled(compiled: CompiledDeck, caster_id: int, spawn_pos: Vector2 var persistent: bool = compiled.feature_tags == CoreFeatureTag.PERSISTENT_MEMORY var ctx: SpellContext = _acquire_ctx(caster_id, persistent) ctx.hp_cost_accum = 0.0 + ctx.mana_spent_this_cast = false 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 @@ -337,8 +337,6 @@ func execute_compiled(compiled: CompiledDeck, caster_id: int, spawn_pos: Vector2 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) @@ -364,7 +362,7 @@ func execute_compiled(compiled: CompiledDeck, caster_id: int, spawn_pos: Vector2 break 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: + if ctx.mana_spent_this_cast: PlayerStats.mana_heat = minf(SettingsManager.mana_heat_max(), PlayerStats.mana_heat + SettingsManager.mana_heat_per_cast()) if ops_count >= max_ops: push_warning("SpellEvaluator: MAX_OPS reached (caster=%d)" % caster_id) @@ -501,6 +499,7 @@ func _charge_mana(base_cost: float, ctx: SpellContext, infinite: bool, heat_mult if base_cost <= 0.0: return true if PlayerStats.spend_mana(base_cost * heat_mult): + ctx.mana_spent_this_cast = true # 真实扣蓝才记(infinite 卖血不算)→ heat 只对真实施法上升 return true if infinite: ctx.hp_cost_accum += SettingsManager.infinite_hp_per_shot()