111 lines
4 KiB
GDScript
111 lines
4 KiB
GDScript
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
|
|
const HEIGHT_TO_METERS : float = 1.0 #PokeAPI donne la taille en decimetres ; mets 1.0 si ton import l'a deja convertie en metres
|
|
const MIN_HEIGHT : float = 0.3 #bornes de la capsule en metres (augmente MAX_HEIGHT pour une echelle fidele aux tres gros pokemon)
|
|
const MAX_HEIGHT : float = 8.0
|
|
const DEFAULT_HEIGHT : float = 1.0 #utilise si la taille n'est pas renseignee (0)
|
|
|
|
static var _counter : int = 0 #ordre d'envoi, pour retrouver le plus recent
|
|
|
|
var pokemon : PokemonInstance
|
|
var model : Node3D
|
|
var order : int = 0
|
|
var rider : Node3D = null
|
|
|
|
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) and n.rider == null:
|
|
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)
|
|
|
|
#taille issue des donnees de l'espece ; le rayon suit la hauteur pour garder des proportions coherentes
|
|
var meters : float = p.species.height * HEIGHT_TO_METERS if p.species != null else 0.0
|
|
var height : float = clampf(meters if meters > 0.0 else DEFAULT_HEIGHT, MIN_HEIGHT, MAX_HEIGHT)
|
|
var radius : float = clampf(height * 0.3, 0.12, 1.5)
|
|
|
|
var body := MeshInstance3D.new()
|
|
var capsule := CapsuleMesh.new()
|
|
capsule.radius = radius
|
|
capsule.height = maxf(height, radius * 2.0) #une capsule ne peut pas etre plus basse que son diametre
|
|
body.mesh = capsule
|
|
body.position.y = capsule.height * 0.5 #les pieds posent sur le sol
|
|
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 = radius * 0.97
|
|
torus.outer_radius = radius * 1.2
|
|
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()
|