Das echte Spiel umging die Sitzung bisher bei Bereitschaft, Startaufstellung und Rückgängig, ließ Bots den Gast-Sitz planen und den Gast selbst simulieren. Jetzt rechnet nur der Host; Gäste zeigen den Kampf aus Momentaufnahmen. Dazu Protokollversion, Wiedereinstieg über den Anzeigenamen, verbindliches Bereit, Entwurfs-Privatsphäre und Bot-Planung einmal pro Runde. Geprüft: test_lune_net 43/43, net_rounds.py 12/12 (5 Runden, Wiedereinstieg, gleiche Prüfsummen), Regeln 91/91, M2 73/73, verify_m1 38/38, verify_m2 14/14. Offen und eingefroren: OpenGL-Modus, nativer Windows-11-Test, Demo-Neubau. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
132 lines
5.6 KiB
GDScript
132 lines
5.6 KiB
GDScript
extends MainMenu
|
|
## The Phase 20A menu shell retitled for Champions of Luneraria. Match options here are
|
|
## the new mode's options (own Bund, lane, seed); they never touch the legacy GameState.
|
|
|
|
signal host_requested(port: int, display_name: String)
|
|
signal join_requested(address: String, port: int, display_name: String)
|
|
|
|
var bund: String = "wurzelbund"
|
|
var lane: String = "mid"
|
|
var lane_choice: ChoiceGroup
|
|
var _net_name: LineEdit
|
|
var _net_address: LineEdit
|
|
var _net_port: LineEdit
|
|
var _net_status: Label
|
|
|
|
func _ready() -> void:
|
|
super._ready()
|
|
title_label.text = "CHAMPIONS"
|
|
subtitle_label.text = "OF LUNERARIA"
|
|
subtitle_label.theme_type_variation = "TitleLabel"
|
|
subtitle_label.add_theme_font_size_override("font_size", 54)
|
|
title_label.add_theme_font_size_override("font_size", 64)
|
|
var tagline := Label.new()
|
|
tagline.text = "DREI LANES · 3 GEGEN 3 · AUTOBATTLER"
|
|
tagline.theme_type_variation = "SubtitleLabel"
|
|
brand_names.add_child(tagline)
|
|
lore_label.text = "Führe deine Armee über drei Lanes. Die Partie endet erst, wenn ein Ancient fällt."
|
|
emblem_rect.texture = load("res://assets/ui/emblems/luneraria.svg")
|
|
version_label.text = "Prototyp %s · 1 Mensch + 5 Bots oder 6 über Direktverbindung" % str(ProjectSettings.get_setting("application/config/version", ""))
|
|
_title_caption.text = "Glutpakt und Wurzelbund an den Ufern der Grünen Kluft"
|
|
_retarget_setup()
|
|
|
|
func _retarget_setup() -> void:
|
|
# Replace the Saumwacht-only options (match length, rival, AI level, timed build).
|
|
faction_choice.item_selected.disconnect(faction_choice.item_selected.get_connections()[0].callable)
|
|
faction_choice.item_selected.connect(func(index: int) -> void:
|
|
bund = DataCatalog.FACTIONS[index]
|
|
sync())
|
|
var body: VBoxContainer = mode_choice.get_parent()
|
|
var at: int = mode_choice.get_index() - 1
|
|
for node: Node in [mode_choice, length_summary, _advanced_button.get_parent(), _advanced]:
|
|
node.visible = false
|
|
body.get_child(at).visible = false
|
|
var heading := Label.new()
|
|
heading.text = "DEINE LANE"
|
|
heading.theme_type_variation = "SectionLabel"
|
|
body.add_child(heading)
|
|
body.move_child(heading, at)
|
|
lane_choice = ChoiceGroup.new(true)
|
|
lane_choice.add_option("Top", "oberer Korridor")
|
|
lane_choice.add_option("Mid", "direkter Weg")
|
|
lane_choice.add_option("Bottom", "unterer Korridor")
|
|
body.add_child(lane_choice)
|
|
body.move_child(lane_choice, at + 1)
|
|
lane_choice.item_selected.connect(func(index: int) -> void:
|
|
lane = LuneMap.LANES[index]
|
|
sync())
|
|
var rules := Label.new()
|
|
rules.theme_type_variation = "MutedLabel"
|
|
rules.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
|
rules.text = "Team Blau (Südwest) mit zwei Bot-Verbündeten gegen drei Bots in Team Rot. Kein Rundenlimit: Es gewinnt, wer den gegnerische Ancient zerstört. Für sechs Menschen hosten oder beitreten; freie Plätze bleiben Bots. Der Host ist autoritativ."
|
|
body.add_child(rules)
|
|
body.move_child(rules, at + 2)
|
|
var net_heading := Label.new()
|
|
net_heading.text = "NETZWERK · DIREKTVERBINDUNG"
|
|
net_heading.theme_type_variation = "SectionLabel"
|
|
body.add_child(net_heading)
|
|
_net_name = LineEdit.new()
|
|
_net_name.placeholder_text = "Anzeigename"
|
|
_net_name.text = "Spieler"
|
|
body.add_child(_net_name)
|
|
var net_row := HBoxContainer.new()
|
|
_net_address = LineEdit.new()
|
|
_net_address.placeholder_text = "Host-Adresse"
|
|
_net_address.text = "127.0.0.1"
|
|
_net_address.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
_net_port = LineEdit.new()
|
|
_net_port.placeholder_text = "Port"
|
|
_net_port.text = "24571"
|
|
_net_port.custom_minimum_size.x = 90
|
|
net_row.add_child(_net_address)
|
|
net_row.add_child(_net_port)
|
|
body.add_child(net_row)
|
|
var buttons := HBoxContainer.new()
|
|
var host_btn := Button.new()
|
|
host_btn.text = "Hosten"
|
|
host_btn.tooltip_text = "Partie eröffnen. Freie Plätze sind Bots."
|
|
host_btn.pressed.connect(func() -> void: host_requested.emit(int(_net_port.text), _net_name.text))
|
|
var join_btn := Button.new()
|
|
join_btn.text = "Beitreten"
|
|
join_btn.tooltip_text = "Mit Host und Port verbinden."
|
|
join_btn.pressed.connect(func() -> void: join_requested.emit(_net_address.text.strip_edges(), int(_net_port.text), _net_name.text))
|
|
buttons.add_child(host_btn)
|
|
buttons.add_child(join_btn)
|
|
body.add_child(buttons)
|
|
_net_status = Label.new()
|
|
_net_status.theme_type_variation = "MutedLabel"
|
|
_net_status.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
|
_net_status.text = "Wer die Verbindung verliert, wird vom Bot vertreten und kann mit demselben Anzeigenamen auf seinen Platz zurückkehren."
|
|
body.add_child(_net_status)
|
|
|
|
## Shows why a network start or a running network match ended.
|
|
func notify_error(text: String) -> void:
|
|
if text.is_empty() or _net_status == null:
|
|
return
|
|
_net_status.text = "Netzwerk: " + text
|
|
_net_status.add_theme_color_override("font_color", Color("ff8a70"))
|
|
|
|
func sync() -> void:
|
|
super.sync()
|
|
if lane_choice == null:
|
|
return
|
|
faction_choice.select(DataCatalog.FACTIONS.find(bund))
|
|
lane_choice.select(LuneMap.LANES.find(lane))
|
|
_style_heading.text = "SPIELWEISE · " + DataCatalog.FACTION_NAMES[bund].to_upper()
|
|
var lines: PackedStringArray = []
|
|
for fact: String in FactionChoice.PROFILES[bund].facts:
|
|
lines.append("• " + fact)
|
|
_style_facts.text = "\n".join(lines)
|
|
_timed_hint.text = "Planung wartet auf dich · Leertaste startet den Kampf"
|
|
var names: PackedStringArray = []
|
|
for entry: Array in Stage.SETUPS[bund].figures:
|
|
names.append(str(DataCatalog.get_unit(entry[0]).display_name))
|
|
_setup_caption.text = "IM BILD " + " · ".join(names)
|
|
if screen == Screen.SETUP and _active and stage.current_shot() != bund:
|
|
stage.set_shot_named(bund, reduced_motion)
|
|
|
|
func _setup_shot() -> String:
|
|
return bund
|
|
|
|
func _on_title_primary() -> void:
|
|
show_screen(Screen.SETUP)
|