feat(mana): _charge_mana 统一门控(热值乘算+infinite扣血)+heavy_cost+HP结算
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user