守卫从 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>
131 lines
4.3 KiB
GDScript
131 lines
4.3 KiB
GDScript
## PlayerManager — 玩家管理 Autoload
|
||
## S0:WASD 移动 + 位置查询
|
||
## 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
|