extends PlayerState class_name ThrowState #@export var cancel_action : StringName = &"player_throw_cancel" #B / clic droit (cree automatiquement s'il n'existe pas) @export var cancel_hint : Hint #cancel_throw.tres var cancel_action : StringName: #l'action vient du Hint : une seule definition get: return cancel_hint.action @export_group("Charge") @export var charge_move_speed : float = 3.0 #vitesse de deplacement pendant que RT est maintenue @export var charge_move_accel : float = 8.0 @export_group("Timing") @export var windup_time : float = 0.25 #delai entre le relachement et le depart de la balle (a caler sur l'animation) @export var recovery_time : float = 0.35 #temps de recuperation apres le lancer var _elapsed : float = 0.0 var _released : bool = false #true des que RT est relachee (phase windup/recovery) var _thrown : bool = false var _pokemon : PokemonInstance = null var _target : Variant = null #AimTarget verrouille au moment du relachement var _free_aim_point : Vector3 = Vector3.ZERO func _ready() -> void: InputRegistry.ensure(cancel_action, [InputRegistry.pad_button(JOY_BUTTON_B), InputRegistry.mouse(MOUSE_BUTTON_RIGHT)]) func on_enter() -> void: _elapsed = 0.0 _released = false _thrown = false _pokemon = GameData.get_selected_pokemon() _target = null _free_aim_point = Vector3.ZERO player.floor_snap_length = 1.0 #player.godot_plush_skin.set_state("throw") func exit() -> void: AimReticle.hide_instance() #si la visee est encore active, GroundedState le reaffiche a la frame suivante func physics_update(delta : float) -> void: if applies(): return player.gravity_apply(delta) if _pokemon == null: slow_down(delta) finish() #rien a lancer (equipe vide ou tous K.O.) return #phase 1 : charge, tant que RT est maintenue if !_released: charge(delta) return #phase 2 : windup puis recovery slow_down(delta) if !_thrown and Input.is_action_just_pressed(cancel_action): finish() #annule avant que la balle parte return if _thrown and input.aim_pressed(): finish() #balle partie : on coupe la recuperation pour pouvoir viser tout de suite return _elapsed += delta if !_thrown and _elapsed >= windup_time: throw_ball() if _elapsed >= windup_time + recovery_time: finish() func applies() -> bool: return !player.is_on_floor() and player.velocity.y < 0.0 and fall() #---------- charge ---------- func charge(delta : float) -> void: if Input.is_action_just_pressed(cancel_action): finish() #annule : rien n'est lance return if input.aim_pressed(): player.aim_targeting.begin_aim() #on peut se mettre a viser en pleine charge, sans perdre la charge if !input.throw_held(): release() return move_while_charging(delta) if player.is_aiming: player.aim_targeting.tick() #cible verrouillee / changement de cible pendant la charge else: player.aim_targeting.update_reticle() func release() -> void: #RT relachee : on fige la cible et le point vise, puis le windup demarre _released = true _elapsed = 0.0 _target = player.aim_target if is_instance_valid(player.aim_target) else null _free_aim_point = player.throw_resolver.compute_free_aim_point() func move_while_charging(delta : float) -> void: player.move_dir = input.read_move_dir() if player.move_dir and player.is_on_floor(): accelerate(charge_move_speed, charge_move_accel, delta) else: slow_down(delta) func finish() -> void: if input.aim_held(): player.aim_targeting.begin_aim() #visee enfoncee : elle continue apres le lancer (sans effet si deja active) #accroupi ou non : la posture est conservee, Idle / Walk la reappliquent var dir : Vector2 = input.read_move_raw() request_ground_state(dir != Vector2.ZERO) #---------- lancer ---------- func throw_ball() -> void: _thrown = true hints_changed.emit() AimReticle.hide_instance() #la balle est autonome : son atterrissage et l'envoi du pokemon sont geres par ThrowResolver, meme si le joueur change d'etat entre-temps player.throw_resolver.throw_ball(_pokemon, _target, _free_aim_point) func get_hints() -> Array[Hint]: var list : Array[Hint] = [] if !_thrown: list.append(cancel_hint) #plus d'annulation possible une fois la balle partie return list