champions-of-luneraria/Assets/Luneraria/UI/Game/LuneMainMenu.cs
Martin Stickelbroeck d590fdebeb U4-Nachtrag: Startbildschirm in der Szene, Rückkehr aus der Partie
Befund aus dem Spieltest des U3-Nachtrags: Nichts legte LuneNetPlay in die
Szene. Das Spiel begann direkt in einer Partie, der Startbildschirm aus U4
war unerreichbar, und der Verlassen-Dialog beendete das Spiel statt zum
Hauptmenü zu führen.

- LuneWorldBuilder legt LuneNetPlay auf das Match-Objekt; Szene neu gebaut.
- Das Menü bekommt ein eigenes UIDocument über dem HUD. Vorher hing es an
  keinem Panel, und das HUD hätte es beim ersten Partiestart geleert.
- Rückkehr über „Menü“ → „Zum Menü“: HUD und Aufstellungsflächen weg,
  Sitzung beendet, leere Karte. Die Kamera ruht hinter dem Menü und beginnt
  jede Partie ungedreht. Die Bund-Wahl ist markiert, die Knopfreihe passt
  in die Tafel.
- Tests und Benchmark ohne Startbildschirm (LuneMatchDriver.DirectStart,
  -luneBenchmark); Bot-Partien und Fingerabdrücke unverändert.
- Neu: LuneSmoke (-luneSmoke <Ordner>), Selbsttest eines Players nach
  lune_smoke.gd. StartScreenTests führt ihn im Editor aus.
- Tests: EditMode 29/29, PlayMode 14/14 (StartScreenTests 26/26), Verify
  718/0, Fingerabdrücke DCE95C96556F3A1A und 7B8E43AEFC0D60C5 unverändert.
  macOS-Player: Selbsttest 26/26, Bilder in
  docs/port/screenshots/u4/startbildschirm/. Ein zusammenhängender
  PlayMode-Lauf auf dem Endstand und ein Player-Benchmark stehen aus, weil
  der Mac zugeklappt schlief (siehe U4-NETWORK.md).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 05:37:51 +02:00

