Launcher fullscreen and auto-scaled (GUI matrix, lists grow with the screen), game menu fully covered and muted, no minimize
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -26,6 +26,11 @@
|
|||||||
|
|
||||||
Server und Mod müssen beide 2.8 sein (Server: `git pull && docker compose up -d --build`).
|
Server und Mod müssen beide 2.8 sein (Server: `git pull && docker compose up -d --build`).
|
||||||
|
|
||||||
|
**Launcher im Vollbild**
|
||||||
|
- Der MSCO-Launcher füllt das ganze Hauptmenü und **skaliert automatisch** mit der Auflösung; Listen wachsen mit dem
|
||||||
|
Platz (keine abgeschnittenen Knöpfe mehr). Das Spielmenü dahinter (Logo, Farbauswahl, MP3 …) ist komplett verdeckt
|
||||||
|
und gesperrt, die Menü-Musik ist stumm. MINIMIZE entfällt
|
||||||
|
|
||||||
**P2P: Weiterspielen oder neues Spiel**
|
**P2P: Weiterspielen oder neues Spiel**
|
||||||
- In der Host-Lobby: **CONTINUE** (eigenen Spielstand weiterspielen, mit Datum) oder **NEW GAME** (frische Welt für
|
- In der Host-Lobby: **CONTINUE** (eigenen Spielstand weiterspielen, mit Datum) oder **NEW GAME** (frische Welt für
|
||||||
alle). Der bisherige Spielstand wird vorher nach `MSCO_Saves/<Datum>` gesichert (die letzten 10 bleiben), mit
|
alle). Der bisherige Spielstand wird vorher nach `MSCO_Saves/<Datum>` gesichert (die letzten 10 bleiben), mit
|
||||||
|
|||||||
+45
-42
@@ -12,10 +12,18 @@ namespace MSCO
|
|||||||
{
|
{
|
||||||
enum Page { P2P, ServerBrowser, Favorites }
|
enum Page { P2P, ServerBrowser, Favorites }
|
||||||
|
|
||||||
const float Width = 760f;
|
// Vollbild, automatisch skaliert: gebaut für RefHeight x RefWidth, vergrößert/verkleinert passend zur Auflösung
|
||||||
const float Height = 500f;
|
const float RefHeight = 760f;
|
||||||
const float SidebarWidth = 190f;
|
const float RefWidth = 1180f;
|
||||||
const float StatusHeight = 36f;
|
const float MinScale = 0.8f, MaxScale = 1.75f;
|
||||||
|
const float SidebarWidth = 240f;
|
||||||
|
const float StatusHeight = 40f;
|
||||||
|
const float MaxContentWidth = 1100f;
|
||||||
|
float vw, vh, uiScale = 1f; // virtuelle Bildschirmgröße (Einheiten vor der Skalierung)
|
||||||
|
float contentHeight = 400f; // Höhe des Inhaltsbereichs - Listen wachsen damit
|
||||||
|
|
||||||
|
/// <summary>Höhe für eine Liste: was nach "reserved" für Überschriften/Knöpfe übrig bleibt.</summary>
|
||||||
|
float ListHeight(float reserved, float min) { return Mathf.Max(min, contentHeight - reserved); }
|
||||||
|
|
||||||
|
|
||||||
static readonly Color Background = Hex(0x17, 0x17, 0x1b);
|
static readonly Color Background = Hex(0x17, 0x17, 0x1b);
|
||||||
@@ -34,7 +42,6 @@ namespace MSCO
|
|||||||
Page page = Page.P2P;
|
Page page = Page.P2P;
|
||||||
Vector2 profileScroll;
|
Vector2 profileScroll;
|
||||||
bool quitConfirm;
|
bool quitConfirm;
|
||||||
bool minimized;
|
|
||||||
Vector2 playerScroll;
|
Vector2 playerScroll;
|
||||||
Vector2 serverScroll;
|
Vector2 serverScroll;
|
||||||
bool browserLoaded;
|
bool browserLoaded;
|
||||||
@@ -65,8 +72,8 @@ namespace MSCO
|
|||||||
/// <summary>Fläche, die das Fenster gerade belegt (GUI-Koordinaten, oben = 0).</summary>
|
/// <summary>Fläche, die das Fenster gerade belegt (GUI-Koordinaten, oben = 0).</summary>
|
||||||
public Rect ScreenRect { get; private set; }
|
public Rect ScreenRect { get; private set; }
|
||||||
|
|
||||||
/// <summary>Fenster offen (nicht minimiert) - dann ist das Spielmenü dahinter gesperrt.</summary>
|
/// <summary>Der Launcher füllt im Hauptmenü immer den ganzen Bildschirm - das Spielmenü dahinter ist gesperrt.</summary>
|
||||||
public bool IsOpen { get { return !minimized; } }
|
public bool IsOpen { get { return true; } }
|
||||||
|
|
||||||
/// <summary>Ist die Maus gerade über dem Fenster bzw. der eingeklappten Leiste?</summary>
|
/// <summary>Ist die Maus gerade über dem Fenster bzw. der eingeklappten Leiste?</summary>
|
||||||
public bool ContainsMouse()
|
public bool ContainsMouse()
|
||||||
@@ -78,24 +85,37 @@ namespace MSCO
|
|||||||
public void Draw(MultiplayerManager mp)
|
public void Draw(MultiplayerManager mp)
|
||||||
{
|
{
|
||||||
EnsureStyles();
|
EnsureStyles();
|
||||||
|
uiScale = Mathf.Clamp(Mathf.Min(Screen.height / RefHeight, Screen.width / RefWidth), MinScale, MaxScale);
|
||||||
|
vw = Screen.width / uiScale;
|
||||||
|
vh = Screen.height / uiScale;
|
||||||
|
ScreenRect = new Rect(0, 0, Screen.width, Screen.height);
|
||||||
|
Matrix4x4 previous = GUI.matrix;
|
||||||
|
GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(uiScale, uiScale, 1f));
|
||||||
|
try { DrawScaled(mp); }
|
||||||
|
finally { GUI.matrix = previous; }
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawScaled(MultiplayerManager mp)
|
||||||
|
{
|
||||||
|
// Undurchsichtiger Hintergrund über das ganze Spielmenü (Logo, Farbauswahl, Knöpfe ... sind dahinter verborgen)
|
||||||
|
if (texScreen == null) texScreen = Solid(Hex(0x0d, 0x0d, 0x10));
|
||||||
|
GUI.DrawTexture(new Rect(0, 0, vw, vh), texScreen);
|
||||||
bool pendingJoin = mp.PendingJoinHost != null && !mp.IsInSession;
|
bool pendingJoin = mp.PendingJoinHost != null && !mp.IsInSession;
|
||||||
bool versionWarning = mp.PendingVersionWarning != null && !mp.IsInSession;
|
bool versionWarning = mp.PendingVersionWarning != null && !mp.IsInSession;
|
||||||
bool disconnected = mp.DisconnectNotice != null && !mp.IsInSession;
|
bool disconnected = mp.DisconnectNotice != null && !mp.IsInSession;
|
||||||
// Getrennt / Neu-Verbinden: eigenes Fenster in der Bildmitte, alles andere gesperrt
|
// Getrennt / Neu-Verbinden: eigenes Fenster in der Bildmitte, alles andere gesperrt
|
||||||
if (disconnected) { DrawPrompt(mp, false); return; }
|
if (disconnected) { DrawPrompt(mp, false); return; }
|
||||||
if (mp.IsReconnecting) { DrawPrompt(mp, true); return; }
|
if (mp.IsReconnecting) { DrawPrompt(mp, true); return; }
|
||||||
if (pendingJoin || versionWarning) minimized = false; // JOIN-Link / Warnung: Fenster zeigen
|
var window = new Rect(0, 0, vw, vh);
|
||||||
if (minimized) { DrawMinimized(mp); return; }
|
|
||||||
|
|
||||||
var window = new Rect((Screen.width - Width) / 2f, (Screen.height - Height) / 2f, Width, Height);
|
|
||||||
ScreenRect = window;
|
|
||||||
UiTheme.DrawShadow(window);
|
|
||||||
GUI.Box(window, GUIContent.none, windowBox);
|
GUI.Box(window, GUIContent.none, windowBox);
|
||||||
|
|
||||||
DrawSidebar(new Rect(window.x, window.y, SidebarWidth, Height - StatusHeight));
|
DrawSidebar(new Rect(0, 0, SidebarWidth, vh - StatusHeight));
|
||||||
|
|
||||||
var content = new Rect(window.x + SidebarWidth + 32, window.y + 28,
|
// Inhalt mittig im freien Bereich, nicht beliebig breit (lesbar bleiben)
|
||||||
Width - SidebarWidth - 64, Height - StatusHeight - 52);
|
float free = vw - SidebarWidth;
|
||||||
|
float width = Mathf.Min(MaxContentWidth, free - 96f);
|
||||||
|
var content = new Rect(SidebarWidth + (free - width) / 2f, 40f, width, vh - StatusHeight - 72f);
|
||||||
|
contentHeight = content.height;
|
||||||
GUILayout.BeginArea(content);
|
GUILayout.BeginArea(content);
|
||||||
if (versionWarning) DrawVersionWarning(mp);
|
if (versionWarning) DrawVersionWarning(mp);
|
||||||
else if (pendingJoin) DrawJoinConfirm(mp);
|
else if (pendingJoin) DrawJoinConfirm(mp);
|
||||||
@@ -105,7 +125,7 @@ namespace MSCO
|
|||||||
else DrawServerBrowser(mp);
|
else DrawServerBrowser(mp);
|
||||||
GUILayout.EndArea();
|
GUILayout.EndArea();
|
||||||
|
|
||||||
DrawStatusBar(mp, new Rect(window.x, window.yMax - StatusHeight, Width, StatusHeight));
|
DrawStatusBar(mp, new Rect(0, vh - StatusHeight, vw, StatusHeight));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------- Bereiche
|
// ---------------------------------------------------------------- Bereiche
|
||||||
@@ -238,7 +258,7 @@ namespace MSCO
|
|||||||
GUI.Label(new Rect(header.x + header.width * 0.62f, header.y, 70, header.height), "TYPE", headerCellStyle);
|
GUI.Label(new Rect(header.x + header.width * 0.62f, header.y, 70, header.height), "TYPE", headerCellStyle);
|
||||||
GUI.Label(new Rect(header.x + header.width * 0.76f, header.y, 50, header.height), "PING", headerCellStyle);
|
GUI.Label(new Rect(header.x + header.width * 0.76f, header.y, 50, header.height), "PING", headerCellStyle);
|
||||||
|
|
||||||
serverScroll = GUILayout.BeginScrollView(serverScroll, GUILayout.Height(180));
|
serverScroll = GUILayout.BeginScrollView(serverScroll, GUILayout.Height(ListHeight(230f, 140f)));
|
||||||
if (shownError != null) GUILayout.Label(shownError, textStyle);
|
if (shownError != null) GUILayout.Label(shownError, textStyle);
|
||||||
if (servers.Count == 0)
|
if (servers.Count == 0)
|
||||||
GUILayout.Label(browser.Loading ? "Loading servers..." : all.Count > 0 ? "No servers match your filters." : "No servers online right now.", textStyle);
|
GUILayout.Label(browser.Loading ? "Loading servers..." : all.Count > 0 ? "No servers match your filters." : "No servers online right now.", textStyle);
|
||||||
@@ -366,7 +386,7 @@ namespace MSCO
|
|||||||
GUILayout.Space(8);
|
GUILayout.Space(8);
|
||||||
|
|
||||||
List<Favorites.Favorite> favs = Favorites.All;
|
List<Favorites.Favorite> favs = Favorites.All;
|
||||||
favoritesScroll = GUILayout.BeginScrollView(favoritesScroll, GUILayout.Height(250));
|
favoritesScroll = GUILayout.BeginScrollView(favoritesScroll, GUILayout.Height(ListHeight(190f, 160f)));
|
||||||
if (favs.Count == 0)
|
if (favs.Count == 0)
|
||||||
GUILayout.Label("No favorites yet. Click the heart next to a server in the Server Browser, or add one by address below.", textStyle);
|
GUILayout.Label("No favorites yet. Click the heart next to a server in the Server Browser, or add one by address below.", textStyle);
|
||||||
Favorites.Favorite remove = null;
|
Favorites.Favorite remove = null;
|
||||||
@@ -582,7 +602,7 @@ namespace MSCO
|
|||||||
GUILayout.BeginVertical(rowBox, GUILayout.ExpandWidth(true), GUILayout.MinHeight(150));
|
GUILayout.BeginVertical(rowBox, GUILayout.ExpandWidth(true), GUILayout.MinHeight(150));
|
||||||
GUILayout.Space(6);
|
GUILayout.Space(6);
|
||||||
GUILayout.Label("PLAYERS " + s.Players + " / " + s.MaxPlayers, captionStyle);
|
GUILayout.Label("PLAYERS " + s.Players + " / " + s.MaxPlayers, captionStyle);
|
||||||
profileScroll = GUILayout.BeginScrollView(profileScroll, GUILayout.Height(130));
|
profileScroll = GUILayout.BeginScrollView(profileScroll, GUILayout.Height(ListHeight(330f, 130f)));
|
||||||
if (s.PlayerNames == null || s.PlayerNames.Count == 0)
|
if (s.PlayerNames == null || s.PlayerNames.Count == 0)
|
||||||
GUILayout.Label("Nobody online right now.", textStyle);
|
GUILayout.Label("Nobody online right now.", textStyle);
|
||||||
else
|
else
|
||||||
@@ -696,7 +716,7 @@ namespace MSCO
|
|||||||
|
|
||||||
const float PromptWidth = 540f;
|
const float PromptWidth = 540f;
|
||||||
const float PromptHeight = 270f;
|
const float PromptHeight = 270f;
|
||||||
Texture2D texDim, texDanger;
|
Texture2D texDim, texDanger, texScreen;
|
||||||
GUIStyle promptTextStyle;
|
GUIStyle promptTextStyle;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -713,10 +733,9 @@ namespace MSCO
|
|||||||
promptTextStyle.wordWrap = true;
|
promptTextStyle.wordWrap = true;
|
||||||
promptTextStyle.fontSize = 15;
|
promptTextStyle.fontSize = 15;
|
||||||
}
|
}
|
||||||
GUI.DrawTexture(new Rect(0, 0, Screen.width, Screen.height), texDim);
|
GUI.DrawTexture(new Rect(0, 0, vw, vh), texDim);
|
||||||
|
|
||||||
var r = new Rect((Screen.width - PromptWidth) / 2f, (Screen.height - PromptHeight) / 2f, PromptWidth, PromptHeight);
|
var r = new Rect((vw - PromptWidth) / 2f, (vh - PromptHeight) / 2f, PromptWidth, PromptHeight);
|
||||||
ScreenRect = new Rect(0, 0, Screen.width, Screen.height); // Spielmenü dahinter komplett sperren
|
|
||||||
UiTheme.DrawShadow(r);
|
UiTheme.DrawShadow(r);
|
||||||
GUI.Box(r, GUIContent.none, windowBox);
|
GUI.Box(r, GUIContent.none, windowBox);
|
||||||
string title = reconnecting
|
string title = reconnecting
|
||||||
@@ -809,7 +828,7 @@ namespace MSCO
|
|||||||
GUILayout.Space(16);
|
GUILayout.Space(16);
|
||||||
|
|
||||||
GUILayout.Label("PLAYERS " + mp.Players.Count + "/" + mp.SessionMaxPlayers, captionStyle);
|
GUILayout.Label("PLAYERS " + mp.Players.Count + "/" + mp.SessionMaxPlayers, captionStyle);
|
||||||
playerScroll = GUILayout.BeginScrollView(playerScroll, GUILayout.Height(170));
|
playerScroll = GUILayout.BeginScrollView(playerScroll, GUILayout.Height(ListHeight(mp.IsHost ? 330f : 170f, 120f)));
|
||||||
foreach (PlayerInfo p in mp.Players)
|
foreach (PlayerInfo p in mp.Players)
|
||||||
{
|
{
|
||||||
Rect row = GUILayoutUtility.GetRect(10, 32, GUILayout.ExpandWidth(true));
|
Rect row = GUILayoutUtility.GetRect(10, 32, GUILayout.ExpandWidth(true));
|
||||||
@@ -848,25 +867,9 @@ namespace MSCO
|
|||||||
GUI.Box(r, GUIContent.none, sidebarBox);
|
GUI.Box(r, GUIContent.none, sidebarBox);
|
||||||
Texture2D dot = mp.IsFullyConnected ? texOnline : mp.IsInSession || mp.IsReconnecting ? texPending : texMuted;
|
Texture2D dot = mp.IsFullyConnected ? texOnline : mp.IsInSession || mp.IsReconnecting ? texPending : texMuted;
|
||||||
GUI.DrawTexture(new Rect(r.x + 18, r.y + (r.height - 8) / 2f, 8, 8), dot);
|
GUI.DrawTexture(new Rect(r.x + 18, r.y + (r.height - 8) / 2f, 8, 8), dot);
|
||||||
GUI.Label(new Rect(r.x + 36, r.y, r.width - 180, r.height), mp.Status, statusStyle);
|
GUI.Label(new Rect(r.x + 36, r.y, r.width - 72, r.height), mp.Status, statusStyle);
|
||||||
if (GUI.Button(new Rect(r.xMax - 118, r.y + 6, 106, r.height - 12), "MINIMIZE", tabStyle))
|
|
||||||
minimized = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawMinimized(MultiplayerManager mp)
|
|
||||||
{
|
|
||||||
var r = new Rect((Screen.width - 220) / 2f, 0, 220, 30);
|
|
||||||
ScreenRect = r;
|
|
||||||
UiTheme.DrawShadow(r);
|
|
||||||
GUI.Box(r, GUIContent.none, sidebarBox);
|
|
||||||
GUI.Box(new Rect(r.x + 10, r.yMax - 3, r.width - 20, 3), GUIContent.none, accentBox);
|
|
||||||
Texture2D dot = mp.IsFullyConnected ? texOnline : mp.IsInSession || mp.IsReconnecting ? texPending : texMuted;
|
|
||||||
Texture2D logo = Branding.LogoTexture;
|
|
||||||
if (logo != null) GUI.DrawTexture(new Rect(r.x + 8, r.y + 4, 22, 22), logo);
|
|
||||||
GUI.DrawTexture(new Rect(r.x + 36, r.y + 11, 8, 8), dot);
|
|
||||||
if (GUI.Button(new Rect(r.x + 48, r.y, r.width - 48, r.height), "MSCO MULTIPLAYER ▾", tabStyle))
|
|
||||||
minimized = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------- Hilfen
|
// ---------------------------------------------------------------- Hilfen
|
||||||
|
|
||||||
|
|||||||
@@ -72,6 +72,23 @@ namespace MSCO
|
|||||||
return hidden;
|
return hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Geräusche und Musik des Spiel-Hauptmenüs stummschalten - der MSCO-Launcher deckt das ganze Menü ab.
|
||||||
|
/// Gibt die Anzahl der stummgeschalteten Quellen zurück.
|
||||||
|
/// </summary>
|
||||||
|
public static int MuteMenuAudio()
|
||||||
|
{
|
||||||
|
int muted = 0;
|
||||||
|
foreach (Object o in Object.FindObjectsOfType(typeof(AudioSource)))
|
||||||
|
{
|
||||||
|
var src = (AudioSource)o;
|
||||||
|
if (src == null || src.mute || src.transform.root.name.StartsWith("MSCO")) continue;
|
||||||
|
src.mute = true;
|
||||||
|
muted++;
|
||||||
|
}
|
||||||
|
return muted;
|
||||||
|
}
|
||||||
|
|
||||||
public static void StartGame()
|
public static void StartGame()
|
||||||
{
|
{
|
||||||
PlayMakerFSM fsm;
|
PlayMakerFSM fsm;
|
||||||
|
|||||||
@@ -880,6 +880,7 @@ namespace MSCO
|
|||||||
{
|
{
|
||||||
nextMenuButtonCheck = Time.realtimeSinceStartup + 0.5f;
|
nextMenuButtonCheck = Time.realtimeSinceStartup + 0.5f;
|
||||||
menuButtonsHidden = MenuHelper.HideGameMenuButtons() > 0;
|
menuButtonsHidden = MenuHelper.HideGameMenuButtons() > 0;
|
||||||
|
MenuHelper.MuteMenuAudio(); // Launcher im Vollbild: Menü-Musik/-Geräusche aus
|
||||||
}
|
}
|
||||||
UpdateOverlayKeys();
|
UpdateOverlayKeys();
|
||||||
PersistPendingState(); // Kick-Grund / Neu-Verbinden über das Neuladen der Mods hinweg merken
|
PersistPendingState(); // Kick-Grund / Neu-Verbinden über das Neuladen der Mods hinweg merken
|
||||||
|
|||||||
Reference in New Issue
Block a user