extends CharacterBody3D @export var stats: PlayerStats var anim_player: AnimationPlayer = null var current_anim: String = "" var attack_lock_until: float = 0.0 func _ready() -> void: add_to_group("player") PlayerData.init_from_resource(stats) if PlayerData.returning_from_dungeon: global_position = PlayerData.portal_position + Vector3(0, 1, -5) PlayerData.returning_from_dungeon = false elif PlayerData.dungeon_cleared: PlayerData.clear_cache() anim_player = get_node_or_null("Mesh/Model/AnimationPlayer") _play_anim("Idle") EventBus.attack_executed.connect(_on_attack_executed) EventBus.entity_died.connect(_on_entity_died) EventBus.player_respawned.connect(_on_player_respawned) func _process(_delta: float) -> void: if not PlayerData.alive: return var now: float = Time.get_ticks_msec() / 1000.0 if now < attack_lock_until: return if velocity.length() > 0.1: _play_anim("Running_A") else: _play_anim("Idle") func _play_anim(anim_name: String, loop: bool = true) -> void: if not anim_player: return if current_anim == anim_name: return if not anim_player.has_animation(anim_name): return var anim: Animation = anim_player.get_animation(anim_name) if anim: anim.loop_mode = Animation.LOOP_LINEAR if loop else Animation.LOOP_NONE anim_player.play(anim_name) current_anim = anim_name func _on_attack_executed(attacker: Node, _pos: Vector3, _dir: Vector3, _damage: float) -> void: if attacker != self: return _play_anim("1H_Melee_Attack_Chop", false) attack_lock_until = Time.get_ticks_msec() / 1000.0 + 0.5 func _on_entity_died(entity: Node) -> void: if entity != PlayerData: return _play_anim("Death_A", false) func _on_player_respawned(_player: Node) -> void: _play_anim("Idle")