extends AnimationTree @export_group("References") @export var _player: Player @export var _attack: PlayerAttacker @export var _bone_flipper: BoneFlipper @export var _sfx_audio_player: AudioStreamPlayer3D @export var _hurt_particles: GPUParticles3D @export var _footsteps_player: FootstepsPlayer @export_group("Audio") @export var _hurt_sound: AudioStream @export var _hit_sounds: Array[AudioStream] @export var _swing_sounds: AudioStream var _speed: float var _has_input: bool var _queue_hit_sound: bool @onready var _sfx_audio_playback_polyphonic := ( _sfx_audio_player.get_stream_playback() as AudioStreamPlaybackPolyphonic ) func _ready() -> void: assert(_player, "_player missing!") Music.track_started.connect(_on_music_track_started) _set_bpm() _attack.attacked.connect(_on_attack_attacked) _attack.did_hit.connect(_on_attack_did_hit) _player.stats.damaged.connect(_on_stats_damaged) func _process(_delta: float) -> void: _footsteps_player.can_play = _player.is_on_floor() _bone_flipper.flip = _is_left() var has_input_prev := _has_input _speed = _player.velocity.length() / _player.mover.move_speed _has_input = ( _player.mover.move_input.length() > 0 and not _player.attack.is_hitting() ) var velocity_relative := _player.to_local( _player.global_position + _player.velocity ) var velocity_blend := ( Vector2(-velocity_relative.x, velocity_relative.z) / _player.mover.move_speed ) if _is_left(): velocity_blend.x = -velocity_blend.x Debugger.text("velocity_blend", velocity_blend, 2) set(&"parameters/locomotion/run/blend_position", velocity_blend) if has_input_prev != _has_input: if _has_input: _abort_oneshots() else: _run_to_idle() if _queue_hit_sound: _queue_hit_sound = false for stream in _hit_sounds: _play_sound(stream) func _is_left() -> bool: return _attack.side == PlayerAttacker.Side.LEFT func _play_sound(stream: AudioStream) -> void: _sfx_audio_playback_polyphonic.play_stream(stream) func _set_bpm() -> void: set(&"parameters/main_time_scale/scale", Music.bpm_factor) func _run_to_idle() -> void: set( &"parameters/run->idle_oneshot/request", AnimationNodeOneShot.ONE_SHOT_REQUEST_FIRE ) func _abort_oneshots() -> void: set(&"parameters/hit_oneshot/request", AnimationNodeOneShot.ONE_SHOT_REQUEST_ABORT) set( &"parameters/run->idle_oneshot/request", AnimationNodeOneShot.ONE_SHOT_REQUEST_ABORT ) func _on_attack_attacked() -> void: set(&"parameters/hit_oneshot/request", AnimationNodeOneShot.ONE_SHOT_REQUEST_FIRE) _play_sound(_swing_sounds) func _on_attack_did_hit() -> void: _queue_hit_sound = true func _on_stats_damaged() -> void: _play_sound(_hurt_sound) _hurt_particles.restart() _hurt_particles.emitting = true for node in _hurt_particles.get_children(): if node is GPUParticles3D: (node as GPUParticles3D).restart() (node as GPUParticles3D).emitting = true func _on_music_track_started() -> void: _set_bpm()