Tales-of-PKMN/game/core/flag_manager.gd

202 lines
6.7 KiB
GDScript

extends Node
## Gestionnaire global de flags de progression.
## Un flag = un fait du monde identifié par un StringName.
##
## Types supportés : bool, int, String, float, Variant (fallback).
##
## Écriture : set_flag(), set_flag_once(), increment_flag()
## Lecture : get_flag(), has_flag(), is_flag_true()
## Conditions : evaluate_condition() pour tester des conditions composées.
# Stockage : flag_name (StringName) -> valeur (Variant)
var _flags: Dictionary = {}
# Flags marqués "one-shot" (ne peuvent être modifiés qu'une fois)
var _one_shot_flags: Dictionary = {} # flag_name -> true
# --- Écriture ---
## Définit un flag à une valeur. Écrase toujours.
func set_flag(flag_name: StringName, value: Variant) -> void:
var old_value: Variant = _flags.get(flag_name, null)
if old_value == value:
return
_flags[flag_name] = value
GameEvents.flag_changed.emit(flag_name, old_value, value)
_emit_legacy_signal(flag_name, value)
## Définit un flag uniquement s'il n'a jamais été défini.
## Retourne true si le flag a effectivement été posé.
func set_flag_once(flag_name: StringName, value: Variant = true) -> bool:
if flag_name in _flags:
return false
set_flag(flag_name, value)
_one_shot_flags[flag_name] = true
return true
## Incrémente un flag numérique. Crée le flag à 0 si inexistant.
func increment_flag(flag_name: StringName, amount: int = 1) -> void:
var current: int = int(_flags.get(flag_name, 0))
set_flag(flag_name, current + amount)
## Décrémente un flag numérique (utile pour consommation de ressources).
func decrement_flag(flag_name: StringName, amount: int = 1) -> void:
increment_flag(flag_name, -amount)
## Supprime un flag (à utiliser avec prudence en dev).
func clear_flag(flag_name: StringName) -> void:
if flag_name in _flags:
var old_value: Variant = _flags[flag_name]
_flags.erase(flag_name)
GameEvents.flag_changed.emit(flag_name, old_value, null)
# --- Lecture ---
func get_flag(flag_name: StringName, default: Variant = null) -> Variant:
return _flags.get(flag_name, default)
func has_flag(flag_name: StringName) -> bool:
return flag_name in _flags
## Retourne true si le flag existe ET vaut `true` (booléen).
## Pour un int, retourne true si non-zéro.
func is_flag_true(flag_name: StringName) -> bool:
if not flag_name in _flags:
return false
var v: Variant = _flags[flag_name]
if v is bool:
return v
if v is int or v is float:
return v != 0
return v != null
func get_int(flag_name: StringName, default: int = 0) -> int:
return int(_flags.get(flag_name, default))
func get_bool(flag_name: StringName, default: bool = false) -> bool:
return bool(_flags.get(flag_name, default))
func get_string(flag_name: StringName, default: String = "") -> String:
return String(_flags.get(flag_name, default))
# --- Conditions composées ---
## Évalue un dictionnaire de conditions :
## {
## &"prof_rencontre": true, # flag == true
## &"badges_gagnes": 3, # flag >= 3 (int)
## &"starter_choisi": "pikachu", # flag == "pikachu"
## }
## Retourne true si TOUTES les conditions sont satisfaites.
func evaluate_condition(conditions: Dictionary) -> bool:
for flag_name in conditions:
var expected: Variant = conditions[flag_name]
var actual: Variant = _flags.get(flag_name, null)
# Booléen attendu : on teste la vérité (marche pour bool et int)
if expected is bool:
if is_flag_true(flag_name) != expected:
return false
# Int attendu : on teste >= (utile pour les compteurs)
elif expected is int:
if int(actual if actual != null else 0) < expected:
return false
# String / autre : égalité stricte
else:
if actual != expected:
return false
return true
## Version "au moins une condition vraie" (OR).
func evaluate_any_condition(conditions: Dictionary) -> bool:
for flag_name in conditions:
var expected: Variant = conditions[flag_name]
var actual: Variant = _flags.get(flag_name, null)
if expected is bool:
if is_flag_true(flag_name) == expected:
return true
elif expected is int:
if int(actual if actual != null else 0) >= expected:
return true
else:
if actual == expected:
return true
return false
# --- Signaux spécifiques (optionnel mais pratique) ---
## Émet des signaux dédiés pour certains flags pour simplifier les connexions.
## Exemple : GameEvents.badge_gagne.emit() si le flag "badges_gagnes" change.
func _emit_legacy_signal(flag_name: StringName, value: Variant) -> void:
# Ajoute ici tes mappings custom si tu veux des signaux dédiés.
# Ex :
# if flag_name == &"badges_gagnes":
# GameEvents.badge_count_changed.emit(int(value))
pass
# --- Connexion automatique aux événements du jeu ---
func _ready() -> void:
_connect_gameplay_events()
func _connect_gameplay_events() -> void:
GameEvents.pokemon_captured.connect(_on_pokemon_captured)
GameEvents.pokemon_defeated.connect(_on_pokemon_defeated)
GameEvents.item_collected.connect(_on_item_collected)
GameEvents.npc_talked.connect(_on_npc_talked)
GameEvents.location_reached.connect(_on_location_reached)
func _on_pokemon_captured(species_id: StringName) -> void:
increment_flag(&"nb_pokemon_captures", 1)
set_flag_once(&"premier_pokemon_capture", true)
# Flag spécifique par espèce : ex. "capture_chenipan" = true
set_flag(StringName("capture_%s" % species_id), true)
increment_flag(StringName("nb_captures_%s" % species_id), 1)
func _on_pokemon_defeated(species_id: StringName) -> void:
increment_flag(&"nb_pokemon_vaincus", 1)
increment_flag(StringName("nb_vaincus_%s" % species_id), 1)
func _on_item_collected(item_id: StringName, amount: int) -> void:
increment_flag(StringName("item_%s" % item_id), amount)
func _on_npc_talked(npc_id: StringName) -> void:
set_flag(StringName("talked_%s" % npc_id), true)
increment_flag(StringName("nb_dialogues_%s" % npc_id), 1)
func _on_location_reached(location_id: StringName) -> void:
set_flag(StringName("visited_%s" % location_id), true)
# --- Sauvegarde / Chargement ---
func get_save_data() -> Dictionary:
return {
"flags": _flags.duplicate(true),
"one_shot": _one_shot_flags.duplicate(),
}
func load_save_data(data: Dictionary) -> void:
_flags = data.get("flags", {}).duplicate(true)
_one_shot_flags = data.get("one_shot", {}).duplicate()
# --- Debug ---
func debug_print_all() -> void:
print("\n=== FLAGS ===")
if _flags.is_empty():
print("(aucun flag)")
else:
var keys: Array = _flags.keys()
keys.sort()
for flag_name in keys:
var v: Variant = _flags[flag_name]
var type_str := type_string(typeof(v))
var one_shot := " [one-shot]" if flag_name in _one_shot_flags else ""
print(" %s = %s (%s)%s" % [flag_name, str(v), type_str, one_shot])
print("=============\n")
func debug_clear_all() -> void:
_flags.clear()
_one_shot_flags.clear()
print("[FlagManager] Tous les flags ont été effacés.")