extends Node ## Entry scene of Champions of Luneraria: title menu, match, return, rematch. ## Offline is a local host with bot-filled seats. Host/Join uses LuneSession (ENet). ## Legacy QA switches open the archived Saumwacht scene unchanged. const Game = preload("res://scripts/luneraria/lune_game.gd") const Menu = preload("res://scripts/luneraria/ui/lune_menu.gd") const Session = preload("res://scripts/luneraria/net/lune_session.gd") const LEGACY_SWITCHES: Array[String] = ["--smoke-test", "--faction-test", "--expedition-test", "--ringborke", "--legacy"] var menu: MainMenu var game: Node3D var settings: Control var session: Node var _layer: CanvasLayer var _last_options: Dictionary = {} func _ready() -> void: for arg: String in OS.get_cmdline_user_args(): if arg in LEGACY_SWITCHES: get_tree().change_scene_to_file.call_deferred("res://scenes/main.tscn") return DisplayServer.window_set_title("Champions of Luneraria") get_tree().auto_accept_quit = false get_tree().root.close_requested.connect(Soundscape.quit_game) _layer = CanvasLayer.new() _layer.layer = 10 add_child(_layer) menu = Menu.new() _layer.add_child(menu) menu.start_requested.connect(func() -> void: start_match({"seed": int(Time.get_unix_time_from_system()) % 100000})) menu.quit_requested.connect(func() -> void: Soundscape.quit_game()) menu.settings_requested.connect(toggle_settings) if menu.has_signal("host_requested"): menu.host_requested.connect(_host_match) menu.join_requested.connect(_join_match) settings = LuneSettings.new() _layer.add_child(settings) settings.visible = false settings.closed.connect(toggle_settings) menu.enter() # Platform smoke test of exported builds (macOS app, Windows): plays a short match alone. for arg: String in OS.get_cmdline_user_args(): if arg.begins_with("--lune-smoke"): var smoke: Node = load("res://scripts/luneraria/lune_smoke.gd").new() smoke.setup(self, arg.get_slice("=", 1) if arg.contains("=") else "") add_child(smoke) func start_match(options: Dictionary) -> void: var own_team_slots: Array[int] = [0, 1, 2] var human: int = own_team_slots[LuneMap.LANES.find(menu.lane)] if menu.lane in LuneMap.LANES else 1 var bunds: Array = [] var rng := RandomNumberGenerator.new() rng.seed = int(options.get("seed", 1)) for slot: int in range(6): bunds.append(menu.bund if slot == human else DataCatalog.FACTIONS[rng.randi() % DataCatalog.FACTIONS.size()]) _last_options = {"seed": int(options.get("seed", 1)), "human_slot": human, "bunds": bunds} _launch(_last_options) func _host_match(port: int, display_name: String) -> void: _reset_session() session = Session.new() session.name = "LuneSession" add_child(session) var options := {"seed": int(Time.get_unix_time_from_system()) % 100000, "human_slot": 0, "bunds": []} var rng := RandomNumberGenerator.new() rng.seed = options.seed for slot: int in range(6): options.bunds.append(menu.bund if slot == 0 else DataCatalog.FACTIONS[rng.randi() % DataCatalog.FACTIONS.size()]) var err: int = session.host_enet(port, options, display_name) if err == OK: _last_options = options _launch(_last_options) else: push_warning(session.last_error) menu.notify_error(session.last_error) _reset_session() func _join_match(address: String, port: int, display_name: String) -> void: _reset_session() session = Session.new() session.name = "LuneSession" add_child(session) session.session_message.connect(func(text: String, is_error: bool) -> void: print(("FEHLER: " if is_error else "") + text)) session.host_lost.connect(func() -> void: var reason: String = session.last_error if session != null else "" return_to_menu() menu.notify_error(reason)) if session.join_enet(address, port, display_name) != OK: menu.notify_error(session.last_error) _reset_session() return # Every setup from the host (first join, rejoin, rematch) relaunches the match view. session.match_resetup.connect(func() -> void: if session != null and session.match_state != null: _last_options = {"seed": session.match_state.seed_value, "human_slot": session.local_slot, "bunds": []} _launch(_last_options)) func _reset_session() -> void: if session != null and is_instance_valid(session): session.close() session.queue_free() session = null func _launch(options: Dictionary) -> void: # The rematch button lives inside the old match; detach it now, free it after the signal. if is_instance_valid(game): remove_child(game) game.queue_free() game = Game.new() add_child(game) game.input_blocked = func() -> bool: return menu.visible or settings.visible if session != null and is_instance_valid(session): game.session = session game.start(options) game.exit_requested.connect(return_to_menu) game.rematch_requested.connect(_rematch) game.hud.settings_requested.connect(toggle_settings) menu.leave() func _rematch() -> void: _last_options.seed = int(_last_options.seed) + 1 if session != null and is_instance_valid(session) and session.is_networked(): if not session.is_authority(): game.hud.notify("Eine Revanche startet der Host.", true) return session.host_rematch(_last_options.duplicate(true)) _launch(_last_options) func return_to_menu() -> void: if is_instance_valid(game): remove_child(game) game.queue_free() game = null _reset_session() menu._locked = false menu.pending_screen = MainMenu.Screen.TITLE menu.enter() func toggle_settings() -> void: settings.visible = not settings.visible # Only an offline match pauses behind the settings; a network match keeps running. var networked: bool = session != null and is_instance_valid(session) and session.is_networked() if is_instance_valid(game) and not networked: game.paused = settings.visible or game.paused if settings.visible: settings.sync() func _unhandled_input(event: InputEvent) -> void: if event is InputEventKey and event.pressed and not event.echo: if event.keycode == KEY_F1 or (event.keycode == KEY_ESCAPE and settings.visible): toggle_settings() get_viewport().set_input_as_handled() elif event.keycode in [KEY_M, KEY_F11]: if event.keycode == KEY_M: Preferences.muted = not Preferences.muted else: Preferences.fullscreen = not Preferences.fullscreen Preferences.commit() get_viewport().set_input_as_handled()