Files
pirate-dice/scripts/main.gd
2026-09-16 17:55:28 +02:00

130 lines
4.1 KiB
GDScript

extends Node
## Wechselt zwischen Menü und laufenden Partien.
##
## Mit `-- server <port>` 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")