From 0b55d270e5c068528c6011f81883cf4d8062b2fb Mon Sep 17 00:00:00 2001 From: MrMaho Date: Thu, 1 Oct 2026 20:06:34 +0200 Subject: [PATCH] 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 --- CHANGELOG.md | 5 +++ LobbyUI.cs | 87 ++++++++++++++++++++++--------------------- MenuHelper.cs | 17 +++++++++ MultiplayerManager.cs | 1 + 4 files changed, 68 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 133caf2..86992a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,11 @@ 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** - 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/` gesichert (die letzten 10 bleiben), mit diff --git a/LobbyUI.cs b/LobbyUI.cs index a813b4e..a26c45e 100644 --- a/LobbyUI.cs +++ b/LobbyUI.cs @@ -12,10 +12,18 @@ namespace MSCO { enum Page { P2P, ServerBrowser, Favorites } - const float Width = 760f; - const float Height = 500f; - const float SidebarWidth = 190f; - const float StatusHeight = 36f; + // Vollbild, automatisch skaliert: gebaut für RefHeight x RefWidth, vergrößert/verkleinert passend zur Auflösung + const float RefHeight = 760f; + const float RefWidth = 1180f; + 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 + + /// Höhe für eine Liste: was nach "reserved" für Überschriften/Knöpfe übrig bleibt. + float ListHeight(float reserved, float min) { return Mathf.Max(min, contentHeight - reserved); } static readonly Color Background = Hex(0x17, 0x17, 0x1b); @@ -34,7 +42,6 @@ namespace MSCO Page page = Page.P2P; Vector2 profileScroll; bool quitConfirm; - bool minimized; Vector2 playerScroll; Vector2 serverScroll; bool browserLoaded; @@ -65,8 +72,8 @@ namespace MSCO /// Fläche, die das Fenster gerade belegt (GUI-Koordinaten, oben = 0). public Rect ScreenRect { get; private set; } - /// Fenster offen (nicht minimiert) - dann ist das Spielmenü dahinter gesperrt. - public bool IsOpen { get { return !minimized; } } + /// Der Launcher füllt im Hauptmenü immer den ganzen Bildschirm - das Spielmenü dahinter ist gesperrt. + public bool IsOpen { get { return true; } } /// Ist die Maus gerade über dem Fenster bzw. der eingeklappten Leiste? public bool ContainsMouse() @@ -78,24 +85,37 @@ namespace MSCO public void Draw(MultiplayerManager mp) { 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 versionWarning = mp.PendingVersionWarning != null && !mp.IsInSession; bool disconnected = mp.DisconnectNotice != null && !mp.IsInSession; // Getrennt / Neu-Verbinden: eigenes Fenster in der Bildmitte, alles andere gesperrt if (disconnected) { DrawPrompt(mp, false); return; } if (mp.IsReconnecting) { DrawPrompt(mp, true); return; } - if (pendingJoin || versionWarning) minimized = false; // JOIN-Link / Warnung: Fenster zeigen - if (minimized) { DrawMinimized(mp); return; } - - var window = new Rect((Screen.width - Width) / 2f, (Screen.height - Height) / 2f, Width, Height); - ScreenRect = window; - UiTheme.DrawShadow(window); + var window = new Rect(0, 0, vw, vh); 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, - Width - SidebarWidth - 64, Height - StatusHeight - 52); + // Inhalt mittig im freien Bereich, nicht beliebig breit (lesbar bleiben) + 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); if (versionWarning) DrawVersionWarning(mp); else if (pendingJoin) DrawJoinConfirm(mp); @@ -105,7 +125,7 @@ namespace MSCO else DrawServerBrowser(mp); GUILayout.EndArea(); - DrawStatusBar(mp, new Rect(window.x, window.yMax - StatusHeight, Width, StatusHeight)); + DrawStatusBar(mp, new Rect(0, vh - StatusHeight, vw, StatusHeight)); } // ---------------------------------------------------------------- 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.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 (servers.Count == 0) 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); List favs = Favorites.All; - favoritesScroll = GUILayout.BeginScrollView(favoritesScroll, GUILayout.Height(250)); + favoritesScroll = GUILayout.BeginScrollView(favoritesScroll, GUILayout.Height(ListHeight(190f, 160f))); 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); Favorites.Favorite remove = null; @@ -582,7 +602,7 @@ namespace MSCO GUILayout.BeginVertical(rowBox, GUILayout.ExpandWidth(true), GUILayout.MinHeight(150)); GUILayout.Space(6); 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) GUILayout.Label("Nobody online right now.", textStyle); else @@ -696,7 +716,7 @@ namespace MSCO const float PromptWidth = 540f; const float PromptHeight = 270f; - Texture2D texDim, texDanger; + Texture2D texDim, texDanger, texScreen; GUIStyle promptTextStyle; /// @@ -713,10 +733,9 @@ namespace MSCO promptTextStyle.wordWrap = true; 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); - ScreenRect = new Rect(0, 0, Screen.width, Screen.height); // Spielmenü dahinter komplett sperren + var r = new Rect((vw - PromptWidth) / 2f, (vh - PromptHeight) / 2f, PromptWidth, PromptHeight); UiTheme.DrawShadow(r); GUI.Box(r, GUIContent.none, windowBox); string title = reconnecting @@ -809,7 +828,7 @@ namespace MSCO GUILayout.Space(16); 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) { Rect row = GUILayoutUtility.GetRect(10, 32, GUILayout.ExpandWidth(true)); @@ -848,25 +867,9 @@ namespace MSCO GUI.Box(r, GUIContent.none, sidebarBox); 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.Label(new Rect(r.x + 36, r.y, r.width - 180, r.height), mp.Status, statusStyle); - if (GUI.Button(new Rect(r.xMax - 118, r.y + 6, 106, r.height - 12), "MINIMIZE", tabStyle)) - minimized = true; + GUI.Label(new Rect(r.x + 36, r.y, r.width - 72, r.height), mp.Status, statusStyle); } - 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 diff --git a/MenuHelper.cs b/MenuHelper.cs index ea306e0..375ffbb 100644 --- a/MenuHelper.cs +++ b/MenuHelper.cs @@ -72,6 +72,23 @@ namespace MSCO return hidden; } + /// + /// 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. + /// + 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() { PlayMakerFSM fsm; diff --git a/MultiplayerManager.cs b/MultiplayerManager.cs index 618f3b6..4a64efc 100644 --- a/MultiplayerManager.cs +++ b/MultiplayerManager.cs @@ -880,6 +880,7 @@ namespace MSCO { nextMenuButtonCheck = Time.realtimeSinceStartup + 0.5f; menuButtonsHidden = MenuHelper.HideGameMenuButtons() > 0; + MenuHelper.MuteMenuAudio(); // Launcher im Vollbild: Menü-Musik/-Geräusche aus } UpdateOverlayKeys(); PersistPendingState(); // Kick-Grund / Neu-Verbinden über das Neuladen der Mods hinweg merken