saumwacht-godot/scripts/luneraria/lune_main.gd
Martin Stickelbroeck 214ed5a1cc M4: autoritative Host/Join-Sitzung, Protokolltests, Loopback und Demos
LuneSession hält den autoritativen Matchzustand; Clients senden nur
Absichten. Fremde Sitze, veraltete Befehle und Doppelkäufe sind geprüft,
Disconnect führt zur Bot-Übernahme statt zum Kernverlust. Exporte und
entpackbare Demos 0.16.0-m4 für Linux und Windows.
2026-09-28 00:57:02 +02:00

135 lines
5 KiB
GDScript

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()
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)
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))
if session.join_enet(address, port, display_name) != OK:
return
session.match_resetup.connect(func() -> void:
if game == null and session != null and session.match_state != null:
_last_options = {"seed": session.match_state.seed_value, "human_slot": session.local_slot, "bunds": []}
_launch(_last_options), CONNECT_ONE_SHOT)
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(func() -> void:
_last_options.seed = int(_last_options.seed) + 1
_launch(_last_options))
game.hud.settings_requested.connect(toggle_settings)
menu.leave()
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
if is_instance_valid(game):
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()