Initial commit
This commit is contained in:
commit
47dbea8ea4
15
data/l10n.gd
Normal file
15
data/l10n.gd
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# data/l10n.gd
|
||||
@tool
|
||||
class_name L10n
|
||||
extends RefCounted
|
||||
|
||||
const FALLBACK := "en"
|
||||
|
||||
static func current_lang() -> String:
|
||||
return TranslationServer.get_locale().substr(0, 2)
|
||||
|
||||
static func pick(texts : Dictionary, default : String = "") -> String:
|
||||
var lang := current_lang()
|
||||
if texts.has(lang): return String(texts[lang])
|
||||
if texts.has(FALLBACK): return String(texts[FALLBACK])
|
||||
return default
|
||||
1
data/l10n.gd.uid
Normal file
1
data/l10n.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://byeysk5pnt16v
|
||||
8
data/learned_move.gd
Normal file
8
data/learned_move.gd
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# data/learned_move.gd
|
||||
@tool
|
||||
class_name LearnedMove
|
||||
extends Resource
|
||||
|
||||
@export var move : MoveData
|
||||
@export var level : int
|
||||
@export var method : String
|
||||
1
data/learned_move.gd.uid
Normal file
1
data/learned_move.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://dxpd7ihc8eew5
|
||||
22
data/move_data.gd
Normal file
22
data/move_data.gd
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# data/move_data.gd
|
||||
@tool
|
||||
class_name MoveData
|
||||
extends Resource
|
||||
|
||||
@export var id : int
|
||||
@export var api_name : String
|
||||
@export var names : Dictionary = {} # {"fr": "Éclair", "en": "Thunder Shock"}
|
||||
@export var descriptions : Dictionary = {}
|
||||
@export var type : String
|
||||
@export var category : String # "physical", "special", "status"
|
||||
@export var power : int
|
||||
@export var accuracy : int
|
||||
@export var pp : int
|
||||
@export var priority : int
|
||||
@export var target : String
|
||||
|
||||
func get_display_name() -> String:
|
||||
return L10n.pick(names, api_name.capitalize())
|
||||
|
||||
func get_description() -> String:
|
||||
return L10n.pick(descriptions)
|
||||
1
data/move_data.gd.uid
Normal file
1
data/move_data.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://chc1mf57j5m4p
|
||||
28
data/pokemon_data.gd
Normal file
28
data/pokemon_data.gd
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# data/pokemon_data.gd
|
||||
@tool
|
||||
class_name PokemonData
|
||||
extends Resource
|
||||
|
||||
@export var id : int
|
||||
@export var api_name : String
|
||||
@export var names : Dictionary = {}
|
||||
@export var descriptions : Dictionary = {}
|
||||
@export var types : PackedStringArray
|
||||
@export var abilities : PackedStringArray
|
||||
@export var base_stats : Dictionary = {}
|
||||
@export var height : float
|
||||
@export var weight : float
|
||||
@export var menu_sprite : Texture2D
|
||||
@export var shiny_menu_sprite : Texture2D
|
||||
@export var model_scene : PackedScene
|
||||
@export var shiny_albedo : Texture2D
|
||||
@export var learnset : Array[LearnedMove] = []
|
||||
|
||||
func get_display_name() -> String:
|
||||
return L10n.pick(names, api_name.capitalize())
|
||||
|
||||
func get_description() -> String:
|
||||
return L10n.pick(descriptions)
|
||||
|
||||
func get_menu_sprite(shiny : bool) -> Texture2D:
|
||||
return shiny_menu_sprite if shiny and shiny_menu_sprite else menu_sprite
|
||||
1
data/pokemon_data.gd.uid
Normal file
1
data/pokemon_data.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://cwmxfian0pdjk
|
||||
19
editor/api_text.gd
Normal file
19
editor/api_text.gd
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# editor/api_text.gd
|
||||
@tool
|
||||
class_name PokeApiText
|
||||
extends RefCounted
|
||||
|
||||
static func names(entries : Array, languages : PackedStringArray) -> Dictionary:
|
||||
var out := {}
|
||||
for n in entries:
|
||||
var lang : String = n["language"]["name"]
|
||||
if lang in languages: out[lang] = n["name"]
|
||||
return out
|
||||
|
||||
static func flavors(entries : Array, languages : PackedStringArray) -> Dictionary:
|
||||
var out := {}
|
||||
for e in entries: # les plus récentes sont à la fin et écrasent les anciennes
|
||||
var lang : String = e["language"]["name"]
|
||||
if lang in languages:
|
||||
out[lang] = String(e["flavor_text"]).replace("\n", " ").replace("\f", " ")
|
||||
return out
|
||||
1
editor/api_text.gd.uid
Normal file
1
editor/api_text.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://cchtb0yvmqssy
|
||||
100
editor/importer_dock.gd
Normal file
100
editor/importer_dock.gd
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
# editor/importer_dock.gd (interface uniquement)
|
||||
@tool
|
||||
extends VBoxContainer
|
||||
|
||||
var editor_filesystem : EditorFileSystem
|
||||
|
||||
var _client : PokeApiClient
|
||||
var _input : LineEdit
|
||||
var _overwrite : CheckBox
|
||||
var _buttons : Array[Button] = []
|
||||
var _progress : ProgressBar
|
||||
var _log : RichTextLabel
|
||||
var _busy : bool = false
|
||||
|
||||
func _ready() -> void:
|
||||
custom_minimum_size = Vector2(260, 0)
|
||||
|
||||
_client = PokeApiClient.new()
|
||||
_client.progress.connect(_on_progress)
|
||||
add_child(_client)
|
||||
|
||||
_add_button("Importer toutes les capacités", _on_import_moves)
|
||||
add_child(HSeparator.new())
|
||||
|
||||
var label := Label.new()
|
||||
label.text = "Pokémon (ids, noms, plages) :"
|
||||
add_child(label)
|
||||
|
||||
_input = LineEdit.new()
|
||||
_input.placeholder_text = "25, eevee, 1-9, mr-mime"
|
||||
add_child(_input)
|
||||
|
||||
_overwrite = CheckBox.new()
|
||||
_overwrite.text = "Écraser les fichiers existants"
|
||||
add_child(_overwrite)
|
||||
|
||||
_add_button("Importer les pokémon", _on_import_pokemon)
|
||||
|
||||
_progress = ProgressBar.new()
|
||||
add_child(_progress)
|
||||
|
||||
_log = RichTextLabel.new()
|
||||
_log.custom_minimum_size = Vector2(0, 200)
|
||||
_log.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
_log.scroll_following = true
|
||||
_log.selection_enabled = true
|
||||
add_child(_log)
|
||||
|
||||
func _add_button(text : String, callback : Callable) -> void:
|
||||
var b := Button.new()
|
||||
b.text = text
|
||||
b.pressed.connect(callback)
|
||||
add_child(b)
|
||||
_buttons.append(b)
|
||||
|
||||
func _on_import_moves() -> void:
|
||||
if _busy: return
|
||||
_set_busy(true)
|
||||
var importer := PokeApiMoveImporter.new(_client, _overwrite.button_pressed)
|
||||
importer.message.connect(_print)
|
||||
await importer.run()
|
||||
editor_filesystem.scan()
|
||||
_set_busy(false)
|
||||
|
||||
func _on_import_pokemon() -> void:
|
||||
if _busy: return
|
||||
var tokens := _parse_input(_input.text)
|
||||
if tokens.is_empty():
|
||||
_print("Rien à importer.")
|
||||
return
|
||||
_set_busy(true)
|
||||
var importer := PokeApiPokemonImporter.new(_client, editor_filesystem, _overwrite.button_pressed)
|
||||
importer.message.connect(_print)
|
||||
await importer.run(tokens)
|
||||
_set_busy(false)
|
||||
|
||||
func _parse_input(text : String) -> Array:
|
||||
var out : Array = []
|
||||
for tok in text.split(",", false):
|
||||
var t : String = tok.strip_edges().to_lower()
|
||||
if t.is_empty(): continue
|
||||
var parts := t.split("-")
|
||||
if parts.size() == 2 and parts[0].is_valid_int() and parts[1].is_valid_int():
|
||||
for i in range(int(parts[0]), int(parts[1]) + 1): out.append(i)
|
||||
elif t.is_valid_int():
|
||||
out.append(int(t))
|
||||
else:
|
||||
out.append(t)
|
||||
return out
|
||||
|
||||
func _on_progress(done : int, total : int) -> void:
|
||||
_progress.max_value = total
|
||||
_progress.value = done
|
||||
|
||||
func _set_busy(value : bool) -> void:
|
||||
_busy = value
|
||||
for b in _buttons: b.disabled = value
|
||||
|
||||
func _print(msg : String) -> void:
|
||||
_log.append_text(msg + "\n")
|
||||
1
editor/importer_dock.gd.uid
Normal file
1
editor/importer_dock.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://dm018deoynvvc
|
||||
56
editor/move_importer.gd
Normal file
56
editor/move_importer.gd
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# editor/move_importer.gd
|
||||
@tool
|
||||
class_name PokeApiMoveImporter
|
||||
extends RefCounted
|
||||
|
||||
signal message(text : String)
|
||||
|
||||
var _client : PokeApiClient
|
||||
var _overwrite : bool
|
||||
var _languages : PackedStringArray
|
||||
var _dir : String
|
||||
|
||||
func _init(client : PokeApiClient, overwrite : bool) -> void:
|
||||
_client = client
|
||||
_overwrite = overwrite
|
||||
_languages = PokeApiSettings.languages()
|
||||
_dir = PokeApiSettings.moves_dir()
|
||||
|
||||
func run() -> void:
|
||||
DirAccess.make_dir_recursive_absolute(_dir)
|
||||
var list = await _client.get_json(PokeApiClient.API + "move?limit=2000")
|
||||
if list == null:
|
||||
message.emit("[color=red]Impossible de récupérer la liste des capacités.[/color]")
|
||||
return
|
||||
|
||||
var entries : Array = []
|
||||
for r in list["results"]:
|
||||
var id : int = int(String(r["url"]).trim_suffix("/").get_file())
|
||||
if id < 10000: entries.append(r) # ignore les capacités "shadow" (Colosseum/XD)
|
||||
|
||||
message.emit("%d capacités à traiter..." % entries.size())
|
||||
await _client.run_parallel(entries, _import_move)
|
||||
message.emit("Capacités terminées.")
|
||||
|
||||
func _import_move(entry : Dictionary) -> void:
|
||||
var path : String = _dir + entry["name"] + ".tres"
|
||||
if !_overwrite and FileAccess.file_exists(path): return
|
||||
|
||||
var j = await _client.get_json(entry["url"])
|
||||
if j == null:
|
||||
message.emit("[color=red]Échec : %s[/color]" % entry["name"])
|
||||
return
|
||||
|
||||
var m := MoveData.new()
|
||||
m.id = j["id"]
|
||||
m.api_name = j["name"]
|
||||
m.names = PokeApiText.names(j["names"], _languages)
|
||||
m.descriptions = PokeApiText.flavors(j["flavor_text_entries"], _languages)
|
||||
m.type = j["type"]["name"]
|
||||
m.category = j["damage_class"]["name"]
|
||||
m.power = int(j["power"]) if j["power"] != null else 0
|
||||
m.accuracy = int(j["accuracy"]) if j["accuracy"] != null else 0
|
||||
m.pp = int(j["pp"]) if j["pp"] != null else 0
|
||||
m.priority = j["priority"]
|
||||
m.target = j["target"]["name"]
|
||||
ResourceSaver.save(m, path)
|
||||
1
editor/move_importer.gd.uid
Normal file
1
editor/move_importer.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://bh51lh1reyxog
|
||||
63
editor/pokeapi_client.gd
Normal file
63
editor/pokeapi_client.gd
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
# editor/pokeapi_client.gd
|
||||
@tool
|
||||
class_name PokeApiClient
|
||||
extends Node
|
||||
|
||||
signal progress(done : int, total : int)
|
||||
|
||||
const API := "https://pokeapi.co/api/v2/"
|
||||
|
||||
var _in_flight : int = 0
|
||||
var _done : int = 0
|
||||
var _total : int = 0
|
||||
|
||||
func get_bytes(url : String) -> PackedByteArray:
|
||||
for attempt in 3:
|
||||
var http := HTTPRequest.new()
|
||||
add_child(http)
|
||||
if http.request(url) != OK:
|
||||
http.queue_free()
|
||||
await get_tree().create_timer(1.0).timeout
|
||||
continue
|
||||
var res : Array = await http.request_completed
|
||||
http.queue_free()
|
||||
if res[0] == HTTPRequest.RESULT_SUCCESS and res[1] == 200:
|
||||
return res[3]
|
||||
if res[1] == 404: break
|
||||
await get_tree().create_timer(1.0).timeout
|
||||
return PackedByteArray()
|
||||
|
||||
func get_json(url : String) -> Variant:
|
||||
var bytes := await get_bytes(url)
|
||||
if bytes.is_empty(): return null
|
||||
return JSON.parse_string(bytes.get_string_from_utf8())
|
||||
|
||||
## Télécharge une URL vers un fichier. Retourne le chemin, ou "" en cas d'échec.
|
||||
func download_file(url : Variant, path : String) -> String:
|
||||
if url == null: return ""
|
||||
var bytes := await get_bytes(String(url))
|
||||
if bytes.is_empty(): return ""
|
||||
var f := FileAccess.open(path, FileAccess.WRITE)
|
||||
if f == null: return ""
|
||||
f.store_buffer(bytes)
|
||||
f.close()
|
||||
return path
|
||||
|
||||
## Lance worker(item) pour chaque élément, avec un nombre limité de tâches simultanées.
|
||||
func run_parallel(items : Array, worker : Callable) -> void:
|
||||
_done = 0
|
||||
_total = items.size()
|
||||
progress.emit(0, _total)
|
||||
for item in items:
|
||||
while _in_flight >= PokeApiSettings.max_concurrent():
|
||||
await get_tree().process_frame
|
||||
_in_flight += 1
|
||||
_run_task(worker, item)
|
||||
while _in_flight > 0:
|
||||
await get_tree().process_frame
|
||||
|
||||
func _run_task(worker : Callable, item : Variant) -> void:
|
||||
await worker.call(item)
|
||||
_in_flight -= 1
|
||||
_done += 1
|
||||
progress.emit(_done, _total)
|
||||
1
editor/pokeapi_client.gd.uid
Normal file
1
editor/pokeapi_client.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://cl808n66wkjwr
|
||||
125
editor/pokemon_importer.gd
Normal file
125
editor/pokemon_importer.gd
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
# editor/pokemon_importer.gd
|
||||
@tool
|
||||
class_name PokeApiPokemonImporter
|
||||
extends RefCounted
|
||||
|
||||
signal message(text : String)
|
||||
|
||||
var _client : PokeApiClient
|
||||
var _fs : EditorFileSystem
|
||||
var _overwrite : bool
|
||||
var _languages : PackedStringArray
|
||||
var _pending : Array = [] # pokémon récupérés, en attente de sauvegarde
|
||||
|
||||
func _init(client : PokeApiClient, fs : EditorFileSystem, overwrite : bool) -> void:
|
||||
_client = client
|
||||
_fs = fs
|
||||
_overwrite = overwrite
|
||||
_languages = PokeApiSettings.languages()
|
||||
|
||||
func run(tokens : Array) -> void:
|
||||
DirAccess.make_dir_recursive_absolute(PokeApiSettings.pokemon_dir())
|
||||
DirAccess.make_dir_recursive_absolute(PokeApiSettings.sprites_dir())
|
||||
if !DirAccess.dir_exists_absolute(PokeApiSettings.moves_dir()):
|
||||
message.emit("[color=orange]Aucune capacité importée : les learnsets seront vides.[/color]")
|
||||
|
||||
_pending.clear()
|
||||
await _client.run_parallel(tokens, _fetch_pokemon) # phase 1 : téléchargement
|
||||
await _save_all() # phase 2 : sprites + .tres
|
||||
_pending.clear()
|
||||
message.emit("Pokémon terminés.")
|
||||
|
||||
func _fetch_pokemon(token : Variant) -> void:
|
||||
var j = await _client.get_json(PokeApiClient.API + "pokemon/" + str(token))
|
||||
if j == null:
|
||||
message.emit("[color=red]Introuvable : %s[/color]" % str(token))
|
||||
return
|
||||
|
||||
var poke_name : String = j["name"]
|
||||
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)
|
||||
return
|
||||
|
||||
var species = await _client.get_json(j["species"]["url"])
|
||||
|
||||
var d := PokemonData.new()
|
||||
d.id = j["id"]
|
||||
d.api_name = poke_name
|
||||
d.height = j["height"] / 10.0
|
||||
d.weight = j["weight"] / 10.0
|
||||
for t in j["types"]:
|
||||
d.types.append(t["type"]["name"])
|
||||
for a in j["abilities"]:
|
||||
d.abilities.append(a["ability"]["name"])
|
||||
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
|
||||
if species != null:
|
||||
d.names = PokeApiText.names(species["names"], _languages)
|
||||
d.descriptions = PokeApiText.flavors(species["flavor_text_entries"], _languages)
|
||||
|
||||
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()
|
||||
|
||||
_pending.append({
|
||||
"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"),
|
||||
})
|
||||
|
||||
func _save_all() -> void:
|
||||
var pngs := PackedStringArray()
|
||||
for p in _pending:
|
||||
for key in ["png", "shiny_png"]:
|
||||
if p[key] != "": pngs.append(p[key])
|
||||
if !pngs.is_empty():
|
||||
_fs.scan()
|
||||
while _fs.is_scanning():
|
||||
await _client.get_tree().process_frame
|
||||
_fs.reimport_files(pngs)
|
||||
|
||||
for p in _pending:
|
||||
var d : PokemonData = p["data"]
|
||||
d.menu_sprite = _load_texture(p["png"])
|
||||
d.shiny_menu_sprite = _load_texture(p["shiny_png"])
|
||||
_link_models(d)
|
||||
d.learnset = _build_learnset(p["moves"])
|
||||
ResourceSaver.save(d, p["path"])
|
||||
message.emit("OK : %s (%d capacités)" % [d.get_display_name(), d.learnset.size()])
|
||||
|
||||
_fs.scan()
|
||||
|
||||
func _load_texture(path : String) -> Texture2D:
|
||||
if path.is_empty(): return null
|
||||
return ResourceLoader.load(path, "Texture2D", ResourceLoader.CACHE_MODE_REPLACE) as Texture2D
|
||||
|
||||
func _link_models(d : PokemonData) -> void:
|
||||
var dir := PokeApiSettings.models_dir()
|
||||
if dir.is_empty(): return
|
||||
var model_path := "%s/%s.glb" % [dir, d.api_name]
|
||||
if ResourceLoader.exists(model_path):
|
||||
d.model_scene = load(model_path) as PackedScene
|
||||
var tex_path := "%s/%s_shiny.png" % [dir, d.api_name]
|
||||
if ResourceLoader.exists(tex_path):
|
||||
d.shiny_albedo = load(tex_path) as Texture2D
|
||||
|
||||
func _build_learnset(raw_moves : Array) -> Array[LearnedMove]:
|
||||
var result : Array[LearnedMove] = []
|
||||
for m in raw_moves:
|
||||
var move_path : String = PokeApiSettings.moves_dir() + String(m["move"]["name"]) + ".tres"
|
||||
if !FileAccess.file_exists(move_path): continue
|
||||
var details : Array = m["version_group_details"]
|
||||
var last : Dictionary = details[details.size() - 1] # jeu le plus récent
|
||||
var lm := LearnedMove.new()
|
||||
lm.move = load(move_path) as MoveData
|
||||
lm.level = last["level_learned_at"]
|
||||
lm.method = last["move_learn_method"]["name"]
|
||||
result.append(lm)
|
||||
result.sort_custom(func(a : LearnedMove, b : LearnedMove) -> bool: return a.level < b.level)
|
||||
return result
|
||||
1
editor/pokemon_importer.gd.uid
Normal file
1
editor/pokemon_importer.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://bhq06l4h5f024
|
||||
7
plugin.cfg
Normal file
7
plugin.cfg
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
; plugin.cfg
|
||||
[plugin]
|
||||
name="PokeAPI Importer"
|
||||
description="Importe pokémon et capacités depuis PokeAPI en ressources .tres"
|
||||
author=""
|
||||
version="1.0"
|
||||
script="plugin.gd"
|
||||
15
plugin.gd
Normal file
15
plugin.gd
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
@tool
|
||||
extends EditorPlugin
|
||||
|
||||
var _dock : Control
|
||||
|
||||
func _enter_tree() -> void:
|
||||
PokeApiSettings.register()
|
||||
_dock = preload("editor/importer_dock.gd").new()
|
||||
_dock.name = "PokeAPI"
|
||||
_dock.editor_filesystem = EditorInterface.get_resource_filesystem()
|
||||
add_control_to_dock(DOCK_SLOT_RIGHT_UL, _dock)
|
||||
|
||||
func _exit_tree() -> void:
|
||||
remove_control_from_docks(_dock)
|
||||
_dock.queue_free()
|
||||
1
plugin.gd.uid
Normal file
1
plugin.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://b5wcxsx45dxd4
|
||||
57
settings.gd
Normal file
57
settings.gd
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
# settings.gd
|
||||
@tool
|
||||
class_name PokeApiSettings
|
||||
extends RefCounted
|
||||
|
||||
const KEY_DATA_DIR := "pokeapi_importer/data_dir"
|
||||
const KEY_LANGUAGES := "pokeapi_importer/languages"
|
||||
const KEY_MODELS_DIR := "pokeapi_importer/models_dir"
|
||||
const KEY_MAX_CONCURRENT := "pokeapi_importer/max_concurrent_requests"
|
||||
|
||||
const DEFAULT_DATA_DIR := "res://data/pokeapi"
|
||||
const DEFAULT_MODELS_DIR := ""
|
||||
const DEFAULT_MAX_CONCURRENT := 8
|
||||
|
||||
static func default_languages() -> PackedStringArray:
|
||||
return PackedStringArray(["fr", "en"])
|
||||
|
||||
static func register() -> void:
|
||||
_add(KEY_DATA_DIR, DEFAULT_DATA_DIR, TYPE_STRING, PROPERTY_HINT_DIR)
|
||||
_add(KEY_LANGUAGES, default_languages(), TYPE_PACKED_STRING_ARRAY)
|
||||
_add(KEY_MODELS_DIR, DEFAULT_MODELS_DIR, TYPE_STRING, PROPERTY_HINT_DIR)
|
||||
_add(KEY_MAX_CONCURRENT, DEFAULT_MAX_CONCURRENT, TYPE_INT, PROPERTY_HINT_RANGE, "1,16")
|
||||
|
||||
static func _add(key : String, default : Variant, type : int,
|
||||
hint : int = PROPERTY_HINT_NONE, hint_string : String = "") -> void:
|
||||
if !ProjectSettings.has_setting(key):
|
||||
ProjectSettings.set_setting(key, default)
|
||||
ProjectSettings.set_initial_value(key, default)
|
||||
ProjectSettings.add_property_info({
|
||||
"name": key, "type": type, "hint": hint, "hint_string": hint_string})
|
||||
|
||||
static func data_dir() -> String:
|
||||
return String(ProjectSettings.get_setting(KEY_DATA_DIR, DEFAULT_DATA_DIR)).trim_suffix("/")
|
||||
|
||||
static func languages() -> PackedStringArray:
|
||||
return PackedStringArray(ProjectSettings.get_setting(KEY_LANGUAGES, default_languages()))
|
||||
|
||||
static func models_dir() -> String:
|
||||
return String(ProjectSettings.get_setting(KEY_MODELS_DIR, DEFAULT_MODELS_DIR)).trim_suffix("/")
|
||||
|
||||
static func max_concurrent() -> int:
|
||||
return int(ProjectSettings.get_setting(KEY_MAX_CONCURRENT, DEFAULT_MAX_CONCURRENT))
|
||||
|
||||
# chemins dérivés (avec "/" final pour les dossiers)
|
||||
static func moves_dir() -> String: return data_dir() + "/moves/"
|
||||
static func pokemon_dir() -> String: return data_dir() + "/pokemon/"
|
||||
static func sprites_dir() -> String: return data_dir() + "/sprites/"
|
||||
|
||||
# nommage des fichiers pokémon : "0025_pikachu"
|
||||
static func pokemon_stem(id : int, api_name : String) -> String:
|
||||
return "%04d_%s" % [id, api_name]
|
||||
|
||||
## "0025_pikachu" -> {"id": 25, "name": "pikachu"} ; {} si le format ne correspond pas
|
||||
static func parse_pokemon_stem(stem : String) -> Dictionary:
|
||||
var sep := stem.find("_")
|
||||
if sep <= 0 or !stem.substr(0, sep).is_valid_int(): return {}
|
||||
return {"id": int(stem.substr(0, sep)), "name": stem.substr(sep + 1)}
|
||||
1
settings.gd.uid
Normal file
1
settings.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://dovcv62sbjna
|
||||
Loading…
Reference in a new issue