202 lines
6.7 KiB
GDScript
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.")
|