Files
spellforge/scripts/domain/player_manager.gd
T
joywayerandClaude Opus 5 729c74932f refactor(attr): cpu_limit 守卫移至换杖处;cast_delay_mod 改在开火时生效
守卫从 execute_compiled 搬到 equip_wand:诊断应放在能导致故障的状态转换处,
而非 2 次/秒的使用点——施法端刷屏会埋掉根因,换杖处每局只跑几次且拿得到杖名。
maxi(..., 1) 保留:错误已在换杖处大声报出,退化不再静默,而去掉它会让法术硬瘫。

订正原注释的事实错误:它声称兜住「attributes.json 缺失」与「attr_id 打错」,
但这两种 player_stats.gd:173/191 早已各自报错,守卫零覆盖。真正无人管的是
「新调用点忘了注入」(正是 combat_test 修前的样子)与「cores.json 某杖配成 0」。

cast_delay_mod 由 equip_wand 快照改为开火时相乘:_cast_timer 语义两种写法一致
(总是从上次写入值倒数),代价仅每次施法一次浮点乘法,但「下一个周期」由下次
换杖变为下次施法——玩家买急速道具期待的是后者。随之删去解释延迟生效的注释。

另在 _rebuild_wand 注入处记下 remove-before-add 为何 load-bearing:漏了会让
cpu_limit 随 install_spell 累积,而 hard: 50 会把它封成一个看起来合理的数字。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 11:53:16 +08:00

131 lines
4.3 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## PlayerManager — 玩家管理 Autoload
## S0WASD 移动 + 位置查询
## S1:自动施法
## S4:停步蓄力 / 冲刺边沿检测(P6-N69、P6-N36
extends Node
const MOVE_THRESHOLD_NORM: float = 0.1
const CHARGE_TRIGGER_TIME: float = 1.5
var _position: Vector2 = Vector2.ZERO
var _velocity: Vector2 = Vector2.ZERO
var _player_node: Node2D = null
var _equipped_core: CoreDefinition = null
var _equipped_compiled: CompiledDeck = null
var _cast_timer: float = 0.0
var _cast_interval: float = 0.5 # 法杖基准间隔(core.cast_interval);实际间隔在开火时 × cast_delay_mod
var _is_moving: bool = false
var _stationary_time: float = 0.0
var _charge_triggered: bool = false
var _last_dash_time: float = -999.0
var kill_count: int = 0
func _ready() -> void:
EventBus.subscribe(EventID.ENEMY_KILLED, _on_enemy_killed)
func _physics_process(delta: float) -> void:
PlayerStats.regen_mana(delta)
PlayerStats.mana_heat = maxf(0.0, PlayerStats.mana_heat - SettingsManager.mana_heat_decay() * delta)
_handle_movement(delta)
_handle_charge_state(delta)
_handle_auto_cast(delta)
func _handle_movement(delta: float) -> void:
var dir := Vector2(
Input.get_axis("ui_left", "ui_right"),
Input.get_axis("ui_up", "ui_down")
)
_is_moving = dir.length() > MOVE_THRESHOLD_NORM
if _is_moving:
dir = dir.normalized()
_velocity = dir * PlayerStats.move_speed
else:
_velocity = Vector2.ZERO
_position += _velocity * delta
if _player_node:
_player_node.global_position = _position
func _handle_charge_state(delta: float) -> void:
if Input.is_action_just_pressed("ui_accept"):
_last_dash_time = Time.get_ticks_msec() / 1000.0
EventBus.emit(EventID.DASH_TRIGGERED, {
"caster_id": 0,
"stationary_time": _stationary_time,
})
_stationary_time = 0.0
_charge_triggered = false
return
if _is_moving:
if _stationary_time > 0.0:
_stationary_time = 0.0
_charge_triggered = false
EventBus.emit(EventID.CHARGE_STATE_CHANGED, {"caster_id": 0, "progress": 0.0})
else:
_stationary_time += delta
if _stationary_time >= 0.3:
var prog: float = clampf(_stationary_time / CHARGE_TRIGGER_TIME, 0.0, 1.0)
EventBus.emit(EventID.CHARGE_STATE_CHANGED, {"caster_id": 0, "progress": prog})
if _stationary_time >= CHARGE_TRIGGER_TIME and not _charge_triggered:
_charge_triggered = true
EventBus.emit(EventID.CHARGE_FIRED, {"caster_id": 0})
func _handle_auto_cast(delta: float) -> void:
if _equipped_compiled == null or _equipped_compiled.is_empty():
return
_cast_timer -= delta
if _cast_timer <= 0.0:
_cast_timer = _cast_interval * PlayerStats.cast_delay_mod
SpellEvaluator.execute_compiled(_equipped_compiled, 0, _position)
func equip_wand(core: CoreDefinition, compiled: CompiledDeck) -> void:
_equipped_core = core
_equipped_compiled = compiled
_cast_interval = core.cast_interval if core else 0.5
_cast_timer = 0.0
if core:
PlayerStats.set_mana_pool(core.base_mana_max, core.base_mana_regen)
else:
PlayerStats.set_mana_pool(100.0, 5.0)
PlayerStats.mana_heat = 0.0
# 0 守卫:cpu_limit=0 → MAX_OPS 退化,法术近乎全静默。放在换杖处而非施法处——施法端 2 次/秒
# 会刷屏埋掉根因,且此处拿得到杖名。兜的是「新调用点忘了注入」与「cores.json 某杖配成 0」,
# 二者上游无任何报错(缺 attributes.json / attr_id 打错已分别由 player_stats.gd:173/191 报出)
if PlayerStats.cpu_limit <= 0:
push_error("PlayerManager: 装备「%s」后 cpu_limit=0,法术执行预算将退化为 40 步;检查该杖 cores.json 的 cpu_limit 与 source=\"core\" 加成注入" % (core.display_name if core else "—"))
func get_position() -> Vector2:
return _position
func get_velocity() -> Vector2:
return _velocity
func get_stationary_time() -> float:
return _stationary_time
func is_charge_triggered() -> bool:
return _charge_triggered
func set_player_node(node: Node2D) -> void:
_player_node = node
if node:
_position = node.global_position
func _on_enemy_killed(_payload: Dictionary) -> void:
kill_count += 1
func reset() -> void:
_position = Vector2.ZERO
_velocity = Vector2.ZERO
_player_node = null
_equipped_compiled = null
_equipped_core = null
_cast_timer = 0.0
_is_moving = false
_stationary_time = 0.0
_charge_triggered = false
kill_count = 0