Add pokemon temporary forms handling

This commit is contained in:
TheThomaas 2026-09-30 15:07:33 +02:00
parent ad28672093
commit 5fc269b35b
4 changed files with 90 additions and 20 deletions

View file

@ -19,6 +19,7 @@ extends Resource
@export var shiny_albedo : Texture2D @export var shiny_albedo : Texture2D
@export var learnset : Array[LearnedMove] = [] @export var learnset : Array[LearnedMove] = []
@export var cry_latest : AudioStream @export var cry_latest : AudioStream
@export var forms : Array[PokemonForm] = []
func get_display_name() -> String: func get_display_name() -> String:
return L10n.pick(names, api_name.capitalize()) return L10n.pick(names, api_name.capitalize())

8
data/pokemon_form.gd Normal file
View file

@ -0,0 +1,8 @@
# data/pokemon_form.gd
@tool
class_name PokemonForm
extends Resource
@export var kind : String
@export var required_item : String
@export var variant : PokemonData

1
data/pokemon_form.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://fmgdjhptqo0d

View file

@ -46,45 +46,76 @@ func _fetch_pokemon(token : Variant) -> void:
var stem := PokeApiSettings.pokemon_stem(int(j["id"]), poke_name) var stem := PokeApiSettings.pokemon_stem(int(j["id"]), poke_name)
var path : String = PokeApiSettings.pokemon_dir() + stem + ".tres" var path : String = PokeApiSettings.pokemon_dir() + stem + ".tres"
if !_overwrite and FileAccess.file_exists(path): if !_overwrite and FileAccess.file_exists(path):
message.emit("Déjà présent : %s" % poke_name) message.emit("Déjà présent : %s (coche « Écraser » pour rafraîchir aussi ses formes méga/gigamax)" % poke_name)
return return
var species = await _client.get_json(j["species"]["url"]) var species = await _client.get_json(j["species"]["url"])
var base_index : int = _pending.size()
_pending.append(await _build_entry(j, species, stem, path))
if species == null: return
for v in species.get("varieties", []):
var variety_name : String = v["pokemon"]["name"]
var kind := _form_kind(variety_name)
if kind == "": continue # formes régionales etc. : importe-les directement par leur nom (ex. "raichu-alola")
var jv = await _client.get_json(v["pokemon"]["url"])
if jv == null:
message.emit("[color=red]Forme introuvable : %s[/color]" % variety_name)
continue
var v_stem := PokeApiSettings.pokemon_stem(int(jv["id"]), variety_name)
var v_path := PokeApiSettings.pokemon_dir() + v_stem + ".tres"
var entry := await _build_entry(jv, species, v_stem, v_path)
entry["form_of"] = base_index
entry["form_kind"] = kind
entry["form_item_guess"] = _guess_item(variety_name, kind)
_pending.append(entry)
func _form_kind(variety_name : String) -> String:
if variety_name.ends_with("-mega-x"): return "mega-x"
if variety_name.ends_with("-mega-y"): return "mega-y"
if variety_name.ends_with("-mega"): return "mega"
if variety_name.ends_with("-gmax"): return "gmax"
return ""
func _guess_item(variety_name : String, kind : String) -> String:
if kind == "gmax": return "" # le gigamax ne consomme pas d'objet
var suffix := "-x" if kind == "mega-x" else ("-y" if kind == "mega-y" else "")
return variety_name.trim_suffix("-" + kind) + "ite" + suffix # convention Bulbapedia, approximative
## Construit une entrée _pending (utilisée pour l'espèce de base ET pour chaque forme)
func _build_entry(j : Dictionary, species : Variant, stem : String, path : String) -> Dictionary:
var d := PokemonData.new() var d := PokemonData.new()
d.id = j["id"] d.id = j["id"]
d.api_name = poke_name d.api_name = j["name"]
d.height = j["height"] / 10.0 d.height = j["height"] / 10.0
d.weight = j["weight"] / 10.0 d.weight = j["weight"] / 10.0
for t in j["types"]: d.types = _resolve_types(j["types"])
d.types = _resolve_types(j["types"]) d.abilities = _build_abilities(j["abilities"])
for a in j["abilities"]:
d.abilities = _build_abilities(j["abilities"])
for s in j["stats"]: for s in j["stats"]:
d.base_stats[s["stat"]["name"]] = s["base_stat"] d.base_stats[s["stat"]["name"]] = s["base_stat"]
d.names = {"en": poke_name.capitalize()} # repli si l'espèce est indisponible d.names = {"en": String(j["name"]).capitalize()}
if species != null: if species != null:
d.names = PokeApiText.names(species["names"], _languages) d.names = PokeApiText.names(species["names"], _languages)
d.descriptions = PokeApiText.flavors(species["flavor_text_entries"], _languages) d.descriptions = PokeApiText.flavors(species["flavor_text_entries"], _languages)
if species.get("evolution_chain") != null: if species.get("evolution_chain") != null:
d.evolution_chain = await _evolution.get_or_build(species["evolution_chain"]["url"]) d.evolution_chain = await _evolution.get_or_build(species["evolution_chain"]["url"])
var sprites : Dictionary = j["sprites"] var sprites : Dictionary = j["sprites"]
var art : Dictionary = sprites.get("other", {}).get("official-artwork", {}) var art : Dictionary = sprites.get("other", {}).get("official-artwork", {})
var sprite_url = art.get("front_default", sprites.get("front_default")) var sprite_url = art.get("front_default", sprites.get("front_default"))
var shiny_url = art.get("front_shiny", sprites.get("front_shiny")) var shiny_url = art.get("front_shiny", sprites.get("front_shiny"))
var dir := PokeApiSettings.sprites_dir() var sdir := PokeApiSettings.sprites_dir()
var cries : Dictionary = j.get("cries", {}) var cries : Dictionary = j.get("cries", {})
var cry_path := await _client.download_file(cries.get("latest"), PokeApiSettings.cries_dir() + stem + ".ogg")
return {
_pending.append({
"data": d, "path": path, "moves": j["moves"], "data": d, "path": path, "moves": j["moves"],
"png": await _client.download_file(sprite_url, dir + stem + ".png"), "png": await _client.download_file(sprite_url, sdir + stem + ".png"),
"shiny_png": await _client.download_file(shiny_url, dir + stem + "_shiny.png"), "shiny_png": await _client.download_file(shiny_url, sdir + stem + "_shiny.png"),
"cry": cry_path, "cry": await _client.download_file(cries.get("latest"), PokeApiSettings.cries_dir() + stem + ".ogg"),
}) }
func _save_all() -> void: func _save_all() -> void:
var to_reimport := PackedStringArray() var to_reimport := PackedStringArray()
@ -97,7 +128,7 @@ func _save_all() -> void:
await _client.get_tree().process_frame await _client.get_tree().process_frame
_fs.reimport_files(to_reimport) _fs.reimport_files(to_reimport)
var saved : Array[PokemonData] = [] # étape 1 : sprite/cri/modèle/learnset pour toutes les entrées (bases et formes)
for p in _pending: for p in _pending:
var d : PokemonData = p["data"] var d : PokemonData = p["data"]
d.menu_sprite = _load_texture(p["png"]) d.menu_sprite = _load_texture(p["png"])
@ -105,12 +136,41 @@ func _save_all() -> void:
d.cry_latest = _load_audio(p["cry"]) d.cry_latest = _load_audio(p["cry"])
_link_models(d) _link_models(d)
d.learnset = _build_learnset(p["moves"]) d.learnset = _build_learnset(p["moves"])
# étape 2 : formes d'abord, pour obtenir une référence externe (pas embarquée) depuis la base
for p in _pending:
if !p.has("form_of"): continue
var variant : PokemonData = p["data"]
ResourceSaver.save(variant, p["path"])
var base_d : PokemonData = _pending[p["form_of"]]["data"]
base_d.forms.append(_make_form(base_d, variant, p["form_kind"], p["form_item_guess"]))
# étape 3 : bases (et pokémon sans formes)
for p in _pending:
if p.has("form_of"): continue
var d : PokemonData = p["data"]
ResourceSaver.save(d, p["path"]) ResourceSaver.save(d, p["path"])
saved.append(d) message.emit("OK : %s (%d capacités, %d formes)" % [d.get_display_name(), d.learnset.size(), d.forms.size()])
message.emit("OK : %s (%d capacités)" % [d.get_display_name(), d.learnset.size()])
_fs.scan() _fs.scan()
## Préserve une correction manuelle de required_item faite lors d'un import précédent.
func _make_form(base_d : PokemonData, variant : PokemonData, kind : String, item_guess : String) -> PokemonForm:
var form := PokemonForm.new()
form.kind = kind
form.variant = variant
form.required_item = item_guess
var old_path := PokeApiSettings.pokemon_dir() + PokeApiSettings.pokemon_stem(base_d.id, base_d.api_name) + ".tres"
if FileAccess.file_exists(old_path):
var old := ResourceLoader.load(old_path, "", ResourceLoader.CACHE_MODE_REPLACE) as PokemonData
if old:
for f in old.forms:
if f.kind == kind and f.required_item != "" and f.required_item != item_guess:
form.required_item = f.required_item
break
return form
func _load_texture(path : String) -> Texture2D: func _load_texture(path : String) -> Texture2D:
if path.is_empty(): return null if path.is_empty(): return null
return ResourceLoader.load(path, "Texture2D", ResourceLoader.CACHE_MODE_REPLACE) as Texture2D return ResourceLoader.load(path, "Texture2D", ResourceLoader.CACHE_MODE_REPLACE) as Texture2D