Own main menu (game's Continue/New Game/Credits/Changelog/Quit hidden, QUIT in the launcher); new server profile layout; admin items: ratchet set, battery, shop items; session shows server player limit

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-27 10:41:54 +02:00
co-authored by Claude Opus 5.5
parent 9f55646123
commit bba20cf991
5 changed files with 166 additions and 44 deletions
+4
View File
@@ -19,6 +19,10 @@ Server und Mod müssen beide 2.7 sein (Server: `git pull && docker compose up -d
- Letzte Position wird zuverlässig gespeichert (nicht mehr immer an der Toilette spawnen) - Letzte Position wird zuverlässig gespeichert (nicht mehr immer an der Toilette spawnen)
- Server-Browser: Klick auf den Server öffnet das Profil (JOIN-Knopf in der Liste entfernt) - Server-Browser: Klick auf den Server öffnet das Profil (JOIN-Knopf in der Liste entfernt)
- Alle Mod-Meldungen nur noch in der MSCO-Konsole (F7) - Alle Mod-Meldungen nur noch in der MSCO-Konsole (F7)
- Eigenes Hauptmenü: Continue, New Game, Credits, Changelog und Quit des Spiels sind ausgeblendet, der MSCO-Launcher
ist das Hauptmenü (mit eigenem QUIT)
- Server-Profil neu: JOIN oben, Spielerliste rechts, Namensfeld ohne Steam in eigenem Kasten
- Admin-ITEMS: Werkzeuge und Laden-Artikel (Ratschenkasten, Batterie, Motoröl, Kühlmittel, Bremsflüssigkeit ...)
**Behoben** **Behoben**
- Auto fiel beim Fahren eines anderen Spielers auseinander (angebaute Teile rissen an ihren Gelenken ab) - Auto fiel beim Fahren eines anderen Spielers auseinander (angebaute Teile rissen an ihren Gelenken ab)
+44 -6
View File
@@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using HutongGames.PlayMaker;
using UnityEngine; using UnityEngine;
namespace MSCO namespace MSCO
@@ -7,31 +8,59 @@ namespace MSCO
/// Admin: Items spawnen. Kopiert ein vorhandenes Item des Spiels (z.B. "beer case(itemx)" aus dem Laden) und /// Admin: Items spawnen. Kopiert ein vorhandenes Item des Spiels (z.B. "beer case(itemx)" aus dem Laden) und
/// setzt die Kopie an die gewünschte Stelle. Die Kopie behält den Namen des Originals, damit sich die Spiel-Logik /// setzt die Kopie an die gewünschte Stelle. Die Kopie behält den Namen des Originals, damit sich die Spiel-Logik
/// (Trinken, Essen, Tanken ...) wie beim echten Item verhält und der Objekt-Sync sie bei allen gleich benennt. /// (Trinken, Essen, Tanken ...) wie beim echten Item verhält und der Objekt-Sync sie bei allen gleich benennt.
/// Werkzeuge wie der Ratschenkasten sind im Spiel keine Gegenstände, sondern ein Schalter ("hat der Spieler") -
/// die werden dem Admin direkt gegeben.
/// </summary> /// </summary>
public static class ItemSpawner public static class ItemSpawner
{ {
/// <summary>Auswahl im Admin-Menü (Name ohne "(itemx)").</summary> /// <summary>Werkzeug statt Gegenstand: globale Spielvariable, die "gekauft" bedeutet.</summary>
public const string RatchetSet = "ratchet set";
/// <summary>Auswahl im Admin-Menü (Anzeigename).</summary>
public static readonly string[] Items = public static readonly string[] Items =
{ {
"beer case", "sausages", "pizza", "macaron box", "milk", "juice", "ground coffee", "sugar", "yeast", "booze", // Werkzeug / Auto
"gasoline", "diesel", "spray can", "fire extinguisher", "flashlight", "lantern", "car jack", "floor jack", RatchetSet, "spanner set", "battery", "car jack", "floor jack", "motor hoist", "flashlight", "lantern",
"spanner set", "sledgehammer", "radio", "camera", "grill charcoal", "fireworks bag", "water bucket" "fire extinguisher", "spray can", "sledgehammer", "ax", "digging bar",
// Flüssigkeiten / Kraftstoff
"motor oil", "coolant", "brake fluid", "two stroke fuel", "gasoline", "diesel", "n2o bottle",
// Essen / Trinken / Laden
"beer case", "beer", "booze", "vodka shot", "milk", "juice", "coffee", "ground coffee", "sugar", "yeast",
"sausages", "pizza", "macaron box", "potato chips", "cigarettes", "mosquito spray", "grill charcoal",
// Sonstiges
"grill", "radio", "camera", "helmet", "bucket", "water bucket", "fireworks bag", "parts magazine"
};
// Items, die im Spiel nicht "(itemx)" heißen
static readonly Dictionary<string, string> objectNames = new Dictionary<string, string>
{
{ "battery", "battery(Clone)" }
}; };
static readonly Dictionary<string, GameObject> templates = new Dictionary<string, GameObject>(); static readonly Dictionary<string, GameObject> templates = new Dictionary<string, GameObject>();
public static bool IsKnown(string item) { return System.Array.IndexOf(Items, item) >= 0; }
static string ObjectName(string item)
{
string name;
return objectNames.TryGetValue(item, out name) ? name : item + "(itemx)";
}
static GameObject FindTemplate(string item) static GameObject FindTemplate(string item)
{ {
GameObject cached; GameObject cached;
if (templates.TryGetValue(item, out cached) && cached != null) return cached; if (templates.TryGetValue(item, out cached) && cached != null) return cached;
string name = item + "(itemx)"; string name = ObjectName(item);
GameObject best = null; GameObject best = null;
foreach (Object o in Resources.FindObjectsOfTypeAll(typeof(GameObject))) foreach (Object o in Resources.FindObjectsOfTypeAll(typeof(GameObject)))
{ {
var go = (GameObject)o; var go = (GameObject)o;
if (go.name != name || go.GetComponent<Rigidbody>() == null) continue; if (go.name != name || go.GetComponent<Rigidbody>() == null) continue;
if (best == null || (go.activeInHierarchy && !best.activeInHierarchy)) best = go; // am liebsten ein echtes in der Welt // Keine eingebauten Teile kopieren (z.B. die Batterie im Auto): nicht unter einem anderen Rigidbody
if (go.transform.parent != null && go.transform.parent.GetComponentInParent<Rigidbody>() != null) continue;
if (best == null || (go.activeInHierarchy && !best.activeInHierarchy)) best = go; // am liebsten eins aus der Welt
} }
templates[item] = best; templates[item] = best;
return best; return best;
@@ -60,6 +89,15 @@ namespace MSCO
return true; return true;
} }
/// <summary>Ratschenkasten "kaufen": der Spieler hat ihn dann wie nach dem Kauf im Laden.</summary>
public static bool GiveRatchetSet()
{
FsmBool has = FsmVariables.GlobalVariables.FindFsmBool("PlayerHasRatchet");
if (has == null) return false;
has.Value = true;
return true;
}
public static void ClearCache() { templates.Clear(); } public static void ClearCache() { templates.Clear(); }
} }
} }
+79 -37
View File
@@ -31,6 +31,8 @@ namespace MSCO
static readonly Color Pending = Hex(0xf2, 0xc2, 0x1d); static readonly Color Pending = Hex(0xf2, 0xc2, 0x1d);
Page page = Page.P2P; Page page = Page.P2P;
Vector2 profileScroll;
bool quitConfirm;
bool minimized; bool minimized;
Vector2 playerScroll; Vector2 playerScroll;
Vector2 serverScroll; Vector2 serverScroll;
@@ -46,6 +48,7 @@ namespace MSCO
Texture2D texBackground, texSidebar, texAccent, texOnline, texPending, texMuted, texRow; Texture2D texBackground, texSidebar, texAccent, texOnline, texPending, texMuted, texRow;
List<ServerListEntry> shownAll, shownServers; // Stand des letzten Layout-Durchgangs List<ServerListEntry> shownAll, shownServers; // Stand des letzten Layout-Durchgangs
string layoutError; string layoutError;
GUIStyle dangerStyle;
GUIStyle windowBox, sidebarBox, rowBox, accentBox, pillStyle, pillMutedStyle, chipOnStyle, chipOffStyle; GUIStyle windowBox, sidebarBox, rowBox, accentBox, pillStyle, pillMutedStyle, chipOnStyle, chipOffStyle;
GUIStyle logoStyle, taglineStyle, navStyle, navActiveStyle, titleStyle, textStyle, captionStyle, GUIStyle logoStyle, taglineStyle, navStyle, navActiveStyle, titleStyle, textStyle, captionStyle,
fieldStyle, primaryStyle, secondaryStyle, statusStyle, tabStyle, headerCellStyle, fieldStyle, primaryStyle, secondaryStyle, statusStyle, tabStyle, headerCellStyle,
@@ -112,6 +115,14 @@ namespace MSCO
y += 44; y += 44;
if (NavButton(new Rect(r.x, y, r.width, 42), "Server Browser", page == Page.ServerBrowser)) page = Page.ServerBrowser; if (NavButton(new Rect(r.x, y, r.width, 42), "Server Browser", page == Page.ServerBrowser)) page = Page.ServerBrowser;
// Eigenes Hauptmenü: Spiel beenden (die Knöpfe des Spiel-Menüs sind ausgeblendet)
if (quitConfirm)
{
if (GUI.Button(new Rect(r.x + 16, r.yMax - 118, r.width - 32, 36), "QUIT GAME?", dangerStyle)) Application.Quit();
if (GUI.Button(new Rect(r.x + 16, r.yMax - 76, r.width - 32, 30), "CANCEL", tabStyle)) quitConfirm = false;
}
else if (GUI.Button(new Rect(r.x + 16, r.yMax - 76, r.width - 32, 36), "QUIT", tabStyle)) quitConfirm = true;
GUI.Label(new Rect(r.x + 24, r.yMax - 30, r.width - 48, 20), "v" + MSCO.ModVersion, taglineStyle); GUI.Label(new Rect(r.x + 24, r.yMax - 30, r.width - 48, 20), "v" + MSCO.ModVersion, taglineStyle);
} }
@@ -295,7 +306,7 @@ namespace MSCO
selectedServer = null; selectedServer = null;
return; return;
} }
GUILayout.Space(6); GUILayout.Space(8);
if (s == null) if (s == null)
{ {
@@ -304,64 +315,94 @@ namespace MSCO
return; return;
} }
// Kopf: Name + Abzeichen links, JOIN (und ohne Steam das Namensfeld) rechts - immer sichtbar, // Kopf: Name + Abzeichen links, JOIN rechts - immer sichtbar
// egal wie lang Beschreibung und Spielerliste darunter sind
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
GUILayout.BeginVertical(); GUILayout.BeginVertical();
GUILayout.Label(s.Name, titleStyle); GUILayout.Label(s.Name, titleStyle);
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
GUILayout.Label(s.RequireSteam ? "STEAM" : "NON-STEAM", badgeStyle, GUILayout.ExpandWidth(false)); Rect badgeRow = GUILayoutUtility.GetRect(10, 20, GUILayout.ExpandWidth(true));
if (s.IsLocal) { GUILayout.Space(10); GUILayout.Label("LOCAL", badgeStyle, GUILayout.ExpandWidth(false)); } float bx = badgeRow.x;
GUILayout.FlexibleSpace(); bx += Pill(bx, badgeRow, s.RequireSteam ? "STEAM" : "NON-STEAM", s.RequireSteam ? pillStyle : pillMutedStyle);
if (s.IsLocal) bx += Pill(bx, badgeRow, "LOCAL", pillStyle);
if (s.Version != MSCO.ModVersion) Pill(bx, badgeRow, "v" + s.Version, pillStyle);
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.EndVertical(); GUILayout.EndVertical();
GUILayout.FlexibleSpace(); GUILayout.Space(12);
GUILayout.BeginVertical(GUILayout.Width(190));
GUI.enabled = CanJoin(mp, s); GUI.enabled = CanJoin(mp, s);
if (GUILayout.Button(s.Players >= s.MaxPlayers ? "SERVER FULL" : "JOIN SERVER", primaryStyle, GUILayout.Height(42), GUILayout.Width(190))) if (GUILayout.Button(s.Players >= s.MaxPlayers ? "SERVER FULL" : "JOIN SERVER", primaryStyle, GUILayout.Height(44), GUILayout.Width(180)))
mp.JoinServer(s.Address, s.Port, s.Name, s.MaxPlayers); mp.JoinServer(s.Address, s.Port, s.Name, s.MaxPlayers);
GUI.enabled = true; GUI.enabled = true;
if (s.RequireSteam && !mp.SteamAvailable)
GUILayout.Label("This server requires Steam.", textStyle);
else if (!mp.SteamAvailable)
{
GUILayout.Space(4);
GUILayout.BeginHorizontal();
GUILayout.Label("NAME", captionStyle, GUILayout.Width(42));
DrawNameField(mp, 140);
GUILayout.EndHorizontal();
}
GUILayout.EndVertical();
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
if (!string.IsNullOrEmpty(s.Description)) GUILayout.Label(s.Description, textStyle); if (!string.IsNullOrEmpty(s.Description)) GUILayout.Label(s.Description, textStyle);
GUILayout.Space(10); GUILayout.Space(10);
// Ohne Steam: Name wählen (sauber in einem eigenen Kasten)
if (s.RequireSteam && !mp.SteamAvailable)
{
GUILayout.BeginHorizontal(rowBox, GUILayout.Height(40));
GUILayout.Label("This server requires Steam - start the game through Steam to join.", textStyle);
GUILayout.EndHorizontal();
GUILayout.Space(10);
}
else if (!mp.SteamAvailable)
{
GUILayout.BeginHorizontal(rowBox, GUILayout.Height(44));
GUILayout.Space(6);
GUILayout.BeginVertical(GUILayout.Width(110));
GUILayout.Space(4);
GUILayout.Label("PLAYING AS", captionStyle);
GUILayout.Label("no Steam", textStyle);
GUILayout.EndVertical();
GUILayout.BeginVertical();
GUILayout.Space(5);
DrawNameField(mp, 220);
GUILayout.EndVertical();
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
GUILayout.Space(10);
}
// Infos links, Spielerliste rechts
bool versionOk = s.Version == MSCO.ModVersion; bool versionOk = s.Version == MSCO.ModVersion;
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
GUILayout.BeginVertical(GUILayout.Width(250)); GUILayout.BeginVertical(GUILayout.Width(250));
InfoRow("HOSTED BY", string.IsNullOrEmpty(s.Owner) ? "unknown" : s.Owner, rowNameStyle); InfoLine("HOSTED BY", string.IsNullOrEmpty(s.Owner) ? "unknown" : s.Owner, rowNameStyle);
InfoRow("VERSION", "v" + s.Version + (versionOk ? "" : " (you: v" + MSCO.ModVersion + ")"), versionOk ? rowNameStyle : badgeStyle); InfoLine("VERSION", "v" + s.Version + (versionOk ? "" : " (you: v" + MSCO.ModVersion + ")"), versionOk ? rowNameStyle : badgeStyle);
InfoRow("STEAM", s.RequireSteam ? "Required" : "Not required", rowNameStyle); InfoLine("STEAM", s.RequireSteam ? "Required" : "Not required", rowNameStyle);
InfoRow("ONLINE FOR", s.UptimeSeconds >= 0 ? FormatUptime(s.UptimeSeconds) : "-", rowNameStyle); InfoLine("ONLINE FOR", s.UptimeSeconds >= 0 ? FormatUptime(s.UptimeSeconds) : "-", rowNameStyle);
InfoLine("PING", s.Ping < 0 ? "-" : s.Ping + " ms", rowNameStyle);
InfoLine("ADDRESS", s.Address + ":" + s.Port, rowNameStyle);
GUILayout.EndVertical(); GUILayout.EndVertical();
GUILayout.BeginVertical(); GUILayout.Space(16);
InfoRow("PLAYERS", s.Players + " / " + s.MaxPlayers, rowNameStyle);
InfoRow("PING", s.Ping < 0 ? "-" : s.Ping + " ms", rowNameStyle); GUILayout.BeginVertical(rowBox, GUILayout.ExpandWidth(true), GUILayout.MinHeight(150));
InfoRow("ADDRESS", s.Address + ":" + s.Port, rowNameStyle); GUILayout.Space(6);
GUILayout.Label("PLAYERS " + s.Players + " / " + s.MaxPlayers, captionStyle);
profileScroll = GUILayout.BeginScrollView(profileScroll, GUILayout.Height(130));
if (s.PlayerNames == null || s.PlayerNames.Count == 0)
GUILayout.Label("Nobody online right now.", textStyle);
else
foreach (string name in s.PlayerNames)
{
Rect line = GUILayoutUtility.GetRect(10, 24, GUILayout.ExpandWidth(true));
GUI.DrawTexture(new Rect(line.x + 2, line.y + 8, 8, 8), texOnline);
GUI.Label(new Rect(line.x + 18, line.y, line.width - 18, line.height), name, rowNameStyle);
}
GUILayout.EndScrollView();
GUILayout.EndVertical(); GUILayout.EndVertical();
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
// Spielerliste
if (s.PlayerNames != null && s.PlayerNames.Count > 0)
{
GUILayout.Space(4);
GUILayout.Label("ONLINE", captionStyle);
GUILayout.Label(string.Join(", ", s.PlayerNames.ToArray()), textStyle);
}
GUILayout.FlexibleSpace(); GUILayout.FlexibleSpace();
} }
/// <summary>Eine Info-Zeile: Bezeichnung links (grau), Wert rechts.</summary>
void InfoLine(string caption, string value, GUIStyle valueStyle)
{
GUILayout.BeginHorizontal(GUILayout.Height(26));
GUILayout.Label(caption, captionStyle, GUILayout.Width(100));
GUILayout.Label(value, valueStyle);
GUILayout.EndHorizontal();
}
/// <summary>Kleines abgerundetes Abzeichen, so breit wie sein Text. Gibt die belegte Breite zurück.</summary> /// <summary>Kleines abgerundetes Abzeichen, so breit wie sein Text. Gibt die belegte Breite zurück.</summary>
static float Pill(float x, Rect row, string text, GUIStyle style) static float Pill(float x, Rect row, string text, GUIStyle style)
{ {
@@ -576,6 +617,7 @@ namespace MSCO
navActiveStyle.padding = new RectOffset(26, 10, 0, 0); navActiveStyle.padding = new RectOffset(26, 10, 0, 0);
primaryStyle = Button(Accent, AccentHover, Color.white, 14, FontStyle.Bold); primaryStyle = Button(Accent, AccentHover, Color.white, 14, FontStyle.Bold);
dangerStyle = Button(UiTheme.Danger, UiTheme.DangerHover, Color.white, 13, FontStyle.Bold);
secondaryStyle = Button(Secondary, SecondaryHover, Color.white, 14, FontStyle.Bold); secondaryStyle = Button(Secondary, SecondaryHover, Color.white, 14, FontStyle.Bold);
tabStyle = Button(Sidebar, Secondary, Muted, 11, FontStyle.Bold); tabStyle = Button(Sidebar, Secondary, Muted, 11, FontStyle.Bold);
+21
View File
@@ -51,6 +51,27 @@ namespace MSCO
} }
/// <summary>Startet das Spiel wie ein Klick auf CONTINUE.</summary> /// <summary>Startet das Spiel wie ein Klick auf CONTINUE.</summary>
// Knöpfe des Spiel-Hauptmenüs, die MSCO ersetzt (der MSCO-Launcher ist das Hauptmenü)
static readonly string[] HiddenMenuButtons = { "ButtonContinue", "ButtonNewgame", "ButtonCredits", "ButtonQuit", "ButtonChangelog" };
/// <summary>
/// Continue, New Game, Credits, Changelog und Quit des Spiels unsichtbar und unklickbar machen. Nicht abschalten:
/// die Logik hinter CONTINUE lädt beim Beitreten den übertragenen Spielstand (StartGame). Gibt die Anzahl zurück.
/// </summary>
public static int HideGameMenuButtons()
{
int hidden = 0;
foreach (string name in HiddenMenuButtons)
{
GameObject button = GameObject.Find(name);
if (button == null) continue;
foreach (Renderer r in button.GetComponentsInChildren<Renderer>(true)) r.enabled = false;
foreach (Collider c in button.GetComponentsInChildren<Collider>(true)) c.enabled = false;
hidden++;
}
return hidden;
}
public static void StartGame() public static void StartGame()
{ {
PlayMakerFSM fsm; PlayMakerFSM fsm;
+18 -1
View File
@@ -344,6 +344,13 @@ namespace MSCO
public void SpawnItem(string item) public void SpawnItem(string item)
{ {
if (!CanCheat || localPlayer == null) return; if (!CanCheat || localPlayer == null) return;
if (item == ItemSpawner.RatchetSet)
{
// Werkzeug, kein Gegenstand: nur für einen selbst freischalten
overlays.Log(ItemSpawner.GiveRatchetSet() ? "You now have the ratchet set" : "Ratchet set not found (only works in the game)");
AnnounceCheat("gave themselves the ratchet set");
return;
}
Transform head = playerCamera != null ? playerCamera.transform : localPlayer; Transform head = playerCamera != null ? playerCamera.transform : localPlayer;
Vector3 forward = new Vector3(head.forward.x, 0f, head.forward.z).normalized; Vector3 forward = new Vector3(head.forward.x, 0f, head.forward.z).normalized;
if (forward.sqrMagnitude < 0.01f) forward = localPlayer.forward; if (forward.sqrMagnitude < 0.01f) forward = localPlayer.forward;
@@ -366,7 +373,7 @@ namespace MSCO
string item = r.ReadString(); string item = r.ReadString();
var pos = new Vector3(r.ReadSingle(), r.ReadSingle(), r.ReadSingle()); var pos = new Vector3(r.ReadSingle(), r.ReadSingle(), r.ReadSingle());
var rot = new Quaternion(r.ReadSingle(), r.ReadSingle(), r.ReadSingle(), r.ReadSingle()); var rot = new Quaternion(r.ReadSingle(), r.ReadSingle(), r.ReadSingle(), r.ReadSingle());
if (System.Array.IndexOf(ItemSpawner.Items, item) < 0) return false; // nur bekannte Items if (!ItemSpawner.IsKnown(item) || item == ItemSpawner.RatchetSet) return false; // nur bekannte Gegenstände
return ItemSpawner.Spawn(item, pos, rot); return ItemSpawner.Spawn(item, pos, rot);
} }
@@ -740,6 +747,12 @@ namespace MSCO
// Solange das Multiplayer-Fenster offen ist, reagiert das Spielmenü (CONTINUE, QUIT ...) gar nicht; // Solange das Multiplayer-Fenster offen ist, reagiert das Spielmenü (CONTINUE, QUIT ...) gar nicht;
// erst nach MINIMIZE wieder. Über der eingeklappten Leiste ebenfalls nicht (sonst öffnet sich z.B. der Changelog). // erst nach MINIMIZE wieder. Über der eingeklappten Leiste ebenfalls nicht (sonst öffnet sich z.B. der Changelog).
MenuInputBlocker.SetBlocked(MenuHelper.InMenu && (lobby.IsOpen || lobby.ContainsMouse() || overlays.AnyOpen)); MenuInputBlocker.SetBlocked(MenuHelper.InMenu && (lobby.IsOpen || lobby.ContainsMouse() || overlays.AnyOpen));
// Eigenes Hauptmenü: Singleplayer-Knöpfe des Spiels ausblenden (einmal pro Menü-Besuch, sobald sie da sind)
if (MenuHelper.InMenu && !menuButtonsHidden && Time.realtimeSinceStartup >= nextMenuButtonCheck)
{
nextMenuButtonCheck = Time.realtimeSinceStartup + 0.5f;
menuButtonsHidden = MenuHelper.HideGameMenuButtons() > 0;
}
UpdateOverlayKeys(); UpdateOverlayKeys();
MscoLog.Drain(overlays.Log); // alle Mod-Meldungen in die MSCO-Konsole (F7) MscoLog.Drain(overlays.Log); // alle Mod-Meldungen in die MSCO-Konsole (F7)
UpdatePresence(); UpdatePresence();
@@ -1232,8 +1245,12 @@ namespace MSCO
MscoLog.Info("[MSCO] Sent save (" + files.Count + " files) to " + targets.Count + " player(s)"); MscoLog.Info("[MSCO] Sent save (" + files.Count + " files) to " + targets.Count + " player(s)");
} }
bool menuButtonsHidden;
float nextMenuButtonCheck;
void OnLevelWasLoaded(int level) void OnLevelWasLoaded(int level)
{ {
menuButtonsHidden = false; // neues Hauptmenü: Knöpfe wieder ausblenden
nextPresenceUpdate = 0f; // Discord gleich nach dem Szenenwechsel aktualisieren nextPresenceUpdate = 0f; // Discord gleich nach dem Szenenwechsel aktualisieren
if (MenuHelper.InGame) if (MenuHelper.InGame)
{ {