Tales-of-PKMN/game/player/aim/throw_resolver.gd
TheThomaas 250976d170 Squashed commit of the following:
commit 8e7da96bfcd52b230feaf40fbc6d4a42b80c5723
Author: TheThomaas <thethomaas@protonmail.ch>
Date:   Sat Oct 17 18:55:14 2026 +0200

    Make state machine more reusable

commit 37da7248b8c33ce95cff7ef9332c5ccf6e3cd418
Author: TheThomaas <thethomaas@protonmail.ch>
Date:   Sat Oct 17 18:44:34 2026 +0200

    Change aim state into flag

commit fbb3a84a12e2cabff9b36390da7483b8de7e9379
Author: TheThomaas <thethomaas@protonmail.ch>
Date:   Sat Oct 17 18:33:08 2026 +0200

    Replace fixed references with ui references

commit 913955b13e9980780ac88761845d9e1f165f0d0e
Author: TheThomaas <thethomaas@protonmail.ch>
Date:   Fri Oct 16 13:32:21 2026 +0200

    Update camera action handling

commit 4ce0ac13ffea473cf62c2fefd1571e9df1fbbcaa
Author: TheThomaas <thethomaas@protonmail.ch>
Date:   Fri Oct 16 13:04:39 2026 +0200

    Update mounting system

commit e410eca38760955b2866eecd6ac0281e2041e76d
Author: TheThomaas <thethomaas@protonmail.ch>
Date:   Fri Oct 16 12:57:25 2026 +0200

    Update aiming system

commit 4c788df485ccbff5f91fd51d9f480e495f11da5f
Author: TheThomaas <thethomaas@protonmail.ch>
Date:   Thu Oct 15 21:50:26 2026 +0200

    Centralize states names into constants

commit 6059479c87d749e986a48adb520ef33121104da5
Author: TheThomaas <thethomaas@protonmail.ch>
Date:   Thu Oct 15 21:25:32 2026 +0200

    Refator Crouch state into flag

commit 08faec70e770042d36eed6f173a63f3aac1f67c6
Author: TheThomaas <thethomaas@protonmail.ch>
Date:   Wed Oct 14 10:19:35 2026 +0200

    Update player input management and state machine

commit 11a0aecc58c50c5862a03b4fd0b55e8db024031f
Author: TheThomaas <thethomaas@protonmail.ch>
Date:   Wed Oct 14 10:17:06 2026 +0200

    Delete charizard.tres

commit 399eac2b6c64f242a1c2e12360d811127f87681c
Author: TheThomaas <thethomaas@protonmail.ch>
Date:   Tue Oct 13 18:20:45 2026 +0200

    Remove unused MountedState

commit 42a3668c52fe96a7509a74290a94fc5a8084b713
Author: TheThomaas <thethomaas@protonmail.ch>
Date:   Tue Oct 13 18:18:08 2026 +0200

    Group grounded states under common class
2026-10-17 18:56:13 +02:00

104 lines
5 KiB
GDScript

