Mondherz-Ancients (Saumkern-Kristall als Herz, Sichelklingen, Ringe, Krone, Teamwappen, Energieadern) mit den Stufen stabil/instabil/kritisch, kurzem Zusammenbruch und bleibender Ruine; der Sieg wartet nie auf die Grafik. Turmfamilie aus einem Blender-Generator: Grenzwacht (Tower 1), Bastion (Tower 2), Kernwacht (Basisturm in der Formensprache des Ancients), mit Schadensstufen, Einsturz, Ruine und sichtbarem Angriff (Zielring, Aufladen, Bolzen, Einschlag) - der Schaden bleibt vollständig in der Simulation. Bossgrotte Mondschlund mit Tor, Treppe in die Tiefe, Altar, Säulen, Felsen und fünf Zuständen; der Generator hält Abstand zur Top-Lane, die Höhle braucht eine kleine Geländesenke (nur Darstellung). Superboss Schlundkrone mit 24 Knochen und 11 Clips (inkl. Brüllen und Sieg), Adern in Teamfarbe, dasselbe Modell für Grotte und Lane. Gemeinsame LN-Materialfamilie (Stein, Metall, Kristall, Rune, Stoff, Mondwasser, Kreaturenhaut), Occlusion-Fade ohne dithernde Schatten. Keine Regeländerung. Geprüft: verify_g2 245/245, verify_g1 443/443, Regeln 91/91 und 73/73, Stress 17/17, Balance 40 Partien identisch mit G1, verify_m1 38/38, verify_m2 14/14, Netz 43/43 und Loopback 12/12, Legacy 209 und 463. 21 Ansichten vorher/nachher (1920x1080), Videos Turmkampf und Bossbegegnung, Leistung G1/G2 auf demselben Mac (+0,5 ms in vollen Kampfansichten). Pakete 0.17.0-g2 für macOS (Universal, ad-hoc signiert), Windows x64/ARM64 und Linux; macOS nativ und unter Rosetta, beide Windows-Pakete in Windows 11 ARM (Parallels, OpenGL-Fallback) mit --lune-smoke bestanden. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
134 lines
5.8 KiB
GDScript
134 lines
5.8 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")
|
|
# Luneraria keeps its own version; application/config/version stays the Saumwacht demo's.
|
|
var version: String = str(ProjectSettings.get_setting("luneraria/config/version", ProjectSettings.get_setting("application/config/version", "")))
|
|
version_label.text = "Prototyp %s · 1 Mensch + 5 Bots oder 6 über Direktverbindung" % 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)
|