From 6b60f6e07367ab10cf42aae3d94dbc360982b899 Mon Sep 17 00:00:00 2001 From: Joywayer Date: Tue, 21 Jul 2026 15:15:07 +0800 Subject: [PATCH] =?UTF-8?q?feat(mana):=20=5Fcharge=5Fmana=20=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E9=97=A8=E6=8E=A7=EF=BC=88=E7=83=AD=E5=80=BC=E4=B9=98?= =?UTF-8?q?=E7=AE=97+infinite=E6=89=A3=E8=A1=80=EF=BC=89+heavy=5Fcost+HP?= =?UTF-8?q?=E7=BB=93=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/spell_system/spell_evaluator.gd | 50 +++++++++++++------ 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/scripts/domain/spell_system/spell_evaluator.gd b/scripts/domain/spell_system/spell_evaluator.gd index 5cc10c2..e2376b3 100644 --- a/scripts/domain/spell_system/spell_evaluator.gd +++ b/scripts/domain/spell_system/spell_evaluator.gd @@ -323,6 +323,10 @@ func execute_compiled(compiled: CompiledDeck, caster_id: int, spawn_pos: Vector2 return var persistent: bool = compiled.feature_tags == CoreFeatureTag.PERSISTENT_MEMORY 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 @@ -331,8 +335,10 @@ func execute_compiled(compiled: CompiledDeck, caster_id: int, spawn_pos: Vector2 ops_count += 1 var node: SpellNode = deck.pop() var mana_cost: float = float(node.meta.get("mana_cost", 0.0)) - if mana_cost > 0.0 and not PlayerStats.spend_mana(mana_cost): - break # 蓝不足 → 停止本次施法(此节点不执行、剩余跳过) + 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) @@ -341,7 +347,6 @@ func execute_compiled(compiled: CompiledDeck, caster_id: int, spawn_pos: Vector2 break SpellNode.SpellType.MODIFIER: _apply_modifier(node, ctx) - # multicast_count 已在 _apply_modifier 内更新 actions_remaining = 1 + ctx.stats.multicast_count SpellNode.SpellType.TRIGGER: _push_trigger(node, ctx, spawn_pos) @@ -350,14 +355,17 @@ func execute_compiled(compiled: CompiledDeck, caster_id: int, spawn_pos: Vector2 break SpellNode.SpellType.LOGIC: if node.meta.has("fork_branch_ids"): - # CIRCUIT 分叉:各分支在施法点立即并行执行 for bid in node.meta["fork_branch_ids"]: - _run_branch_payload(int(bid), ctx, spawn_pos) + _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) + ops_count = _run_logic_loop(node, ctx, spawn_pos, deck, ops_count, max_ops, infinite, heat_mult) break # LOOP 已消耗剩余节点 elif _eval_logic(node, ctx, spawn_pos) == _LOGIC_SKIP_REST: - break # 条件不成立 → 跳过后续法术 + 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: + 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) _release_ctx(ctx, persistent) @@ -487,6 +495,18 @@ func _push_trigger(node: SpellNode, ctx: SpellContext, spawn_pos: Vector2, trigg 0, 0.0, cold ) +## 统一扣蓝: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 + ## MODIFIER 分支 func _apply_modifier(node: SpellNode, ctx: SpellContext) -> void: var meta: Dictionary = node.meta @@ -504,6 +524,8 @@ func _apply_modifier(node: SpellNode, ctx: SpellContext) -> void: ctx.stats.multicast_count += int(meta["multicast"]) if meta.has("pierce"): ctx.stats.pierce_add += int(meta["pierce"]) + if meta.has("heavy_cost"): + ctx.hp_cost_accum += float(meta["heavy_cost"]) ## ── 上下文获取(区分跨帧持久 / 池化)───────────────────────── func _acquire_ctx(caster_id: int, persistent: bool) -> SpellContext: @@ -553,7 +575,7 @@ func _eval_logic(node: SpellNode, ctx: SpellContext, spawn_pos: Vector2) -> int: ## 简化实现:LOOP 体内不再处理嵌套 LOGIC(顺延后续迭代);尊重 ops 预算 ## 返回更新后的 ops_count func _run_logic_loop(node: SpellNode, ctx: SpellContext, spawn_pos: Vector2, - deck: SpellDeck, ops_count: int, max_ops: int) -> int: + deck: SpellDeck, ops_count: int, max_ops: int, infinite: bool, heat_mult: float) -> int: var count: int = clampi(int(node.meta.get("count", 2)), 1, 16) var body: Array = [] while deck.has_next(): @@ -564,8 +586,8 @@ func _run_logic_loop(node: SpellNode, ctx: SpellContext, spawn_pos: Vector2, if ops_count >= max_ops: return ops_count var n_cost: float = float(n.meta.get("mana_cost", 0.0)) - if n_cost > 0.0 and not PlayerStats.spend_mana(n_cost): - return ops_count # 蓝不足 → 停止整个 LOOP + if not _charge_mana(n_cost, ctx, infinite, heat_mult): + return ops_count match n.type: SpellNode.SpellType.ACTION: _push_projectile(n, ctx, spawn_pos, -1) @@ -579,7 +601,7 @@ func _run_logic_loop(node: SpellNode, ctx: SpellContext, spawn_pos: Vector2, ## ── CIRCUIT 分支执行(LOGIC_FORK 触发)────────────────────── ## 在施法点立即执行某分支 SubPayload 的节点;分支内 MODIFIER 作用域隔离(不泄漏给兄弟分支/主链) ## 嵌套 LOGIC_FORK 递归展开(P6-N66) -func _run_branch_payload(payload_id: int, ctx: SpellContext, spawn_pos: Vector2) -> void: +func _run_branch_payload(payload_id: int, ctx: SpellContext, spawn_pos: Vector2, infinite: bool, heat_mult: float) -> void: var nodes: Array = SubPayloadRegistry.get_nodes(payload_id) if nodes.is_empty(): return @@ -595,8 +617,8 @@ func _run_branch_payload(payload_id: int, ctx: SpellContext, spawn_pos: Vector2) var b_crit: float = s.crit_chance for node in nodes: var node_cost: float = float(node.meta.get("mana_cost", 0.0)) - if node_cost > 0.0 and not PlayerStats.spend_mana(node_cost): - break # 蓝不足 → 停止本分支 + if not _charge_mana(node_cost, ctx, infinite, heat_mult): + break match node.type: SpellNode.SpellType.ACTION: _push_projectile(node, ctx, spawn_pos, -1) @@ -607,7 +629,7 @@ func _run_branch_payload(payload_id: int, ctx: SpellContext, spawn_pos: Vector2) 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) + _run_branch_payload(int(bid), ctx, spawn_pos, infinite, heat_mult) elif _eval_logic(node, ctx, spawn_pos) == _LOGIC_SKIP_REST: break # 还原 stats