317 lines
9.4 KiB
GDScript
317 lines
9.4 KiB
GDScript
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("==================")
|