class_name ChargeComponent extends Node signal charge_changed(current: float, maximum: float, charge_ready: bool, active: bool) signal release_requested(skill_id: StringName, direction: Vector2) @export var charge_duration := 1.1 @export var animation_player_path: NodePath @export var effect_sprite_path: NodePath var value := 0.0 var charge_ready := false var active := false var _hold_symbol: StringName = &"" var _hold_direction := Vector2.ZERO var _effect_time := 0.0 var _animation_time := 0.0 @onready var _animation_player: AnimationPlayer = get_node_or_null(animation_player_path) as AnimationPlayer @onready var _effect_sprite: Sprite2D = get_node_or_null(effect_sprite_path) as Sprite2D func begin_hold(symbol: StringName, direction: Vector2) -> void: _hold_symbol = symbol _hold_direction = direction func finish_hold(symbol: StringName) -> void: if _hold_symbol != symbol: return var release_ready := active and charge_ready var release_direction := _hold_direction var release_skill := &"skill_a_charge_release" if symbol == &"A" else &"skill_d_charge_release" cancel() if release_ready: release_requested.emit(release_skill, release_direction) func tick(delta: float, can_charge: bool) -> void: if _hold_symbol.is_empty(): return if not active: if not can_charge: return _start() if not active: return _update_charge_animation(delta) value = minf(charge_duration, value + delta) charge_ready = value >= charge_duration _update_charge_effect(delta) _emit_changed() func cancel() -> void: _hold_symbol = &"" _hold_direction = Vector2.ZERO active = false value = 0.0 charge_ready = false _animation_time = 0.0 _set_effect_visible(false) _emit_changed() func is_active() -> bool: return active func is_ready() -> bool: return charge_ready func maximum() -> float: return charge_duration func _start() -> void: active = true value = 0.0 charge_ready = false _effect_time = 0.0 _animation_time = 0.0 _play_charge_animation(&"warrior_charge_intro") _update_charge_effect(0.0) _emit_changed() func _update_charge_effect(delta: float) -> void: if _effect_sprite == null: return _effect_sprite.visible = active if not active: return _effect_time += delta _effect_sprite.frame = int(_effect_time * 12.0) % 5 func _update_charge_animation(delta: float) -> void: _animation_time += delta var intro_length := _animation_length(&"warrior_charge_intro") if _animation_time < intro_length: _play_charge_animation(&"warrior_charge_intro") else: _play_charge_animation(&"warrior_charge_loop") func _play_charge_animation(animation_name: StringName) -> void: if _animation_player != null and _animation_player.has_animation(animation_name) and _animation_player.current_animation != animation_name: _animation_player.play(animation_name) func _animation_length(animation_name: StringName) -> float: if _animation_player != null and _animation_player.has_animation(animation_name): return maxf(0.1, _animation_player.get_animation(animation_name).length) return 0.1 func _set_effect_visible(is_visible: bool) -> void: if _effect_sprite != null: _effect_sprite.visible = is_visible func _emit_changed() -> void: charge_changed.emit(value, charge_duration, charge_ready, active)