class_name SentPokemon extends Node3D #un pokemon de l'equipe sorti dans le monde (apres un lancer). #plusieurs peuvent etre sortis en meme temps ; un meme pokemon ne peut l'etre qu'une fois. signal recalled const GROUP : StringName = &"sent_pokemon" const MODEL_FACES_PLUS_Z : bool = true #true si tes modeles regardent vers +Z (cas des glTF) : sinon mets false static var _counter : int = 0 #ordre d'envoi, pour retrouver le plus recent var pokemon : PokemonInstance var model : Node3D var order : int = 0 static func send(parent : Node, p : PokemonInstance, at : Vector3, look_at_pos : Vector3 = Vector3.INF) -> SentPokemon: #un pokemon deja sorti est d'abord rappele (il "se deplace" a l'endroit du nouveau lancer) for existing in parent.get_tree().get_nodes_in_group(GROUP): if existing is SentPokemon and existing.pokemon == p: existing.recall() var sent := SentPokemon.new() sent.pokemon = p sent.order = _counter _counter += 1 parent.add_child(sent) sent.global_position = at sent._build_model() if look_at_pos != Vector3.INF: var flat : Vector3 = Vector3(look_at_pos.x, at.y, look_at_pos.z) if flat.distance_to(at) > 0.01: sent.look_at(flat, Vector3.UP) if MODEL_FACES_PLUS_Z: sent.rotate_y(PI) #look_at oriente -Z vers la cible return sent static func find_for(tree : SceneTree, p : PokemonInstance) -> SentPokemon: #le SentPokemon de ce pokemon, s'il est actuellement dehors for n in tree.get_nodes_in_group(GROUP): if n is SentPokemon and n.pokemon == p and !n.is_queued_for_deletion(): return n return null static func get_latest(tree : SceneTree) -> SentPokemon: #le pokemon sorti le plus recemment var latest : SentPokemon = null for n in tree.get_nodes_in_group(GROUP): if n is SentPokemon and !n.is_queued_for_deletion() and (latest == null or n.order > latest.order): latest = n return latest func _ready() -> void: add_to_group(GROUP) func _build_model() -> void: model = create_model(pokemon) add_child(model) static func create_model(p : PokemonInstance) -> Node3D: #modele 3D de l'espece ; capsule coloree selon le type s'il n'y en a pas var result : Node3D = null var scene : PackedScene = p.species.model_scene if scene: result = scene.instantiate() as Node3D if result == null: result = _create_placeholder(p) return result static func _create_placeholder(p : PokemonInstance) -> Node3D: var colors : Array[Color] = TypeColors.for_species(p.species) var body := MeshInstance3D.new() var capsule := CapsuleMesh.new() capsule.radius = 0.3 capsule.height = 1.0 body.mesh = capsule body.position.y = 0.5 body.material_override = _make_material(colors[0]) #deuxieme type : anneau autour de la capsule if colors.size() > 1: var ring := MeshInstance3D.new() var torus := TorusMesh.new() torus.inner_radius = 0.29 torus.outer_radius = 0.36 ring.mesh = torus ring.material_override = _make_material(colors[1]) body.add_child(ring) return body static func _make_material(color : Color) -> StandardMaterial3D: var mat := StandardMaterial3D.new() mat.albedo_color = color mat.roughness = 0.8 return mat func recall() -> void: recalled.emit() queue_free()