extends Node ## Wechselt zwischen Menü und laufenden Partien. ## ## Mit `-- server ` läuft dieselbe Datei als Spielserver: kein Menü, kein ## Bild, dafür beliebig viele Räume nebeneinander. ## ## Ein Raum steckt in einem SubViewport mit eigener World3D. Nur so hat jede ## Partie ihren eigenen Physikraum. Die Knotennamen sind auf Server und Client ## gleich - daran hängt das RPC-Ziel. var menu: CanvasLayer var raeume: Node func _ready() -> void: raeume = Node.new() raeume.name = "Raeume" add_child(raeume) Net.game_started.connect(_raum_bauen) Net.room_closed.connect(_raum_abbauen) Net.rejoin_requested.connect(_wiedereinstieg) Net.server_closed.connect(_back_to_menu) var args := OS.get_cmdline_user_args() if args.size() >= 2 and args[0] == "server": if not Net.serve(int(args[1])): get_tree().quit(1) return menu = preload("res://scripts/menu.gd").new() add_child(menu) # "Nochmal" nach einer Solopartie: das Menü wird übersprungen. if not Net.rematch.is_empty(): var r: Dictionary = Net.rematch Net.rematch = {} var kap := String(r.get("kampagne", "")) if kap != "": # Nochmal gegen denselben Kapitän: nur, wenn er noch da ist. Kampagne.starten(kap) else: Kampagne.kampagnenpartie = false Net.start_singleplayer(String(r.name), int(r.ai), int(r.stufe), 5, r.get("bots", [])) ## Das Spiel startet im Vollbild; F11 schaltet um. func _unhandled_key_input(event: InputEvent) -> void: if event is InputEventKey and event.pressed and event.keycode == KEY_F11: var full := DisplayServer.window_get_mode() == DisplayServer.WINDOW_MODE_FULLSCREEN DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED if full else DisplayServer.WINDOW_MODE_FULLSCREEN) get_viewport().set_input_as_handled() func _raum_bauen(code: String, cfg: Dictionary) -> void: if raeume.has_node(code): # Ein Nochmal ersetzt die alte Partie; alles andere ist ein Doppel. if not cfg.get("rematch", false): return _raum_abbauen(code) if menu != null: menu.queue_free() menu = null var raum := Node.new() raum.name = code raeume.add_child(raum) var behaelter: Node var sicht: Node if Net.dedicated: # Auf dem Server laufen viele Partien nebeneinander. Jede braucht ihren # eigenen Physikraum, sonst lägen alle Würfel im selben. var c := SubViewportContainer.new() c.stretch = true c.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) var v := SubViewport.new() v.own_world_3d = true behaelter = c sicht = v else: # Der Client hat nur eine Partie und rendert direkt ins Fenster. Ein # SubViewport würde hier nur Auflösung und Kantenglättung kosten. # Die Knotennamen müssen trotzdem gleich heißen - daran hängt das # Ziel jedes RPC. behaelter = Node.new() sicht = Node.new() behaelter.name = "Container" sicht.name = "Viewport" raum.add_child(behaelter) behaelter.add_child(sicht) var game := Node3D.new() game.set_script(preload("res://scripts/game.gd")) game.name = "Game" sicht.add_child(game) game.setup(cfg) ## Nur Server: ein Rückkehrer klopft an. Gibt es seinen verwaisten Platz ## noch, übernimmt er ihn wieder vom Bot. func _wiedereinstieg(code: String, peer: int, pname: String) -> void: var game := raeume.get_node_or_null(NodePath(code + "/Container/Viewport/Game")) if game == null: Net.abweisen(peer, "In dem Raum läuft schon eine Partie.") return var cfg: Dictionary = game.wiedereinstieg(peer, pname) if cfg.is_empty(): Net.abweisen(peer, "In dem Raum läuft schon eine Partie, und kein Platz heißt %s." % pname) return Net.rejoin_annehmen(code, peer, pname, cfg) ## Sofort aus dem Baum, nicht erst am Frameende: ein Nochmal baut den Raum ## unter demselben Namen gleich wieder auf. func _raum_abbauen(code: String) -> void: var raum := raeume.get_node_or_null(NodePath(code)) if raum != null: raeume.remove_child(raum) raum.queue_free() ## Nur wenn wirklich eine Partie lief. Sonst würde ein Verbindungsabbruch im ## Menü die Szene neu laden und die Meldung dazu gleich wieder wegwerfen. func _back_to_menu() -> void: if raeume.get_child_count() == 0: return get_tree().change_scene_to_file("res://scenes/main.tscn")