extends PlayerComponent
class_name ThrowResolver
#resout un lancer : trajectoire de la balle, point d'atterrissage, envoi du pokemon et demande de combat.
#ThrowState ne gere plus que le deroule (charge, windup, recuperation) et appelle throw_ball().
#composant a ajouter sous le PlayerCharacter (le nom du node est libre, voir PlayerComponent).
#il vit aussi longtemps que le joueur : l'atterrissage est gere ici meme si le joueur a change d'etat entre-temps.
@export_group("Ball")
@export var ball_scene : PackedScene #modele de la balle (optionnel : sphere rouge par defaut)
@export var hand_height : float = 1.4
@export var ball_speed : float = 18.0
@export var min_flight_time : float = 0.25
@export var max_flight_time : float = 1.2
@export var arc_height : float = 1.5
@export_group("Landing")
@export_flags_3d_physics var ground_mask : int = 1
@export var land_distance_from_target : float = 1.5 #la balle s'arrete devant une cible de combat
@export var free_hit_radius : float = 2.0 #lancer sans cible verrouillee : rayon autour du point d'atterrissage pour trouver une cible de combat
func register_with_player() -> void:
player.throw_resolver = self
#---------- lancer ----------
func throw_ball(pokemon : PokemonInstance, target : Variant, free_aim_point : Vector3) -> void:
#target : AimTarget verrouille au moment du relachement (ou null), free_aim_point : point vise s'il n'y en a pas
var from : Vector3 = player.global_position + Vector3.UP * hand_height
var to : Vector3 = _get_landing_point(target, free_aim_point)
var flight_time : float = clampf(from.distance_to(to) / ball_speed, min_flight_time, max_flight_time)
var scene : Node = get_tree().current_scene
var ball : ThrownBall = ThrownBall.launch(scene, from, to, flight_time, arc_height, ball_scene)
ball.landed.connect(_on_ball_landed.bind(pokemon, target, from))
#---------- point vise ----------
func compute_free_aim_point() -> Vector3:
#point vise au centre de l'ecran : premier obstacle, sinon fin de portee ramenee au sol
var cam : Camera3D = player.cam_holder.cam
var origin : Vector3 = cam.global_position
var far : Vector3 = origin - cam.global_basis.z * player.aim_targeting.throw_range
var query := PhysicsRayQueryParameters3D.create(origin, far, ground_mask, [player.get_rid()])
var hit : Dictionary = player.get_world_3d().direct_space_state.intersect_ray(query)
if hit.is_empty(): return _snap_to_ground(far)
return _snap_to_ground(hit["position"] + hit["normal"] * 0.5)
func _get_landing_point(target : Variant, free_aim_point : Vector3) -> Vector3:
if !is_instance_valid(target): return free_aim_point
var target_pos : Vector3 = target.global_position
if _starts_battle(target):
#la balle s'arrete devant la cible, du cote du joueur
var to_player : Vector3 = player.global_position - target_pos
to_player.y = 0.0
if to_player.length() > land_distance_from_target:
target_pos += to_player.normalized() * land_distance_from_target
return _snap_to_ground(target_pos)
func _snap_to_ground(point : Vector3) -> Vector3:
var query := PhysicsRayQueryParameters3D.create(point + Vector3.UP * 2.0, point + Vector3.DOWN * 6.0, ground_mask, [player.get_rid()])
var hit : Dictionary = player.get_world_3d().direct_space_state.intersect_ray(query)
return hit["position"] if !hit.is_empty() else point
#---------- atterrissage ----------
func _on_ball_landed(landing_pos : Vector3, pokemon : PokemonInstance, target : Variant, thrower_pos : Vector3) -> void:
var scene : Node = get_tree().current_scene if is_inside_tree() else null
if scene == null: return
#lancer sans cible verrouillee : si la balle atterrit pres d'une cible de combat, c'est elle qui est visee
if !is_instance_valid(target):
target = _find_battle_target_near(landing_pos)
var target_valid : bool = is_instance_valid(target)
#le pokemon regarde la cible, ou dans la direction du lancer s'il n'y en a pas
var look_pos : Vector3 = target.global_position if target_valid else landing_pos + (landing_pos - thrower_pos)
var sent : SentPokemon = SentPokemon.send(scene, pokemon, landing_pos, look_pos)
if target_valid and _starts_battle(target):
GameData.battle_requested.emit(sent, target)
elif target_valid:
#diagnostic : retire ce message quand tout fonctionne
push_warning("ThrowResolver : la cible '%s' ne lance pas de combat (starts_battle faux : Encounter vide, ou aim_target.gd pas remplace)." % target.name)
func _find_battle_target_near(point : Vector3) -> AimTarget:
#cible de combat active la plus proche du point d'atterrissage, dans free_hit_radius
var best : AimTarget = null
var best_dist : float = free_hit_radius
for node in get_tree().get_nodes_in_group(AimTargeting.TARGET_GROUP):
var t := node as AimTarget
if t == null or !t.enabled or !_starts_battle(t): continue
var dist : float = point.distance_to(t.global_position)
if dist <= best_dist:
best_dist = dist
best = t
return best
func _starts_battle(target : Object) -> bool:
#AimTarget doit avoir : @export var starts_battle : bool = false (get() renvoie null si la propriete n'existe pas)
return target.get("starts_battle") == true