diff --git a/data/pokemon_data.gd b/data/pokemon_data.gd index 4f4ffde..c981c63 100644 --- a/data/pokemon_data.gd +++ b/data/pokemon_data.gd @@ -19,6 +19,7 @@ extends Resource @export var shiny_albedo : Texture2D @export var learnset : Array[LearnedMove] = [] @export var cry_latest : AudioStream +@export var forms : Array[PokemonForm] = [] func get_display_name() -> String: return L10n.pick(names, api_name.capitalize()) diff --git a/data/pokemon_form.gd b/data/pokemon_form.gd new file mode 100644 index 0000000..5b08203 --- /dev/null +++ b/data/pokemon_form.gd @@ -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 diff --git a/data/pokemon_form.gd.uid b/data/pokemon_form.gd.uid new file mode 100644 index 0000000..6a7f463 --- /dev/null +++ b/data/pokemon_form.gd.uid @@ -0,0 +1 @@ +uid://fmgdjhptqo0d diff --git a/editor/pokemon_importer.gd b/editor/pokemon_importer.gd index 7eb551f..ec4129e 100644 --- a/editor/pokemon_importer.gd +++ b/editor/pokemon_importer.gd @@ -46,45 +46,76 @@ func _fetch_pokemon(token : Variant) -> void: var stem := PokeApiSettings.pokemon_stem(int(j["id"]), poke_name) var path : String = PokeApiSettings.pokemon_dir() + stem + ".tres" 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 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() d.id = j["id"] - d.api_name = poke_name + d.api_name = j["name"] d.height = j["height"] / 10.0 d.weight = j["weight"] / 10.0 - for t in j["types"]: - d.types = _resolve_types(j["types"]) - for a in j["abilities"]: - d.abilities = _build_abilities(j["abilities"]) + d.types = _resolve_types(j["types"]) + d.abilities = _build_abilities(j["abilities"]) for s in j["stats"]: 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: d.names = PokeApiText.names(species["names"], _languages) d.descriptions = PokeApiText.flavors(species["flavor_text_entries"], _languages) if species.get("evolution_chain") != null: d.evolution_chain = await _evolution.get_or_build(species["evolution_chain"]["url"]) - + var sprites : Dictionary = j["sprites"] var art : Dictionary = sprites.get("other", {}).get("official-artwork", {}) var sprite_url = art.get("front_default", sprites.get("front_default")) 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 cry_path := await _client.download_file(cries.get("latest"), PokeApiSettings.cries_dir() + stem + ".ogg") - - _pending.append({ + + return { "data": d, "path": path, "moves": j["moves"], - "png": await _client.download_file(sprite_url, dir + stem + ".png"), - "shiny_png": await _client.download_file(shiny_url, dir + stem + "_shiny.png"), - "cry": cry_path, - }) + "png": await _client.download_file(sprite_url, sdir + stem + ".png"), + "shiny_png": await _client.download_file(shiny_url, sdir + stem + "_shiny.png"), + "cry": await _client.download_file(cries.get("latest"), PokeApiSettings.cries_dir() + stem + ".ogg"), + } func _save_all() -> void: var to_reimport := PackedStringArray() @@ -97,7 +128,7 @@ func _save_all() -> void: await _client.get_tree().process_frame _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: var d : PokemonData = p["data"] d.menu_sprite = _load_texture(p["png"]) @@ -105,12 +136,41 @@ func _save_all() -> void: d.cry_latest = _load_audio(p["cry"]) _link_models(d) 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"]) - saved.append(d) - message.emit("OK : %s (%d capacités)" % [d.get_display_name(), d.learnset.size()]) + message.emit("OK : %s (%d capacités, %d formes)" % [d.get_display_name(), d.learnset.size(), d.forms.size()]) _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: if path.is_empty(): return null return ResourceLoader.load(path, "Texture2D", ResourceLoader.CACHE_MODE_REPLACE) as Texture2D