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
|
return
|
||||||
var persistent: bool = compiled.feature_tags == CoreFeatureTag.PERSISTENT_MEMORY
|
var persistent: bool = compiled.feature_tags == CoreFeatureTag.PERSISTENT_MEMORY
|
||||||
var ctx: SpellContext = _acquire_ctx(caster_id, persistent)
|
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 deck: SpellDeck = compiled.make_runtime_deck()
|
||||||
var ops_count: int = 0
|
var ops_count: int = 0
|
||||||
var max_ops: int = MAX_OPS_PER_CPU * 5
|
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
|
ops_count += 1
|
||||||
var node: SpellNode = deck.pop()
|
var node: SpellNode = deck.pop()
|
||||||
var mana_cost: float = float(node.meta.get("mana_cost", 0.0))
|
var mana_cost: float = float(node.meta.get("mana_cost", 0.0))
|
||||||
if mana_cost > 0.0 and not PlayerStats.spend_mana(mana_cost):
|
if not _charge_mana(mana_cost, ctx, infinite, heat_mult):
|
||||||
break # 蓝不足 → 停止本次施法(此节点不执行、剩余跳过)
|
break # 蓝不足且非 infinite → 停施
|
||||||
|
if mana_cost > 0.0:
|
||||||
|
cast_fired = true
|
||||||
match node.type:
|
match node.type:
|
||||||
SpellNode.SpellType.ACTION:
|
SpellNode.SpellType.ACTION:
|
||||||
_push_projectile(node, ctx, spawn_pos, -1)
|
_push_projectile(node, ctx, spawn_pos, -1)
|
||||||
@@ -341,7 +347,6 @@ func execute_compiled(compiled: CompiledDeck, caster_id: int, spawn_pos: Vector2
|
|||||||
break
|
break
|
||||||
SpellNode.SpellType.MODIFIER:
|
SpellNode.SpellType.MODIFIER:
|
||||||
_apply_modifier(node, ctx)
|
_apply_modifier(node, ctx)
|
||||||
# multicast_count 已在 _apply_modifier 内更新
|
|
||||||
actions_remaining = 1 + ctx.stats.multicast_count
|
actions_remaining = 1 + ctx.stats.multicast_count
|
||||||
SpellNode.SpellType.TRIGGER:
|
SpellNode.SpellType.TRIGGER:
|
||||||
_push_trigger(node, ctx, spawn_pos)
|
_push_trigger(node, ctx, spawn_pos)
|
||||||
@@ -350,14 +355,17 @@ func execute_compiled(compiled: CompiledDeck, caster_id: int, spawn_pos: Vector2
|
|||||||
break
|
break
|
||||||
SpellNode.SpellType.LOGIC:
|
SpellNode.SpellType.LOGIC:
|
||||||
if node.meta.has("fork_branch_ids"):
|
if node.meta.has("fork_branch_ids"):
|
||||||
# CIRCUIT 分叉:各分支在施法点立即并行执行
|
|
||||||
for bid in node.meta["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 String(node.meta.get("logic_op", "")) == "loop":
|
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 已消耗剩余节点
|
break # LOOP 已消耗剩余节点
|
||||||
elif _eval_logic(node, ctx, spawn_pos) == _LOGIC_SKIP_REST:
|
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:
|
if ops_count >= max_ops:
|
||||||
push_warning("SpellEvaluator: MAX_OPS reached (caster=%d)" % caster_id)
|
push_warning("SpellEvaluator: MAX_OPS reached (caster=%d)" % caster_id)
|
||||||
_release_ctx(ctx, persistent)
|
_release_ctx(ctx, persistent)
|
||||||
@@ -487,6 +495,18 @@ func _push_trigger(node: SpellNode, ctx: SpellContext, spawn_pos: Vector2, trigg
|
|||||||
0, 0.0, cold
|
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 分支
|
## MODIFIER 分支
|
||||||
func _apply_modifier(node: SpellNode, ctx: SpellContext) -> void:
|
func _apply_modifier(node: SpellNode, ctx: SpellContext) -> void:
|
||||||
var meta: Dictionary = node.meta
|
var meta: Dictionary = node.meta
|
||||||
@@ -504,6 +524,8 @@ func _apply_modifier(node: SpellNode, ctx: SpellContext) -> void:
|
|||||||
ctx.stats.multicast_count += int(meta["multicast"])
|
ctx.stats.multicast_count += int(meta["multicast"])
|
||||||
if meta.has("pierce"):
|
if meta.has("pierce"):
|
||||||
ctx.stats.pierce_add += int(meta["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:
|
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 预算
|
## 简化实现:LOOP 体内不再处理嵌套 LOGIC(顺延后续迭代);尊重 ops 预算
|
||||||
## 返回更新后的 ops_count
|
## 返回更新后的 ops_count
|
||||||
func _run_logic_loop(node: SpellNode, ctx: SpellContext, spawn_pos: Vector2,
|
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 count: int = clampi(int(node.meta.get("count", 2)), 1, 16)
|
||||||
var body: Array = []
|
var body: Array = []
|
||||||
while deck.has_next():
|
while deck.has_next():
|
||||||
@@ -564,8 +586,8 @@ func _run_logic_loop(node: SpellNode, ctx: SpellContext, spawn_pos: Vector2,
|
|||||||
if ops_count >= max_ops:
|
if ops_count >= max_ops:
|
||||||
return ops_count
|
return ops_count
|
||||||
var n_cost: float = float(n.meta.get("mana_cost", 0.0))
|
var n_cost: float = float(n.meta.get("mana_cost", 0.0))
|
||||||
if n_cost > 0.0 and not PlayerStats.spend_mana(n_cost):
|
if not _charge_mana(n_cost, ctx, infinite, heat_mult):
|
||||||
return ops_count # 蓝不足 → 停止整个 LOOP
|
return ops_count
|
||||||
match n.type:
|
match n.type:
|
||||||
SpellNode.SpellType.ACTION:
|
SpellNode.SpellType.ACTION:
|
||||||
_push_projectile(n, ctx, spawn_pos, -1)
|
_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 触发)──────────────────────
|
## ── CIRCUIT 分支执行(LOGIC_FORK 触发)──────────────────────
|
||||||
## 在施法点立即执行某分支 SubPayload 的节点;分支内 MODIFIER 作用域隔离(不泄漏给兄弟分支/主链)
|
## 在施法点立即执行某分支 SubPayload 的节点;分支内 MODIFIER 作用域隔离(不泄漏给兄弟分支/主链)
|
||||||
## 嵌套 LOGIC_FORK 递归展开(P6-N66)
|
## 嵌套 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)
|
var nodes: Array = SubPayloadRegistry.get_nodes(payload_id)
|
||||||
if nodes.is_empty():
|
if nodes.is_empty():
|
||||||
return
|
return
|
||||||
@@ -595,8 +617,8 @@ func _run_branch_payload(payload_id: int, ctx: SpellContext, spawn_pos: Vector2)
|
|||||||
var b_crit: float = s.crit_chance
|
var b_crit: float = s.crit_chance
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
var node_cost: float = float(node.meta.get("mana_cost", 0.0))
|
var node_cost: float = float(node.meta.get("mana_cost", 0.0))
|
||||||
if node_cost > 0.0 and not PlayerStats.spend_mana(node_cost):
|
if not _charge_mana(node_cost, ctx, infinite, heat_mult):
|
||||||
break # 蓝不足 → 停止本分支
|
break
|
||||||
match node.type:
|
match node.type:
|
||||||
SpellNode.SpellType.ACTION:
|
SpellNode.SpellType.ACTION:
|
||||||
_push_projectile(node, ctx, spawn_pos, -1)
|
_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:
|
SpellNode.SpellType.LOGIC:
|
||||||
if node.meta.has("fork_branch_ids"):
|
if node.meta.has("fork_branch_ids"):
|
||||||
for bid in node.meta["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:
|
elif _eval_logic(node, ctx, spawn_pos) == _LOGIC_SKIP_REST:
|
||||||
break
|
break
|
||||||
# 还原 stats
|
# 还原 stats
|
||||||
|
|||||||
Reference in New Issue
Block a user