extends Node ## GameManager - Globaler Singleton für Spielverwaltung ## Kompatibel mit XR Tools 4 Staging System ## WICHTIG: Als AutoLoad hinzufügen unter Project Settings > AutoLoad ## Name: GameManager, Path: res://scripts/game_manager.gd # === SIGNALS === signal score_changed(new_score: int) signal level_completed(level_name: String, score: int, time: float) signal game_state_changed(new_state: GameState) signal high_score_updated(new_high_score: int) # === ENUMS === enum GameState { MENU, # Im Hauptmenü LOADING, # Level wird geladen PLAYING, # Spiel läuft PAUSED, # Spiel pausiert LEVEL_COMPLETE, # Level abgeschlossen GAME_OVER # Spiel beendet } # === PERSISTENTE DATEN === const SAVE_FILE_PATH = "user://savegame.save" # Spieler-Fortschritt var total_score: int = 0 var high_score: int = 0 var completed_levels: Array[String] = [] var player_preferences: Dictionary = {} # Aktuelle Session-Daten var current_level: String = "" var current_level_score: int = 0 var current_state: GameState = GameState.MENU var level_start_time: float = 0.0 var level_elapsed_time: float = 0.0 var is_timer_running: bool = false # Level-Konfiguration var level_list: Array[String] = [ "res://scenes/levels/level_01.tscn", "res://scenes/levels/level_02.tscn", "res://scenes/levels/level_03.tscn" ] var current_level_index: int = 0 # === INITIALISIERUNG === func _ready() -> void: print("[GameManager] Initialisierung gestartet") load_game() print("[GameManager] Bereit - High Score: ", high_score) func _process(delta: float) -> void: if is_timer_running: level_elapsed_time += delta # === LEVEL MANAGEMENT === func start_level(level_path: String = "") -> void: """Startet ein neues Level""" # Wenn kein Pfad angegeben, verwende aktuellen Index if level_path.is_empty(): if current_level_index >= level_list.size(): print("[GameManager] Alle Level abgeschlossen!") end_game() return level_path = level_list[current_level_index] current_level = level_path current_level_score = 0 level_start_time = Time.get_ticks_msec() / 1000.0 level_elapsed_time = 0.0 is_timer_running = true change_state(GameState.LOADING) print("[GameManager] Starte Level: ", level_path) # Level über XR Tools Staging System laden load_scene_via_staging(level_path) func load_scene_via_staging(scene_path: String, spawn_point = null) -> void: """Lädt eine Szene über das XR Tools Staging System""" # Finde XRToolsSceneBase in der Szenen-Hierarchie var scene_base = find_scene_base() if scene_base and scene_base.has_method("load_scene"): if spawn_point: scene_base.load_scene(scene_path, spawn_point) else: scene_base.load_scene(scene_path) change_state(GameState.PLAYING) else: push_error("[GameManager] XRToolsSceneBase nicht gefunden! Staging System nicht verfügbar.") # Fallback für Tests ohne XR if Engine.is_editor_hint() == false: get_tree().change_scene_to_file(scene_path) change_state(GameState.PLAYING) func find_scene_base() -> Node: """Findet die XRToolsSceneBase Instanz in der Szene""" # Versuche über XRTools Helper-Funktion if XRTools and XRTools.has_method("find_xr_ancestor"): var scene_base = XRTools.find_xr_ancestor(self, "*", "XRToolsSceneBase") if scene_base: return scene_base # Fallback: Durchsuche Szenenbaum return find_node_by_class(get_tree().root, "XRToolsSceneBase") func find_node_by_class(node: Node, class_name: String) -> Node: """Rekursive Suche nach Node mit bestimmter Klasse""" if node.get_class() == class_name or node.get_script() and str(node.get_script()).contains(class_name): return node for child in node.get_children(): var result = find_node_by_class(child, class_name) if result: return result return null func complete_level() -> void: """Level wurde erfolgreich abgeschlossen""" is_timer_running = false # Bonus für schnelle Zeit berechnen var time_bonus = calculate_time_bonus(level_elapsed_time) var final_score = current_level_score + time_bonus # Zum Gesamtscore hinzufügen add_score(final_score) # Level als abgeschlossen markieren if not completed_levels.has(current_level): completed_levels.append(current_level) print("[GameManager] Level abgeschlossen! Score: ", final_score, " Zeit: ", level_elapsed_time, "s") level_completed.emit(current_level, final_score, level_elapsed_time) change_state(GameState.LEVEL_COMPLETE) # Speichern save_game() # Nach kurzer Verzögerung nächstes Level laden await get_tree().create_timer(3.0).timeout next_level() func next_level() -> void: """Lädt das nächste Level""" current_level_index += 1 start_level() func restart_level() -> void: """Startet das aktuelle Level neu""" start_level(current_level) func return_to_menu() -> void: """Kehrt zum Hauptmenü zurück""" is_timer_running = false change_state(GameState.MENU) # Lade die Eingangsszene mit dem Hauptmenü load_scene_via_staging("res://scenes/Level_Base.tscn") func end_game() -> void: """Beendet das Spiel""" is_timer_running = false change_state(GameState.GAME_OVER) save_game() print("[GameManager] Spiel beendet. Finaler Score: ", total_score) # === SCORE MANAGEMENT === func add_score(points: int) -> void: """Fügt Punkte zum Score hinzu""" current_level_score += points total_score += points score_changed.emit(total_score) # High Score Update if total_score > high_score: high_score = total_score high_score_updated.emit(high_score) print("[GameManager] Neuer High Score: ", high_score) func calculate_time_bonus(time_seconds: float) -> int: """Berechnet Zeitbonus basierend auf Levelzeit""" # Beispiel: Je schneller, desto mehr Bonus # Maximum 1000 Punkte bei unter 30 Sekunden var bonus = max(0, 1000 - int(time_seconds * 10)) return bonus func reset_score() -> void: """Setzt den Score zurück""" total_score = 0 current_level_score = 0 score_changed.emit(total_score) # === STATE MANAGEMENT === func change_state(new_state: GameState) -> void: """Ändert den Spielzustand""" if current_state != new_state: current_state = new_state game_state_changed.emit(new_state) print("[GameManager] State geändert zu: ", GameState.keys()[new_state]) func get_state() -> GameState: """Gibt den aktuellen Spielzustand zurück""" return current_state func pause_game() -> void: """Pausiert das Spiel""" if current_state == GameState.PLAYING: is_timer_running = false get_tree().paused = true change_state(GameState.PAUSED) func resume_game() -> void: """Setzt das Spiel fort""" if current_state == GameState.PAUSED: is_timer_running = true get_tree().paused = false change_state(GameState.PLAYING) # === TIMER FUNCTIONS === func get_level_time() -> float: """Gibt die aktuelle Levelzeit zurück""" return level_elapsed_time func stop_timer() -> void: """Stoppt den Timer""" is_timer_running = false func start_timer() -> void: """Startet den Timer""" is_timer_running = true if level_start_time == 0.0: level_start_time = Time.get_ticks_msec() / 1000.0 # === PERSISTIERUNG === func save_game() -> void: """Speichert den Spielfortschritt""" var save_data = { "high_score": high_score, "total_score": total_score, "completed_levels": completed_levels, "current_level_index": current_level_index, "player_preferences": player_preferences, "version": "1.0" } var file = FileAccess.open(SAVE_FILE_PATH, FileAccess.WRITE) if file: file.store_var(save_data) file.close() print("[GameManager] Spiel gespeichert: ", SAVE_FILE_PATH) else: push_error("[GameManager] Fehler beim Speichern: ", FileAccess.get_open_error()) func load_game() -> void: """Lädt den Spielfortschritt""" if not FileAccess.file_exists(SAVE_FILE_PATH): print("[GameManager] Keine Speicherdatei gefunden - Verwende Standardwerte") return var file = FileAccess.open(SAVE_FILE_PATH, FileAccess.READ) if file: var save_data = file.get_var() file.close() if save_data is Dictionary: high_score = save_data.get("high_score", 0) total_score = save_data.get("total_score", 0) completed_levels = save_data.get("completed_levels", []) current_level_index = save_data.get("current_level_index", 0) player_preferences = save_data.get("player_preferences", {}) print("[GameManager] Spiel geladen - High Score: ", high_score) else: push_error("[GameManager] Ungültiges Speicherformat") else: push_error("[GameManager] Fehler beim Laden: ", FileAccess.get_open_error()) func delete_save() -> void: """Löscht die Speicherdatei""" if FileAccess.file_exists(SAVE_FILE_PATH): DirAccess.remove_absolute(SAVE_FILE_PATH) print("[GameManager] Speicherdatei gelöscht") # Werte zurücksetzen high_score = 0 total_score = 0 completed_levels.clear() current_level_index = 0 player_preferences.clear() # === UTILITY FUNCTIONS === func format_time(seconds: float) -> String: """Formatiert Zeit in MM:SS Format""" var minutes = int(seconds) / 60 var secs = int(seconds) % 60 return "%02d:%02d" % [minutes, secs] func get_level_name(level_path: String) -> String: """Extrahiert den Level-Namen aus dem Pfad""" return level_path.get_file().get_basename() # === DEBUG FUNCTIONS === func print_stats() -> void: """Gibt Spielstatistiken aus""" print("=== GAME STATS ===") print("Total Score: ", total_score) print("High Score: ", high_score) print("Current Level: ", get_level_name(current_level)) print("State: ", GameState.keys()[current_state]) print("Completed Levels: ", completed_levels.size()) print("==================")