187 lines
7.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using Luneraria.Core;
using UnityEngine;
using UnityEngine.UIElements;
namespace Luneraria.UI
{
/// <summary>
/// Start screen of Champions of Luneraria (port of the network part of lune_menu.gd):
/// offline (local host with bot seats), host a direct connection, or join one. The menu is
/// the start screen of the game; the HUD's "Menü" returns here. Peer 1 is the host; free
/// seats stay bots. The host is authoritative.
/// </summary>
public sealed class LuneMainMenu : VisualElement
{
public event Action<MatchOptions> StartOffline;
public event Action<int, string> HostRequested;
public event Action<string, int, string> JoinRequested;
public event Action QuitRequested;
public string Bund = "wurzelbund";
public string Lane = "mid";
readonly TextField _name;
readonly TextField _address;
readonly TextField _port;
readonly Label _status;
LuneButton _bundGlut;
LuneButton _bundWurzel;
LuneButton _laneTop;
LuneButton _laneMid;
LuneButton _laneBot;
public LuneMainMenu()
{
style.position = Position.Absolute;
style.left = 0f;
style.top = 0f;
style.right = 0f;
style.bottom = 0f;
style.justifyContent = Justify.Center;
style.alignItems = Align.Center;
style.backgroundColor = new Color(.02f, .03f, .05f, .92f);
var panel = new LunePanel(LunePanels.Menu);
panel.style.minWidth = 560f;
// The four action buttons need about 650 px; at 640 they stuck out of the frame.
panel.style.maxWidth = 760f;
Add(panel);
VisualElement column = LuneUi.Box(true, 14f, panel);
column.style.alignItems = Align.Center;
LuneUi.Label("CHAMPIONS", "TitleLabel", column).style.fontSize = 56f;
LuneUi.Label("OF LUNERARIA", "TitleLabel", column).style.fontSize = 40f;
LuneUi.Label("DREI LANES · 3 GEGEN 3 · AUTOBATTLER", "EyebrowLabel", column);
LuneUi.Wrap(LuneUi.Label("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.", "MutedLabel", column));
LuneUi.Label("DEIN BUND", "SectionLabel", column);
VisualElement bundRow = LuneUi.Box(false, 10f, column);
_bundGlut = new LuneButton("Glutpakt", LuneButtons.HudToggle, bundRow);
_bundWurzel = new LuneButton("Wurzelbund", LuneButtons.HudToggle, bundRow);
_bundGlut.ToggleMode = true;
_bundWurzel.ToggleMode = true;
_bundGlut.Clicked += () => SetBund("glutpakt");
_bundWurzel.Clicked += () => SetBund("wurzelbund");
SetBund(Bund);
LuneUi.Label("DEINE LANE", "SectionLabel", column);
VisualElement laneRow = LuneUi.Box(false, 10f, column);
_laneTop = new LuneButton("Top", LuneButtons.HudToggle, laneRow);
_laneMid = new LuneButton("Mid", LuneButtons.HudToggle, laneRow);
_laneBot = new LuneButton("Bottom", LuneButtons.HudToggle, laneRow);
_laneTop.ToggleMode = true;
_laneMid.ToggleMode = true;
_laneBot.ToggleMode = true;
_laneTop.Clicked += () => SetLane("top");
_laneMid.Clicked += () => SetLane("mid");
_laneBot.Clicked += () => SetLane("bot");
SetLane("mid");
LuneUi.Label("NETZWERK · DIREKTVERBINDUNG", "SectionLabel", column);
_name = Field(column, "Anzeigename", "Spieler");
VisualElement netRow = LuneUi.Box(false, 10f, column);
_address = Field(netRow, "Host-Adresse", "127.0.0.1");
_address.style.flexGrow = 1f;
_port = Field(netRow, "Port", LuneNet.DefaultPort.ToString());
_port.style.width = 100f;
VisualElement actions = LuneUi.Box(false, 12f, column);
new LuneButton("ALLEIN SPIELEN", LuneButtons.Primary, actions).Clicked += StartAlone;
new LuneButton("HOSTEN", LuneButtons.Secondary, actions).Clicked += StartHost;
new LuneButton("BEITRETEN", LuneButtons.Secondary, actions).Clicked += StartJoin;
new LuneButton("BEENDEN", LuneButtons.Secondary, actions).Clicked += () => QuitRequested?.Invoke();
_status = LuneUi.Label("", "MutedLabel", column);
_status.style.unityTextAlign = TextAnchor.MiddleCenter;
}
static TextField Field(VisualElement parent, string label, string value)
{
VisualElement box = LuneUi.Box(true, 4f, parent);
box.style.flexGrow = 1f;
LuneUi.Label(label, "EyebrowLabel", box);
var field = new TextField { value = value };
field.style.flexGrow = 1f;
field.style.minHeight = 32f;
field.style.fontSize = 16f;
box.Add(field);
return field;
}
void SetBund(string bund)
{
Bund = bund;
_bundGlut.On = bund == "glutpakt";
_bundWurzel.On = bund == "wurzelbund";
}
void SetLane(string lane)
{
Lane = lane;
_laneTop.On = lane == "top";
_laneMid.On = lane == "mid";
_laneBot.On = lane == "bot";
}
/// <summary>Own seat on team Blau: slots 0–2 map to top, mid, bot.</summary>
public MatchOptions OfflineOptions()
{
int seat = Array.IndexOf(LuneMap.Lanes, Lane);
if (seat < 0)
seat = 1;
var options = new MatchOptions { Seed = Environment.TickCount & 0xffff, HumanSlot = seat };
for (int slot = 0; slot < 6; slot++)
options.Bunds.Add(slot == seat ? Bund : DataCatalog.Factions[(slot + options.Seed) % DataCatalog.Factions.Length]);
return options;
}
void StartAlone()
{
StartOffline?.Invoke(OfflineOptions());
}
void StartHost()
{
int port = ParsePort();
HostRequested?.Invoke(port, CleanName());
}
void StartJoin()
{
int port = ParsePort();
JoinRequested?.Invoke(_address.value.Trim(), port, CleanName());
}
int ParsePort()
{
int port;
return int.TryParse(_port.value.Trim(), out port) && port > 0 ? port : LuneNet.DefaultPort;
}
string CleanName()
{
string name = (_name.value ?? "").Trim();
if (name.Length == 0)
name = "Spieler";
return name.Length > 24 ? name.Substring(0, 24) : name;
}
public void Notify(string message, bool error = false)
{
_status.text = message ?? "";
_status.style.color = error ? LuneUiTheme.Hex("ff8a70") : LuneUiTheme.Muted;
}
public void Enter()
{
LuneUi.Show(this, true);
Notify("");
}
public void Leave()
{
LuneUi.Show(this, false);
}
}
}