using System.Reflection; using HutongGames.PlayMaker; using HutongGames.PlayMaker.Actions; using UnityEngine; namespace MSCO { /// Hilfsfunktionen rund um Hauptmenü und das Laden der Spielszene. public static class MenuHelper { public const string MenuScene = "MainMenu"; public const string GameScene = "GAME"; public static bool InMenu { get { return Application.loadedLevelName == MenuScene; } } public static bool InGame { get { return Application.loadedLevelName == GameScene; } } static readonly MethodInfo switchState = typeof(Fsm).GetMethod("SwitchState", BindingFlags.NonPublic | BindingFlags.Instance); /// /// Sucht im Hauptmenü die PlayMaker-FSM des CONTINUE-Buttons und den Zustand, /// der per LoadLevel die Szene "GAME" lädt. /// public static bool FindContinueLoadState(out PlayMakerFSM fsm, out FsmState state) { fsm = null; state = null; foreach (Object o in Object.FindObjectsOfType(typeof(PlayMakerFSM))) { var candidate = (PlayMakerFSM)o; if (!HierarchyContains(candidate.transform, "continue")) continue; foreach (FsmState s in candidate.FsmStates) { if (!s.ActionsLoaded) s.LoadActions(); if (s.Actions == null) continue; foreach (FsmStateAction action in s.Actions) { var load = action as LoadLevel; if (load != null && load.levelName != null && load.levelName.Value == GameScene) { fsm = candidate; state = s; return true; } } } } return false; } /// Startet das Spiel wie ein Klick auf CONTINUE. // Knöpfe des Spiel-Hauptmenüs, die MSCO ersetzt (der MSCO-Launcher ist das Hauptmenü) static readonly string[] HiddenMenuButtons = { "ButtonContinue", "ButtonNewgame", "ButtonCredits", "ButtonQuit", "ButtonChangelog" }; /// /// 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. /// 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(true)) r.enabled = false; foreach (Collider c in button.GetComponentsInChildren(true)) c.enabled = false; hidden++; } 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; mutedByUs.Add(src); muted++; } return muted; } static readonly System.Collections.Generic.List mutedByUs = new System.Collections.Generic.List(); /// Launcher eingeklappt: die von uns stummgeschalteten Menü-Geräusche wieder an. public static void UnmuteMenuAudio() { foreach (AudioSource src in mutedByUs) if (src != null) src.mute = false; mutedByUs.Clear(); } public static void StartGame() { PlayMakerFSM fsm; FsmState state; if (switchState != null && FindContinueLoadState(out fsm, out state)) { MscoLog.Info("[MSCO] Loading via CONTINUE (" + fsm.name + " / " + state.Name + ")"); switchState.Invoke(fsm.Fsm, new object[] { state }); } else { MscoLog.Warning("[MSCO] CONTINUE button not found, loading GAME directly"); Application.LoadLevel(GameScene); } } /// /// Neues Spiel starten (der Spielstand wurde vorher geleert): über die Logik des NEW-GAME-Knopfs des Spiels, /// sonst wie CONTINUE - ohne Spielstand legt das Spiel dann eine frische Welt an. /// public static void StartNewGame() { PlayMakerFSM fsm; FsmState state; if (switchState != null && FindLoadState("newgame", out fsm, out state)) { MscoLog.Info("[MSCO] Starting a new game via NEW GAME (" + fsm.name + " / " + state.Name + ")"); switchState.Invoke(fsm.Fsm, new object[] { state }); return; } MscoLog.Info("[MSCO] NEW GAME button not found - starting with an empty save"); StartGame(); } /// FSM unter einem Objekt, dessen Name "hint" enthält, mit einem Zustand, der eine Szene lädt. static bool FindLoadState(string hint, out PlayMakerFSM fsm, out FsmState state) { fsm = null; state = null; // Nur aktive Objekte der Menü-Szene (die Spiel-Knöpfe sind nur unsichtbar, nicht abgeschaltet) foreach (Object o in Object.FindObjectsOfType(typeof(PlayMakerFSM))) { var candidate = (PlayMakerFSM)o; if (!HierarchyContains(candidate.transform, hint)) continue; foreach (FsmState s in candidate.FsmStates) { if (!s.ActionsLoaded) s.LoadActions(); if (s.Actions == null) continue; foreach (FsmStateAction action in s.Actions) { var load = action as LoadLevel; if (load != null && load.levelName != null && !string.IsNullOrEmpty(load.levelName.Value)) { fsm = candidate; state = s; return true; } } } } return false; } static bool HierarchyContains(Transform t, string text) { for (; t != null; t = t.parent) if (t.name.ToLower().Contains(text)) return true; return false; } } }