merge(mana): Mana 系统完整化(递增蓝耗+infinite卖血+多来源魔力虹吸+HUD+平衡)
11 任务全绿:balance/PlayerStats/PlayerManager/SpellEvaluator 统一扣蓝门控、 魔力虹吸多来源重算、wand_eternal/heavy_cost/mana_leech 数据、Core 编辑器可调、 HUD 蓝条数字+低蓝+过热、平衡调数值、docs 现状更新、复审 heat 覆盖 LOOP/CIRCUIT+死亡防重入。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,7 @@ var _cpu: SpinBox
|
|||||||
var _interval: SpinBox
|
var _interval: SpinBox
|
||||||
var _mana_max: SpinBox
|
var _mana_max: SpinBox
|
||||||
var _mana_regen: SpinBox
|
var _mana_regen: SpinBox
|
||||||
|
var _mana_leech: SpinBox
|
||||||
var _feat: OptionButton
|
var _feat: OptionButton
|
||||||
var _rows: SpinBox
|
var _rows: SpinBox
|
||||||
var _cols: SpinBox
|
var _cols: SpinBox
|
||||||
@@ -43,6 +44,7 @@ func _ready() -> void:
|
|||||||
_interval = _row("施法间隔(s)", UI.spin(0.05, 5, 0.05, 0.5))
|
_interval = _row("施法间隔(s)", UI.spin(0.05, 5, 0.05, 0.5))
|
||||||
_mana_max = _row("蓝上限(Mana Max)", UI.spin(0, 1000, 5, 100))
|
_mana_max = _row("蓝上限(Mana Max)", UI.spin(0, 1000, 5, 100))
|
||||||
_mana_regen = _row("回蓝/秒(Mana Regen)", UI.spin(0, 100, 0.5, 5))
|
_mana_regen = _row("回蓝/秒(Mana Regen)", UI.spin(0, 100, 0.5, 5))
|
||||||
|
_mana_leech = _row("击杀回蓝(Mana Leech)", UI.spin(0, 100, 0.5, 0))
|
||||||
_feat = _row("特性", UI.opt(FEAT, 0))
|
_feat = _row("特性", UI.opt(FEAT, 0))
|
||||||
_rows = _row("矩阵行(MATRIX)", UI.spin(1, 8, 1, 1))
|
_rows = _row("矩阵行(MATRIX)", UI.spin(1, 8, 1, 1))
|
||||||
_cols = _row("矩阵列(MATRIX)", UI.spin(1, 16, 1, 5))
|
_cols = _row("矩阵列(MATRIX)", UI.spin(1, 16, 1, 5))
|
||||||
@@ -87,6 +89,7 @@ func _on_select(idx: int) -> void:
|
|||||||
_interval.value = float(d.get("cast_interval", 0.5))
|
_interval.value = float(d.get("cast_interval", 0.5))
|
||||||
_mana_max.value = float(d.get("mana_max", 100))
|
_mana_max.value = float(d.get("mana_max", 100))
|
||||||
_mana_regen.value = float(d.get("mana_regen", 5))
|
_mana_regen.value = float(d.get("mana_regen", 5))
|
||||||
|
_mana_leech.value = float(d.get("mana_leech", 0))
|
||||||
_feat.selected = int(d.get("feature_tags", 0))
|
_feat.selected = int(d.get("feature_tags", 0))
|
||||||
_rows.value = float(d.get("grid_rows", 1))
|
_rows.value = float(d.get("grid_rows", 1))
|
||||||
_cols.value = float(d.get("grid_cols", 5))
|
_cols.value = float(d.get("grid_cols", 5))
|
||||||
@@ -106,6 +109,7 @@ func _on_apply() -> void:
|
|||||||
"topology": _topo.selected, "cpu_limit": int(_cpu.value),
|
"topology": _topo.selected, "cpu_limit": int(_cpu.value),
|
||||||
"cast_interval": _interval.value, "feature_tags": _feat.selected,
|
"cast_interval": _interval.value, "feature_tags": _feat.selected,
|
||||||
"mana_max": int(_mana_max.value), "mana_regen": _mana_regen.value,
|
"mana_max": int(_mana_max.value), "mana_regen": _mana_regen.value,
|
||||||
|
"mana_leech": _mana_leech.value,
|
||||||
"grid_rows": int(_rows.value), "grid_cols": int(_cols.value), "edges": edges,
|
"grid_rows": int(_rows.value), "grid_cols": int(_cols.value), "edges": edges,
|
||||||
}
|
}
|
||||||
_cur = cid
|
_cur = cid
|
||||||
@@ -118,7 +122,7 @@ func _on_new() -> void:
|
|||||||
while _data.has(cid): cid = "new_core_%d" % i; i += 1
|
while _data.has(cid): cid = "new_core_%d" % i; i += 1
|
||||||
_data[cid] = {"display_name": cid, "slot_count": 5, "topology": 0,
|
_data[cid] = {"display_name": cid, "slot_count": 5, "topology": 0,
|
||||||
"cpu_limit": 5, "cast_interval": 0.5, "feature_tags": 0,
|
"cpu_limit": 5, "cast_interval": 0.5, "feature_tags": 0,
|
||||||
"mana_max": 100, "mana_regen": 5, "edges": []}
|
"mana_max": 100, "mana_regen": 5, "mana_leech": 0, "edges": []}
|
||||||
_refresh()
|
_refresh()
|
||||||
|
|
||||||
func _on_delete() -> void:
|
func _on_delete() -> void:
|
||||||
|
|||||||
+3
-1
@@ -9,5 +9,7 @@
|
|||||||
"4": 450.0,
|
"4": 450.0,
|
||||||
"5": 1800.0
|
"5": 1800.0
|
||||||
},
|
},
|
||||||
"aether_thresholds": [[1, 0], [5, 3], [10, 8], [15, 14], [20, 20]]
|
"aether_thresholds": [[1, 0], [5, 3], [10, 8], [15, 14], [20, 20]],
|
||||||
|
"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 }
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-5
@@ -1,7 +1,8 @@
|
|||||||
{
|
{
|
||||||
"wand_basic": { "display_name": "小木法杯", "slot_count": 5, "topology": 0, "cpu_limit": 5, "cast_interval": 0.5, "feature_tags": 0, "mana_max": 100, "mana_regen": 5 },
|
"wand_basic": { "display_name": "小木法杯", "slot_count": 5, "topology": 0, "cpu_limit": 5, "cast_interval": 0.5, "feature_tags": 0, "mana_max": 100, "mana_regen": 8 },
|
||||||
"wand_fast": { "display_name": "高速法杯", "slot_count": 3, "topology": 0, "cpu_limit": 3, "cast_interval": 0.25, "feature_tags": 0, "mana_max": 60, "mana_regen": 4 },
|
"wand_fast": { "display_name": "高速法杯", "slot_count": 3, "topology": 0, "cpu_limit": 3, "cast_interval": 0.25, "feature_tags": 0, "mana_max": 60, "mana_regen": 6 },
|
||||||
"wand_memory": { "display_name": "记忆法杖", "slot_count": 6, "topology": 0, "cpu_limit": 6, "cast_interval": 0.5, "feature_tags": 1, "mana_max": 100, "mana_regen": 5 },
|
"wand_memory": { "display_name": "记忆法杖", "slot_count": 6, "topology": 0, "cpu_limit": 6, "cast_interval": 0.5, "feature_tags": 1, "mana_max": 100, "mana_regen": 8 },
|
||||||
"matrix_board": { "display_name": "矩阵板", "slot_count": 8, "topology": 1, "cpu_limit": 8, "cast_interval": 0.5, "feature_tags": 0, "grid_rows": 2, "grid_cols": 4, "mana_max": 120, "mana_regen": 6 },
|
"matrix_board": { "display_name": "矩阵板", "slot_count": 8, "topology": 1, "cpu_limit": 8, "cast_interval": 0.5, "feature_tags": 0, "grid_rows": 2, "grid_cols": 4, "mana_max": 120, "mana_regen": 9 },
|
||||||
"circuit_fork": { "display_name": "分叉回路", "slot_count": 3, "topology": 2, "cpu_limit": 5, "cast_interval": 0.6, "feature_tags": 0, "edges": [{"from": 0, "to": 1}, {"from": 0, "to": 2}], "mana_max": 100, "mana_regen": 5 }
|
"circuit_fork": { "display_name": "分叉回路", "slot_count": 3, "topology": 2, "cpu_limit": 5, "cast_interval": 0.6, "feature_tags": 0, "edges": [{"from": 0, "to": 1}, {"from": 0, "to": 2}], "mana_max": 100, "mana_regen": 8 },
|
||||||
|
"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 }
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"action_spark_bolt": { "type": 0, "display_name": "Spark Bolt", "description": "发射一颗电光飞弹,造成 3 伤害。", "element_tags": [], "meta": { "base_damage": 3.0, "speed": 350.0, "lifetime": 4.0, "radius": 6.0, "damage_type": 0, "mana_cost": 5, "shop_cost": 15 } },
|
"action_spark_bolt": { "type": 0, "display_name": "Spark Bolt", "description": "发射一颗电光飞弹,造成 3 伤害。", "element_tags": [], "meta": { "base_damage": 3.0, "speed": 350.0, "lifetime": 4.0, "radius": 6.0, "damage_type": 0, "mana_cost": 3, "shop_cost": 15 } },
|
||||||
"action_energy_orb": { "type": 0, "display_name": "Energy Orb", "description": "较慢但高伤害的能量球。", "element_tags": [], "meta": { "base_damage": 8.0, "speed": 160.0, "lifetime": 5.0, "radius": 10.0, "damage_type": 0, "mana_cost": 20, "unlock_cost": 4, "shop_cost": 25 } },
|
"action_energy_orb": { "type": 0, "display_name": "Energy Orb", "description": "较慢但高伤害的能量球。", "element_tags": [], "meta": { "base_damage": 8.0, "speed": 160.0, "lifetime": 5.0, "radius": 10.0, "damage_type": 0, "mana_cost": 20, "unlock_cost": 4, "shop_cost": 25 } },
|
||||||
"action_fire_bolt": { "type": 0, "display_name": "Fire Bolt", "description": "火焰飞弹,命中施加燃烧。", "element_tags": ["tag:fire"], "meta": { "base_damage": 4.0, "speed": 320.0, "lifetime": 4.0, "radius": 6.0, "damage_type": 0, "apply_status_id": 1, "mana_cost": 8, "shop_cost": 22 } },
|
"action_fire_bolt": { "type": 0, "display_name": "Fire Bolt", "description": "火焰飞弹,命中施加燃烧。", "element_tags": ["tag:fire"], "meta": { "base_damage": 4.0, "speed": 320.0, "lifetime": 4.0, "radius": 6.0, "damage_type": 0, "apply_status_id": 1, "mana_cost": 8, "shop_cost": 22 } },
|
||||||
"action_poison_dart": { "type": 0, "display_name": "Poison Dart", "description": "毒镖,命中叠加中毒层数。", "element_tags": ["tag:poison"], "meta": { "base_damage": 2.0, "speed": 400.0, "lifetime": 3.0, "radius": 5.0, "damage_type": 0, "apply_status_id": 3, "mana_cost": 6, "shop_cost": 20 } },
|
"action_poison_dart": { "type": 0, "display_name": "Poison Dart", "description": "毒镖,命中叠加中毒层数。", "element_tags": ["tag:poison"], "meta": { "base_damage": 2.0, "speed": 400.0, "lifetime": 3.0, "radius": 5.0, "damage_type": 0, "apply_status_id": 3, "mana_cost": 6, "shop_cost": 20 } },
|
||||||
@@ -14,6 +14,8 @@
|
|||||||
"modifier_spread_mod": { "type": 1, "display_name": "Spread", "description": "后续法术多发一颗子弹。", "element_tags": [], "meta": { "spread_add": 1, "mana_cost": 0, "shop_cost": 18 } },
|
"modifier_spread_mod": { "type": 1, "display_name": "Spread", "description": "后续法术多发一颗子弹。", "element_tags": [], "meta": { "spread_add": 1, "mana_cost": 0, "shop_cost": 18 } },
|
||||||
"modifier_double_cast": { "type": 1, "display_name": "Double Cast", "description": "本次施法额外执行 1 个 ACTION。", "element_tags": [], "meta": { "multicast": 1, "mana_cost": 2, "shop_cost": 25 } },
|
"modifier_double_cast": { "type": 1, "display_name": "Double Cast", "description": "本次施法额外执行 1 个 ACTION。", "element_tags": [], "meta": { "multicast": 1, "mana_cost": 2, "shop_cost": 25 } },
|
||||||
"modifier_pierce_plus": { "type": 1, "display_name": "Pierce +2", "description": "后续法术穿透 +2。", "element_tags": [], "meta": { "pierce": 2, "mana_cost": 8, "shop_cost": 18 } },
|
"modifier_pierce_plus": { "type": 1, "display_name": "Pierce +2", "description": "后续法术穿透 +2。", "element_tags": [], "meta": { "pierce": 2, "mana_cost": 8, "shop_cost": 18 } },
|
||||||
|
"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 } },
|
||||||
"trigger_on_hit": { "type": 2, "display_name": "Trigger: On Hit","description": "命中时在命中点执行后续法术(子母弹)。", "element_tags": [], "meta": { "base_damage": 3.0, "speed": 250.0, "lifetime": 4.0, "radius": 6.0, "damage_type": 0, "mana_cost": 10, "shop_cost": 30 } },
|
"trigger_on_hit": { "type": 2, "display_name": "Trigger: On Hit","description": "命中时在命中点执行后续法术(子母弹)。", "element_tags": [], "meta": { "base_damage": 3.0, "speed": 250.0, "lifetime": 4.0, "radius": 6.0, "damage_type": 0, "mana_cost": 10, "shop_cost": 30 } },
|
||||||
"logic_every_n_shots": { "type": 3, "display_name": "Every 3 Shots","description": "每第 3 次施法才执行后续(需记忆法杖)。", "element_tags": [], "meta": { "logic_op": "every_n_shots", "n": 3, "reg": 0, "mana_cost": 0, "shop_cost": 24 } },
|
"logic_every_n_shots": { "type": 3, "display_name": "Every 3 Shots","description": "每第 3 次施法才执行后续(需记忆法杖)。", "element_tags": [], "meta": { "logic_op": "every_n_shots", "n": 3, "reg": 0, "mana_cost": 0, "shop_cost": 24 } },
|
||||||
"logic_if_hp_below": { "type": 3, "display_name": "If HP < 50%", "description": "仅当生命低于 50% 时执行后续。", "element_tags": [], "meta": { "logic_op": "if_hp_below", "threshold": 0.5, "mana_cost": 0, "shop_cost": 22 } },
|
"logic_if_hp_below": { "type": 3, "display_name": "If HP < 50%", "description": "仅当生命低于 50% 时执行后续。", "element_tags": [], "meta": { "logic_op": "if_hp_below", "threshold": 0.5, "mana_cost": 0, "shop_cost": 22 } },
|
||||||
|
|||||||
@@ -132,7 +132,7 @@
|
|||||||
|
|
||||||
### 4.1 核心资源:魔力 (Mana)
|
### 4.1 核心资源:魔力 (Mana)
|
||||||
|
|
||||||
> **⚠️ 实现现状 (2026-07-20 审计 → MVP 已实现)**:Mana 精实 MVP 已落地——`PlayerStats` 蓝池(`mana`/`mana_max`/`mana_regen`)、`CoreDefinition.base_mana_max/regen`(cores.json)、每法术 `mana_cost`(numerical §2)、`SpellEvaluator` 逐节点扣蓝 + 蓝空停施、HUD 蓝条。**暂缺**:递增蓝耗、`infinite_spells` 扣血、全局+法杖取 Min、cast_delay。详见 `docs_dev/specs/2026-07-20-mana-system-mvp-design.md`。
|
> **⚠️ 实现现状 (2026-07-20 审计 → MVP 已实现)**:Mana 精实 MVP 已落地——`PlayerStats` 蓝池(`mana`/`mana_max`/`mana_regen`)、`CoreDefinition.base_mana_max/regen`(cores.json)、每法术 `mana_cost`(numerical §2)、`SpellEvaluator` 逐节点扣蓝 + 蓝空停施、HUD 蓝条。**已补全**:递增蓝耗、`infinite_spells` 扣血、魔力虹吸(多来源)、HUD 打磨。**仍暂缺**:全局+法杖取 Min、cast_delay。详见 `docs_dev/specs/2026-07-20-mana-system-mvp-design.md`。
|
||||||
|
|
||||||
为了防止无限“加特林核弹”,限制资源是 **魔力**。
|
为了防止无限“加特林核弹”,限制资源是 **魔力**。
|
||||||
* 强力修正器(如伤害+100)会有巨大的 **Delay (施法后摇)** 和 **Mana Cost (蓝耗)**。
|
* 强力修正器(如伤害+100)会有巨大的 **Delay (施法后摇)** 和 **Mana Cost (蓝耗)**。
|
||||||
|
|||||||
@@ -173,6 +173,7 @@ Damage = ((Base + Add) × Mult) × (1 - Res) - Armor
|
|||||||
* **最小帧限制**: 设定射击间隔至少为 1 帧 (0.016s)。
|
* **最小帧限制**: 设定射击间隔至少为 1 帧 (0.016s)。
|
||||||
* **过热机制 (Overheat)**: 连续射击超过 5秒,法杖进入“过热”状态,强制冷却 2秒 或 增加散布。
|
* **过热机制 (Overheat)**: 连续射击超过 5秒,法杖进入“过热”状态,强制冷却 2秒 或 增加散布。
|
||||||
* **递增蓝耗**: 连续快速射击时,每发子弹蓝耗增加 1%,停止射击后快速衰减。
|
* **递增蓝耗**: 连续快速射击时,每发子弹蓝耗增加 1%,停止射击后快速衰减。
|
||||||
|
> **⚠️ 实现现状 (2026-07-21 → 已实现)**:递增蓝耗已落地(`PlayerStats.mana_heat`,`balance.json.mana_heat` 可调,平衡页签);未做「最小帧限制/过热强制冷却」(用递增蓝耗替代)。
|
||||||
|
|
||||||
### B. 显卡杀手 (GPU Killer)
|
### B. 显卡杀手 (GPU Killer)
|
||||||
* **问题**: 玩家构建出 `无限分裂` + `无限粒子`。
|
* **问题**: 玩家构建出 `无限分裂` + `无限粒子`。
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ var _lbl_dps: Label = null
|
|||||||
var _lbl_charge: Label = null
|
var _lbl_charge: Label = null
|
||||||
var _lbl_statuses: Label = null
|
var _lbl_statuses: Label = null
|
||||||
var _mana_bar: ProgressBar = null
|
var _mana_bar: ProgressBar = null
|
||||||
|
var _mana_lbl: Label = null
|
||||||
|
|
||||||
var _dps_poll_timer: float = 0.0
|
var _dps_poll_timer: float = 0.0
|
||||||
|
|
||||||
@@ -243,6 +244,12 @@ func _setup_hud() -> void:
|
|||||||
mana_bg.bg_color = Color(0.12, 0.12, 0.22)
|
mana_bg.bg_color = Color(0.12, 0.12, 0.22)
|
||||||
_mana_bar.add_theme_stylebox_override("background", mana_bg)
|
_mana_bar.add_theme_stylebox_override("background", mana_bg)
|
||||||
_hud_layer.add_child(_mana_bar)
|
_hud_layer.add_child(_mana_bar)
|
||||||
|
_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)
|
||||||
|
|
||||||
_lbl_status = make_label.call(Vector2(20, 620), 20)
|
_lbl_status = make_label.call(Vector2(20, 620), 20)
|
||||||
_lbl_status.modulate = Color.YELLOW
|
_lbl_status.modulate = Color.YELLOW
|
||||||
@@ -341,6 +348,18 @@ func _refresh_hud() -> void:
|
|||||||
if _mana_bar:
|
if _mana_bar:
|
||||||
_mana_bar.max_value = PlayerStats.mana_max
|
_mana_bar.max_value = PlayerStats.mana_max
|
||||||
_mana_bar.value = PlayerStats.mana
|
_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)
|
||||||
|
var h: float = clampf(PlayerStats.mana_heat, 0.0, 0.7)
|
||||||
|
_mana_bar.modulate = Color(1.0, 1.0 - h, 1.0 - h)
|
||||||
|
if _mana_lbl:
|
||||||
|
_mana_lbl.text = tr("UI_MANA") % [int(PlayerStats.mana), int(PlayerStats.mana_max)]
|
||||||
|
|
||||||
func _refresh_shop_ui() -> void:
|
func _refresh_shop_ui() -> void:
|
||||||
for i in _shop_btns.size():
|
for i in _shop_btns.size():
|
||||||
|
|||||||
@@ -26,9 +26,15 @@ var resistance: float = 0.0 # 0.0~1.0
|
|||||||
var mana: float = 100.0
|
var mana: float = 100.0
|
||||||
var mana_max: float = 100.0
|
var mana_max: float = 100.0
|
||||||
var mana_regen: float = 5.0 # 每秒回复
|
var mana_regen: float = 5.0 # 每秒回复
|
||||||
|
var mana_heat: float = 0.0 # 递增蓝耗热值(0=无,1=蓝耗翻倍)
|
||||||
|
var mana_leech: float = 0.0 # 击杀回蓝量(换杖/换牌重算)
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
EventBus.subscribe(EventID.PLAYER_DAMAGED, _on_player_damaged)
|
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)
|
||||||
|
|
||||||
## 升级曲线:roundf(10 × 1.4^(level-1)),P6-N16
|
## 升级曲线:roundf(10 × 1.4^(level-1)),P6-N16
|
||||||
## Level 1→2: 10 XP; Level 5→6: ≈ 54 XP
|
## Level 1→2: 10 XP; Level 5→6: ≈ 54 XP
|
||||||
@@ -91,6 +97,22 @@ func regen_mana(delta: float) -> void:
|
|||||||
return
|
return
|
||||||
mana = min(mana_max, mana + mana_regen * delta)
|
mana = min(mana_max, mana + mana_regen * delta)
|
||||||
|
|
||||||
|
## 击杀回蓝(魔力虹吸)
|
||||||
|
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, {})
|
||||||
|
|
||||||
func _on_player_damaged(payload: Dictionary) -> void:
|
func _on_player_damaged(payload: Dictionary) -> void:
|
||||||
take_damage(float(payload.get("damage", 0.0)))
|
take_damage(float(payload.get("damage", 0.0)))
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ func complete_ftue(chosen_difficulty: int) -> void:
|
|||||||
var _MULTS: Dictionary = {} # key → [初学者, 标准, 挑战]
|
var _MULTS: Dictionary = {} # key → [初学者, 标准, 挑战]
|
||||||
var _BOSS_HP: Dictionary = {} # 原型→基础血量
|
var _BOSS_HP: Dictionary = {} # 原型→基础血量
|
||||||
var _AETHER_THRESHOLDS: Array = [] # [[到达波次, 碎片], ...] 升序
|
var _AETHER_THRESHOLDS: Array = [] # [[到达波次, 碎片], ...] 升序
|
||||||
|
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}
|
||||||
|
|
||||||
const BALANCE_JSON: String = "res://data/balance.json"
|
const BALANCE_JSON: String = "res://data/balance.json"
|
||||||
|
|
||||||
@@ -61,6 +63,12 @@ func aether_for_wave(wave: int) -> int:
|
|||||||
result = maxi(result, int(entry[1])) # 取 ≤wave 阶梯中的最大值,容忍无序表(设计师手填不必排序)
|
result = maxi(result, int(entry[1])) # 取 ≤wave 阶梯中的最大值,容忍无序表(设计师手填不必排序)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
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))
|
||||||
|
|
||||||
## 数据驱动:加载 balance.json(难度乘子 + Boss 血量)
|
## 数据驱动:加载 balance.json(难度乘子 + Boss 血量)
|
||||||
func _load_balance() -> void:
|
func _load_balance() -> void:
|
||||||
if not FileAccess.file_exists(BALANCE_JSON):
|
if not FileAccess.file_exists(BALANCE_JSON):
|
||||||
@@ -77,6 +85,12 @@ func _load_balance() -> void:
|
|||||||
_BOSS_HP = data["boss_hp"]
|
_BOSS_HP = data["boss_hp"]
|
||||||
if data.has("aether_thresholds") and data["aether_thresholds"] is Array:
|
if data.has("aether_thresholds") and data["aether_thresholds"] is Array:
|
||||||
_AETHER_THRESHOLDS = data["aether_thresholds"]
|
_AETHER_THRESHOLDS = data["aether_thresholds"]
|
||||||
|
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]
|
||||||
|
|
||||||
func difficulty_name() -> String:
|
func difficulty_name() -> String:
|
||||||
return ["DIFF_BEGINNER", "DIFF_STANDARD", "DIFF_CHALLENGE"][difficulty]
|
return ["DIFF_BEGINNER", "DIFF_STANDARD", "DIFF_CHALLENGE"][difficulty]
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ func _core_from_dict(cid: String, d: Dictionary) -> CoreDefinition:
|
|||||||
c.edges = d.get("edges", [])
|
c.edges = d.get("edges", [])
|
||||||
c.base_mana_max = float(d.get("mana_max", 100.0))
|
c.base_mana_max = float(d.get("mana_max", 100.0))
|
||||||
c.base_mana_regen = float(d.get("mana_regen", 5.0))
|
c.base_mana_regen = float(d.get("mana_regen", 5.0))
|
||||||
|
c.base_mana_leech = float(d.get("mana_leech", 0.0))
|
||||||
return c
|
return c
|
||||||
|
|
||||||
## 默认装备(combat_test 等用):wand_basic + 第一个 ACTION 法术
|
## 默认装备(combat_test 等用):wand_basic + 第一个 ACTION 法术
|
||||||
|
|||||||
@@ -228,6 +228,8 @@ func close_shop_and_next_wave() -> void:
|
|||||||
_transition_to_battle(next_wave)
|
_transition_to_battle(next_wave)
|
||||||
|
|
||||||
func _on_player_died(_payload: Dictionary) -> void:
|
func _on_player_died(_payload: Dictionary) -> void:
|
||||||
|
if state == GameState.GAME_OVER:
|
||||||
|
return # 防重入:同帧内多路死亡(子弹命中 + 卖血施法)不重复结算战绩/发以太
|
||||||
state = GameState.GAME_OVER
|
state = GameState.GAME_OVER
|
||||||
# 先采集本局战绩(reset 前),写入排行榜
|
# 先采集本局战绩(reset 前),写入排行榜
|
||||||
var wave_reached: int = max(1, WaveManager.current_wave)
|
var wave_reached: int = max(1, WaveManager.current_wave)
|
||||||
@@ -256,6 +258,11 @@ func _rebuild_wand() -> void:
|
|||||||
continue
|
continue
|
||||||
nodes.append(SpellRegistry.get_spell(sid)) # 纯数据驱动;未知 id → null(下游按位置跳过)
|
nodes.append(SpellRegistry.get_spell(sid)) # 纯数据驱动;未知 id → null(下游按位置跳过)
|
||||||
_equipped_compiled = SpellEvaluator.compile_wand(_equipped_core, nodes)
|
_equipped_compiled = SpellEvaluator.compile_wand(_equipped_core, nodes)
|
||||||
|
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
|
||||||
|
|
||||||
# ── 法杖存档(ProfileManager A/B 槽,ADR-A2)─────────────────
|
# ── 法杖存档(ProfileManager A/B 槽,ADR-A2)─────────────────
|
||||||
func get_wand_save_data() -> Dictionary:
|
func get_wand_save_data() -> Dictionary:
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ func _ready() -> void:
|
|||||||
|
|
||||||
func _physics_process(delta: float) -> void:
|
func _physics_process(delta: float) -> void:
|
||||||
PlayerStats.regen_mana(delta)
|
PlayerStats.regen_mana(delta)
|
||||||
|
PlayerStats.mana_heat = maxf(0.0, PlayerStats.mana_heat - SettingsManager.mana_heat_decay() * delta)
|
||||||
_handle_movement(delta)
|
_handle_movement(delta)
|
||||||
_handle_charge_state(delta)
|
_handle_charge_state(delta)
|
||||||
_handle_auto_cast(delta)
|
_handle_auto_cast(delta)
|
||||||
@@ -90,6 +91,7 @@ func equip_wand(core: CoreDefinition, compiled: CompiledDeck) -> void:
|
|||||||
PlayerStats.set_mana_pool(core.base_mana_max, core.base_mana_regen)
|
PlayerStats.set_mana_pool(core.base_mana_max, core.base_mana_regen)
|
||||||
else:
|
else:
|
||||||
PlayerStats.set_mana_pool(100.0, 5.0)
|
PlayerStats.set_mana_pool(100.0, 5.0)
|
||||||
|
PlayerStats.mana_heat = 0.0
|
||||||
|
|
||||||
func get_position() -> Vector2:
|
func get_position() -> Vector2:
|
||||||
return _position
|
return _position
|
||||||
|
|||||||
@@ -30,3 +30,4 @@ var topology: int = Topology.LINEAR
|
|||||||
## 魔力池(MVP)——由 WandPreset 从 cores.json 读入
|
## 魔力池(MVP)——由 WandPreset 从 cores.json 读入
|
||||||
@export var base_mana_max: float = 100.0
|
@export var base_mana_max: float = 100.0
|
||||||
@export var base_mana_regen: float = 5.0 # 每秒回复
|
@export var base_mana_regen: float = 5.0 # 每秒回复
|
||||||
|
@export var base_mana_leech: float = 0.0 # 击杀回蓝基础值(可叠加卡组内 meta.mana_leech)
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ var stats: CastStats = null # 本次施法的修改层
|
|||||||
## 跨帧持久寄存器(长度固定 4,P6-N48)
|
## 跨帧持久寄存器(长度固定 4,P6-N48)
|
||||||
## R1-R4:供 LOGIC_EVERY_N_SHOTS 等指令跨帧计数
|
## R1-R4:供 LOGIC_EVERY_N_SHOTS 等指令跨帧计数
|
||||||
var registers: PackedFloat32Array = PackedFloat32Array()
|
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:
|
func _init() -> void:
|
||||||
stats = CastStats.new()
|
stats = CastStats.new()
|
||||||
@@ -21,6 +23,8 @@ func reset() -> void:
|
|||||||
stats.reset()
|
stats.reset()
|
||||||
else:
|
else:
|
||||||
stats = CastStats.new()
|
stats = CastStats.new()
|
||||||
|
hp_cost_accum = 0.0
|
||||||
|
mana_spent_this_cast = false
|
||||||
# registers 小心:仅当 Core 没有 PERSISTENT_MEMORY Feature 时才清零
|
# registers 小心:仅当 Core 没有 PERSISTENT_MEMORY Feature 时才清零
|
||||||
# 具体清空逻辑在 SpellContextPool.acquire_for_wand() 中处理
|
# 具体清空逻辑在 SpellContextPool.acquire_for_wand() 中处理
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
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 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,8 @@ 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 → 停施
|
||||||
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 +345,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 +353,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 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:
|
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 +493,19 @@ 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):
|
||||||
|
ctx.mana_spent_this_cast = true # 真实扣蓝才记(infinite 卖血不算)→ heat 只对真实施法上升
|
||||||
|
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 +523,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 +574,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 +585,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 +600,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 +616,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 +628,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
|
||||||
|
|||||||
@@ -212,3 +212,6 @@ msgstr "Unlocked"
|
|||||||
|
|
||||||
msgid "META_CLOSE"
|
msgid "META_CLOSE"
|
||||||
msgstr "Close"
|
msgstr "Close"
|
||||||
|
|
||||||
|
msgid "UI_MANA"
|
||||||
|
msgstr "MP %d/%d"
|
||||||
|
|||||||
@@ -212,3 +212,6 @@ msgstr "解放済み"
|
|||||||
|
|
||||||
msgid "META_CLOSE"
|
msgid "META_CLOSE"
|
||||||
msgstr "閉じる"
|
msgstr "閉じる"
|
||||||
|
|
||||||
|
msgid "UI_MANA"
|
||||||
|
msgstr "MP %d/%d"
|
||||||
|
|||||||
@@ -212,3 +212,6 @@ msgstr "已解锁"
|
|||||||
|
|
||||||
msgid "META_CLOSE"
|
msgid "META_CLOSE"
|
||||||
msgstr "关闭"
|
msgstr "关闭"
|
||||||
|
|
||||||
|
msgid "UI_MANA"
|
||||||
|
msgstr "蓝 %d/%d"
|
||||||
|
|||||||
@@ -212,3 +212,6 @@ msgstr "已解鎖"
|
|||||||
|
|
||||||
msgid "META_CLOSE"
|
msgid "META_CLOSE"
|
||||||
msgstr "關閉"
|
msgstr "關閉"
|
||||||
|
|
||||||
|
msgid "UI_MANA"
|
||||||
|
msgstr "藍 %d/%d"
|
||||||
|
|||||||
Reference in New Issue
Block a user