diff --git a/Cheat/AllCollection.cs b/Cheat/AllCollection.cs new file mode 100644 index 0000000..f90edac --- /dev/null +++ b/Cheat/AllCollection.cs @@ -0,0 +1,102 @@ +using HarmonyLib; +using MAI2.Util; +using Manager; +using Manager.MaiStudio; +using Manager.UserDatas; +using System.Collections.Generic; +using System.Linq; + +namespace SinmaiAssist.Cheat; + +public class AllCollection +{ + [HarmonyPostfix] + [HarmonyPatch(typeof(UserData), "get_FrameList")] + public static void FrameList(ref List __result, CollectionProcess __instance) + { + List list2 = (from i in __result + where i.stock > 0 + select i.itemId).ToList(); + + foreach (KeyValuePair frame2 in Singleton.Instance.GetFrames()) + { + if (!list2.Contains(frame2.Value.GetID())) + { + list2.Add(frame2.Value.GetID()); + __result.Add(new UserItem(frame2.Value.GetID())); + } + } + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(UserData), "get_IconList")] + public static void IconList(ref List __result, CollectionProcess __instance) + { + List list2 = (from i in __result + where i.stock > 0 + select i.itemId).ToList(); + + foreach (KeyValuePair icon2 in Singleton.Instance.GetIcons()) + { + if (!list2.Contains(icon2.Value.GetID())) + { + list2.Add(icon2.Value.GetID()); + __result.Add(new UserItem(icon2.Value.GetID())); + } + } + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(UserData), "get_PlateList")] + public static void PlateList(ref List __result, CollectionProcess __instance) + { + List list2 = (from i in __result + where i.stock > 0 + select i.itemId).ToList(); + + foreach (KeyValuePair plate2 in Singleton.Instance.GetPlates()) + { + if (!list2.Contains(plate2.Value.GetID())) + { + list2.Add(plate2.Value.GetID()); + __result.Add(new UserItem(plate2.Value.GetID())); + } + } + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(UserData), "get_PartnerList")] + public static void PartnerList(ref List __result, CollectionProcess __instance) + { + List list2 = (from i in __result + where i.stock > 0 + select i.itemId).ToList(); + + foreach (KeyValuePair partner2 in Singleton.Instance.GetPartners()) + { + if (!list2.Contains(partner2.Value.GetID())) + { + list2.Add(partner2.Value.GetID()); + __result.Add(new UserItem(partner2.Value.GetID())); + } + } + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(UserData), "get_TitleList")] + public static void TitleList(ref List __result, CollectionProcess __instance) + { + List list2 = (from i in __result + where i.stock > 0 + select i.itemId).ToList(); + + foreach (KeyValuePair title2 in Singleton.Instance.GetTitles()) + { + if (!list2.Contains(title2.Value.GetID())) + { + list2.Add(title2.Value.GetID()); + __result.Add(new UserItem(title2.Value.GetID())); + } + } + } +} \ No newline at end of file diff --git a/Cheat/AutoPlay.cs b/Cheat/AutoPlay.cs new file mode 100644 index 0000000..a274ee8 --- /dev/null +++ b/Cheat/AutoPlay.cs @@ -0,0 +1,283 @@ +using HarmonyLib; +using MAI2.Util; +using Manager; +using Monitor; +using System; +using System.Collections.ObjectModel; +using static NoteJudge; +using MelonLoader; + +namespace SinmaiAssist.Cheat +{ + public class AutoPlay + { + public enum AutoPlayMode + { + None, + Critical, + Perfect, + Great, + Good, + Random, + RandomAllPerfect, + RandomFullComboPlus, + RandomFullCombo + } + + private static readonly ReadOnlyCollection RandCriticalTiming = Array.AsReadOnly(new NoteJudge.ETiming[5] + { + NoteJudge.ETiming.FastPerfect2nd, + NoteJudge.ETiming.FastPerfect, + NoteJudge.ETiming.Critical, + NoteJudge.ETiming.LatePerfect, + NoteJudge.ETiming.LatePerfect2nd + }); + + private static readonly ReadOnlyCollection RandomJudgeTiming = Array.AsReadOnly(new NoteJudge.ETiming[] + { + NoteJudge.ETiming.Critical, + NoteJudge.ETiming.FastPerfect, + NoteJudge.ETiming.FastPerfect2nd, + NoteJudge.ETiming.LatePerfect, + NoteJudge.ETiming.LatePerfect2nd, + NoteJudge.ETiming.LateGreat2nd, + NoteJudge.ETiming.FastGreat2nd, + NoteJudge.ETiming.LateGood, + NoteJudge.ETiming.FastGood, + NoteJudge.ETiming.TooLate + }); + + public static AutoPlayMode autoPlayMode = AutoPlayMode.None; + public static bool DisableUpdate = false; + + public static bool IsAutoPlay() + { + return autoPlayMode != AutoPlayMode.None; + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(GameManager), "IsAutoPlay")] + public static bool SetIsAutoPlay(ref bool __result) + { + __result = IsAutoPlay(); + return false; + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(GameManager), "set_AutoPlay")] + public static void AutoPlayUpdate() + { + var mode = GameManager.AutoPlay; + if (DisableUpdate) return; + autoPlayMode = (AutoPlayMode)mode; + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(GameManager), "AutoJudge")] + public static void AutoJudge(ref NoteJudge.ETiming __result) + { + var random = UnityEngine.Random.Range(0, 1000); + var random2 = 2; + if (random < 350) + { + random2 += UnityEngine.Random.Range(-2, 3); + } + + switch (autoPlayMode) + { + case AutoPlayMode.Critical: + __result = NoteJudge.ETiming.Critical; + break; + case AutoPlayMode.Perfect: + __result = UnityEngine.Random.Range(0, 2) == 0 + ? NoteJudge.ETiming.LatePerfect2nd + : NoteJudge.ETiming.FastPerfect2nd; + break; + case AutoPlayMode.Great: + __result = UnityEngine.Random.Range(0, 2) == 0 + ? NoteJudge.ETiming.LateGreat + : NoteJudge.ETiming.FastGreat; + break; + case AutoPlayMode.Good: + __result = UnityEngine.Random.Range(0, 2) == 0 + ? NoteJudge.ETiming.LateGood + : NoteJudge.ETiming.FastGood; + break; + case AutoPlayMode.Random: + __result = RandomJudgeTiming[UnityEngine.Random.Range(0, RandomJudgeTiming.Count)]; + break; + case AutoPlayMode.RandomAllPerfect: + __result = RandCriticalTiming[random2]; + break; + case AutoPlayMode.RandomFullComboPlus: + if (random >= 10) + { + __result = RandCriticalTiming[random2]; + } + else + { + __result = UnityEngine.Random.Range(0, 2) == 1 + ? NoteJudge.ETiming.LateGreat + : NoteJudge.ETiming.FastGreat; + } + break; + case AutoPlayMode.RandomFullCombo: + if (random >= 80) + { + __result = RandCriticalTiming[random2]; + } + else if (random >= 20) + { + __result = UnityEngine.Random.Range(0, 2) == 1 + ? NoteJudge.ETiming.LateGreat + : NoteJudge.ETiming.FastGreat; + } + else + { + __result = UnityEngine.Random.Range(0, 2) == 1 + ? NoteJudge.ETiming.LateGood + : NoteJudge.ETiming.FastGood; + } + break; + default: + __result = NoteJudge.ETiming.TooFast; + break; + } + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(NoteBase), "SetAutoPlayJudge")] + public static bool NoteBaseAutoPlayJudge(NoteBase __instance) + { + if (!IsAutoPlay()) return true; + var appearMsec = (float)AccessTools.Field(typeof(NoteBase), "AppearMsec").GetValue(__instance); + var isExNote = (bool)AccessTools.Field(typeof(NoteBase), "IsExNote").GetValue(__instance); + var playJudgeSeMethod = AccessTools.Method(typeof(NoteBase), "PlayJudgeSe"); + + if (NotesManager.GetCurrentMsec() > appearMsec - 4.1666665f && IsAutoPlay()) + { + if ((autoPlayMode == AutoPlayMode.RandomAllPerfect || autoPlayMode == AutoPlayMode.RandomFullCombo || autoPlayMode == AutoPlayMode.RandomFullComboPlus) && isExNote) + { + AccessTools.Field(typeof(NoteBase), "JudgeResult").SetValue(__instance, NoteJudge.ETiming.Critical); + } + else + { + AccessTools.Field(typeof(Monitor.NoteBase), "JudgeResult").SetValue(__instance, GameManager.AutoJudge()); + } + playJudgeSeMethod.Invoke(__instance, null); + } + return false; + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(SlideRoot), "Judge")] + public static bool SlideRootJudge(SlideRoot __instance, ref bool __result) + { + if (!IsAutoPlay()) return true; + + // 检查是否进入判定窗口 + var isNoteCheckTimeStartMethod = AccessTools.Method(typeof(SlideRoot), "IsNoteCheckTimeStart"); + bool isNoteCheckTimeStart = (bool)isNoteCheckTimeStartMethod.Invoke( + __instance, + new object[] { Singleton.Instance.GetGameScore(__instance.MonitorId).UserOption.GetJudgeTimingFrame() } + ); + + if (!isNoteCheckTimeStart) + { + __result = false; + return false; + } + + // ★ 强制所有 Slide 判定为 Critical + var judgeResultField = AccessTools.Field(typeof(SlideRoot), "JudgeResult"); + judgeResultField.SetValue(__instance, NoteJudge.ETiming.Critical); + + __result = true; + return false; + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(TouchNoteB), "Judge")] + public static bool TouchNoteBJudge(TouchNoteB __instance, ref bool __result) + { + if (!IsAutoPlay()) return true; + var isNoteCheckTimeStartMethod = AccessTools.Method(typeof(NoteBase), "IsNoteCheckTimeStart"); + bool isNoteCheckTimeStart = (bool)isNoteCheckTimeStartMethod.Invoke(__instance, new object[] { Singleton.Instance.GetGameScore(__instance.MonitorId).UserOption.GetJudgeTimingFrame() }); + if (isNoteCheckTimeStart) + { + __result = false; + return false; + } + var judgeTimingDiffMsecField = AccessTools.Field(typeof(NoteBase), "JudgeTimingDiffMsec"); + var judgeResultField = AccessTools.Field(typeof(NoteBase), "JudgeResult"); + var appearMsecField = AccessTools.Field(typeof(NoteBase), "AppearMsec"); + var judgeTypeField = AccessTools.Field(typeof(NoteBase), "JudgeType"); + var buttonIdField = AccessTools.Field(typeof(NoteBase), "ButtonId"); + var TouchAreaField = AccessTools.Field(typeof(TouchNoteB), "TouchArea"); + var playJudgeSeMethod = AccessTools.Method(typeof(TouchNoteB), "PlayJudgeSe"); + + var judgeTimingDiffMsec = NotesManager.GetCurrentMsec() - (float)appearMsecField.GetValue(__instance); + judgeTimingDiffMsecField.SetValue(__instance, judgeTimingDiffMsec); + + ETiming judgeResult = (ETiming)judgeResultField.GetValue(__instance); + judgeResult = NoteJudge.GetJudgeTiming(ref judgeTimingDiffMsec, Singleton.Instance.GetGameScore(__instance.MonitorId).UserOption.GetJudgeTimingFrame(), (EJudgeType)judgeTypeField.GetValue(__instance)); + if (autoPlayMode == AutoPlayMode.RandomAllPerfect || + autoPlayMode == AutoPlayMode.RandomFullComboPlus || + autoPlayMode == AutoPlayMode.RandomFullCombo) + { + judgeResult = NoteJudge.ETiming.Critical; + } + judgeResultField.SetValue(__instance, judgeResult); + + TouchSensorType touchArea = (TouchSensorType)TouchAreaField.GetValue(__instance); + if (judgeResult != NoteJudge.ETiming.End) + { + playJudgeSeMethod.Invoke(__instance, null); + int buttonId = (int)buttonIdField.GetValue(__instance); + if (touchArea == TouchSensorType.B) + { + InputManager.SetUsedThisFrame(__instance.MonitorId, (InputManager.TouchPanelArea)(8 + buttonId)); + } + else if (touchArea == TouchSensorType.E) + { + InputManager.SetUsedThisFrame(__instance.MonitorId, (InputManager.TouchPanelArea)(26 + buttonId)); + } + else if (touchArea == TouchSensorType.A) + { + InputManager.SetUsedThisFrame(__instance.MonitorId, (InputManager.TouchPanelArea)(0 + buttonId)); + } + else if (touchArea == TouchSensorType.D) + { + InputManager.SetUsedThisFrame(__instance.MonitorId, (InputManager.TouchPanelArea)(18 + buttonId)); + } + else if (touchArea == TouchSensorType.C) + { + InputManager.SetUsedThisFrame(__instance.MonitorId, InputManager.TouchPanelArea.C1); + InputManager.SetUsedThisFrame(__instance.MonitorId, InputManager.TouchPanelArea.C2); + } + __result = true; + return false; + } + return false; + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(TouchNoteB), "NoteCheck")] + public static void NoteCheck(TouchNoteB __instance) + { + if (!IsAutoPlay()) return; + var judgeResultField = AccessTools.Field(typeof(NoteBase), "JudgeResult"); + var playJudgeSeMethod = AccessTools.Method(typeof(TouchNoteB), "PlayJudgeSe"); + float appearMsec = (float)AccessTools.Field(typeof(NoteBase), "AppearMsec").GetValue(__instance); + if ((autoPlayMode == AutoPlayMode.RandomAllPerfect || + autoPlayMode == AutoPlayMode.RandomFullComboPlus || + autoPlayMode == AutoPlayMode.RandomFullCombo) && + NotesManager.GetCurrentMsec() > appearMsec - 4.1666665f && + IsAutoPlay()) + { + judgeResultField.SetValue(__instance, NoteJudge.ETiming.Critical); + playJudgeSeMethod.Invoke(__instance, null); + } + } + } +} diff --git a/Cheat/ChartController.cs b/Cheat/ChartController.cs new file mode 100644 index 0000000..42737f1 --- /dev/null +++ b/Cheat/ChartController.cs @@ -0,0 +1,285 @@ +using DB; +using HarmonyLib; +using MAI2.Util; +using Manager; +using MelonLoader; +using Monitor; +using Process; +using System; +using System.Reflection; +using UnityEngine; + +namespace SinmaiAssist.Cheat; + +public class ChartController +{ + private enum GameSequence + { + Init, + Sync, + Start, + StartWait, + Play, + PlayEnd, + Result, + ResultEnd, + FinalWait, + Release + } + + public enum Button + { + None, + Pause, + Reset, + TimeSkipAdd, + TimeSkipAdd2, + TimeSkipAdd3, + TimeSkipSub, + TimeSkipSub2, + TimeSkipSub3, + Set, + Back + } + + public static Button ButtonStatus = Button.None; + public static bool IsPlaying = false; + public static double Timer = 0.0; + public static int RecordTime = 0; + private static MovieController _gameMovie; + private static NotesManager _notesManager; + private static GameProcess _gameProcess; + private static GameMonitor[] _monitors; + + [HarmonyPostfix] + [HarmonyPatch(typeof(GameProcess), "OnUpdate")] + private static void OnUpdate(GameProcess __instance) + { + try + { + _gameProcess = __instance; + GameSequence sequence = (GameSequence)typeof(GameProcess).GetField("_sequence", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance); + _monitors = (GameMonitor[])typeof(GameProcess).GetField("_monitors", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(_gameProcess); + _gameMovie = (MovieController)typeof(GameProcess).GetField("_gameMovie", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance); + _notesManager = new NotesManager(); + if (!_notesManager.IsPlaying()) + { + IsPlaying = true; + Timer = 0.0; + RecordTime = 0; + } + if (sequence == GameSequence.Play) + { + var IsPartyPlayMethod = typeof(GameProcess).GetMethod("IsPartyPlay", BindingFlags.NonPublic | BindingFlags.Instance); + bool IsPartyPlay = (bool)IsPartyPlayMethod.Invoke(__instance, null); + if (IsPlaying) + { + Timer += GameManager.GetGameMSecAddD(); + } + if (ButtonStatus == Button.Pause || DebugInput.GetKeyDown(SinmaiAssist.KeyBindConfig.ChartController.Pause.KeyCode)) + { + if (IsPlaying) + { + SoundManager.PauseMusic(true); + GameMoviePause(true); + NotesManager.Pause(true); + } + else + { + SoundManager.PauseMusic(false); + GameMoviePause(false); + NotesManager.Pause(false); + //TimeSkip(0); + } + IsPlaying = !IsPlaying; + } + else if (DebugInput.GetKey(SinmaiAssist.KeyBindConfig.ChartController.Backward.KeyCode) && !IsPlaying) + { + Singleton.Instance.Initialize(IsPartyPlay); + TimeSkip(-16); + } + else if (DebugInput.GetKey(SinmaiAssist.KeyBindConfig.ChartController.Forward.KeyCode) && !IsPlaying) + { + Singleton.Instance.Initialize(IsPartyPlay); + TimeSkip(16); + } + else if (DebugInput.GetKeyDown(SinmaiAssist.KeyBindConfig.ChartController.SetRecord.KeyCode) || ButtonStatus == Button.Set) + { + RecordTime = (int)Timer; + MelonLogger.Msg($"Record Time: {RecordTime}"); + } + else if (DebugInput.GetKeyDown(SinmaiAssist.KeyBindConfig.ChartController.ReturnRecord.KeyCode) || ButtonStatus == Button.Back) + { + int time = RecordTime == 0 ? 999999 : (int)Timer - RecordTime; + TimeSkip(-time); + TimeSkip(0); + MelonLogger.Msg($"Time Jump: {RecordTime}({-time})"); + } + else if (ButtonStatus != Button.None) + { + Singleton.Instance.Initialize(IsPartyPlay); + switch (ButtonStatus) + { + case Button.TimeSkipAdd: + TimeSkip(100); + break; + case Button.TimeSkipAdd2: + TimeSkip(1000); + break; + case Button.TimeSkipAdd3: + TimeSkip(2500); + break; + case Button.TimeSkipSub: + TimeSkip(-100); + break; + case Button.TimeSkipSub2: + TimeSkip(-1000); + break; + case Button.TimeSkipSub3: + TimeSkip(-2500); + break; + case Button.Reset: + Singleton.Instance.SetQuickRetryFrag(flag: true); + break; + default: + break; + } + } + } + ReUpdate(); + } + catch (Exception e) + { + MelonLogger.Error(e); + } + } + + private static void TimeSkip(int addMsec) + { + GameMoviePause(true); + NotesManager.Pause(true); + if (addMsec >= 0) + { + Timer += addMsec; + } + else + { + Timer = ((Timer + (double)addMsec >= 0.0) ? (Timer + (double)addMsec) : 0.0); + } + GameMovieSetSeekFrame(Timer); + SoundManager.SeekMusic((int)Timer); + for (int i = 0; i < _monitors.Length; i++) + { + _monitors[i].Seek((int)Timer); + } + int num = 91; + NotesManager.StartPlay((int)Timer + num); + NotesManager.Pause(true); + if (IsPlaying) + { + SoundManager.PauseMusic(pause: false); + GameMoviePause(false); + NotesManager.Pause(false); + } + else + { + GameMoviePause(true); + } + _gameProcess.UpdateNotes(); + } + + private static void ReUpdate() + { + int[] _skipPhase = new int[2] { -1, -1 }; + int[] _retryPhase = new int[2] { -1, -1 }; + System.Type processBaseType = typeof(GameProcess).BaseType; + var containerField = processBaseType.GetField("container", BindingFlags.NonPublic | BindingFlags.Instance); + ProcessDataContainer container = (ProcessDataContainer)containerField.GetValue(_gameProcess); + for (int num30 = 0; num30 < _monitors.Length; num30++) + { + if (Singleton.Instance.GetGameScore(num30).IsTrackSkip || Singleton.Instance.IsQuickRetry()) + { + for (int num31 = 0; num31 < 35; num31++) + { + InputManager.SetUsedThisFrame(num30, (InputManager.TouchPanelArea)num31); + } + } + _monitors[num30].ViewUpdate(); + if (!Singleton.Instance.GetUserData(num30).IsEntry) + { + continue; + } + if (_skipPhase[num30] != _monitors[num30].GetPushPhase()) + { + _skipPhase[num30] = _monitors[num30].GetPushPhase(); + switch (_skipPhase[num30]) + { + case -1: + container.processManager.ForcedCloseWindow(num30); + break; + case 0: + container.processManager.EnqueueMessage(num30, WindowMessageID.TrackSkip3Second, WindowPositionID.Middle); + break; + case 1: + container.processManager.EnqueueMessage(num30, WindowMessageID.TrackSkip2Second, WindowPositionID.Middle); + break; + case 2: + container.processManager.EnqueueMessage(num30, WindowMessageID.TrackSkip1Second, WindowPositionID.Middle); + break; + case 3: + container.processManager.CloseWindow(num30); + break; + } + } + if (_retryPhase[num30] != _monitors[num30].GetPushPhaseRetry()) + { + _retryPhase[num30] = _monitors[num30].GetPushPhaseRetry(); + switch (_retryPhase[num30]) + { + case -1: + container.processManager.ForcedCloseWindow(num30); + break; + case 0: + container.processManager.EnqueueMessage(num30, WindowMessageID.QuickRetry2Second, WindowPositionID.Middle); + break; + case 1: + container.processManager.EnqueueMessage(num30, WindowMessageID.QuickRetry1Second, WindowPositionID.Middle); + break; + case 2: + container.processManager.CloseWindow(num30); + break; + } + } + } + Singleton.Instance.PlayLastUpdate(); + } + + //修复1.55后 因SBGA程序员闲的没事觉得自己不写点东西就要被优化了,灵机一动给GameMovie类下的方法加上屏幕号 导致Mod调用旧方法时让你最新最热原地爆炸的问题 + private static void GameMoviePause(bool pause) + { + var method = _gameMovie.GetType().GetMethod("Pause"); + if (method.GetParameters().Length == 1) + { + method.Invoke(_gameMovie, [pause]); + } + else + { + method.Invoke(_gameMovie, [0, pause]); + method.Invoke(_gameMovie, [1, pause]); + } + } + + private static void GameMovieSetSeekFrame(double msec) + { + var method = _gameMovie.GetType().GetMethod("SetSeekFrame"); + if (method.GetParameters().Length == 1) + { + method.Invoke(_gameMovie, [msec]); + } + else + { + method.Invoke(_gameMovie, [0, msec]); + method.Invoke(_gameMovie, [1, msec]); + } + } +} \ No newline at end of file diff --git a/Cheat/FastSkip.cs b/Cheat/FastSkip.cs new file mode 100644 index 0000000..5b55124 --- /dev/null +++ b/Cheat/FastSkip.cs @@ -0,0 +1,290 @@ +using HarmonyLib; +using MAI2.Util; +using Manager; +using Manager.UserDatas; +using MelonLoader; +using Monitor; +using Process; +using System; +using System.Reflection; +using UnityEngine; + +namespace SinmaiAssist.Cheat; + +internal class FastSkip +{ + private enum GameSequence + { + Init, + Sync, + Start, + StartWait, + Play, + PlayEnd, + Result, + ResultEnd, + FinalWait, + Release + } + + public static bool CustomSkip = false; + public static bool SkipButton = false; + public static bool Force1Miss = false; + public static int CustomAchivement = 0; + + private static bool _isSkip = false; + private static bool _Miss = false; + + [HarmonyPostfix] + [HarmonyPatch(typeof(GameProcess), "OnUpdate")] + public static void Skip(GameProcess __instance) + { + try + { + System.Type processBaseType = typeof(GameProcess).BaseType; + GameSequence _sequence = (GameSequence)typeof(GameProcess).GetField("_sequence", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance); + var UpdateSubbMonitorDataMethod = typeof(GameProcess).GetMethod("UpdateSubbMonitorData", BindingFlags.NonPublic | BindingFlags.Instance); + var SetReleaseMethod = typeof(GameProcess).GetMethod("SetRelease", BindingFlags.NonPublic | BindingFlags.Instance); + var IsPartyPlayMethod = typeof(GameProcess).GetMethod("IsPartyPlay", BindingFlags.NonPublic | BindingFlags.Instance); + var containerField = processBaseType.GetField("container", BindingFlags.NonPublic | BindingFlags.Instance); + ProcessDataContainer container = (ProcessDataContainer)containerField.GetValue(__instance); + if (_sequence >= GameSequence.Play && _sequence < GameSequence.Release && !GameManager.IsNoteCheckMode) + { + _isSkip = false; + if (DebugInput.GetKeyDown(KeyCode.Space) || SkipButton) + { + _isSkip = true; + GameMonitor[] monitors = (GameMonitor[])typeof(GameProcess).GetField("_monitors", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance); + if (CustomSkip) + { + for (int i = 0; i < monitors.Length; i++) + { + monitors[i].Seek(0); + } + NotesManager.StartPlay(0); + NotesManager.Pause(true); + bool IsPartyPlay = (bool)IsPartyPlayMethod.Invoke(__instance, null); + Singleton.Instance.Initialize(IsPartyPlay); + uint maxCombo = 0u; + for (int i = 0; i < monitors.Length; i++) + { + if (Singleton.Instance.GetUserData(i).IsEntry) + { + monitors[i].ForceAchivement(CustomAchivement, 0); + maxCombo += Singleton.Instance.GetGameScore(i).MaxCombo; + } + } + GameScoreList gameScore = Singleton.Instance.GetGameScore(2); + if (gameScore.IsEnable && !gameScore.IsHuman()) + { + for (int i = 0; i < 2; i++) + { + if (Singleton.Instance.GetUserData(i).IsEntry && GameManager.SelectGhostID[i] != GhostManager.GhostTarget.End) + { + UserGhost ghostToEnum = Singleton.Instance.GetGhostToEnum(GameManager.SelectGhostID[i]); + gameScore.SetForceAchivement_Battle((float)GameManager.ConvAchiveIntToDecimal(ghostToEnum.Achievement)); + break; + } + } + } + for (int i = 0; i < monitors.Length; i++) + { + if (Singleton.Instance.GetUserData(i).IsEntry) + { + Singleton.Instance.GetGameScore(i).SetChain(maxCombo); + } + } + } + if (_isSkip) + { + for (int i = 0; i < monitors.Length; i++) + { + if (Singleton.Instance.GetUserData(i).IsEntry) + { + UpdateSubbMonitorDataMethod.Invoke(__instance, new object[] { i }); + Message[] message = (Message[])typeof(GameProcess).GetField("_message", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance); + container.processManager.SendMessage(message[i]); + Singleton.Instance.SetSyncResult(i); + } + } + SetReleaseMethod.Invoke(__instance, null); + SkipButton = false; + } + } + } + } + catch (Exception e) + { + MelonLogger.Error(e); + } + } + + + [HarmonyPrefix] + [HarmonyPatch(typeof(GameScoreList), "SetForceAchivement")] + public static bool SetForceAchivement(int achivement, int dxscore, GameScoreList __instance) + { + decimal num1 = achivement; + if (num1 >= 100.0m && num1 <= 100.4m) num1 = 100.3m; + long num2; + long num3; + if (num1 > 100.0m) + { + num2 = (long)((decimal)__instance.ScoreTotal._allPerfectScore * (num1 - 1.0m) * 0.01m); + num3 = __instance.ScoreTotal._breakBonusScore; + } + else + { + num2 = (long)((decimal)__instance.ScoreTotal._allPerfectScore * (num1 * 0.99m * 0.01m)); + num3 = (long)((decimal)__instance.ScoreTotal._breakBonusScore * num1 * 0.01m); + } + NoteJudge.ETiming[] NoteArray = new NoteJudge.ETiming[7] + { + NoteJudge.ETiming.Critical, + NoteJudge.ETiming.FastGreat, + NoteJudge.ETiming.FastGreat2nd, + NoteJudge.ETiming.LateGreat, + NoteJudge.ETiming.LateGreat2nd, + NoteJudge.ETiming.LateGreat3rd, + NoteJudge.ETiming.LateGood + }; + int monitorIndex = (int)typeof(GameScoreList).GetField("_monitorIndex", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance); + NoteDataList noteList = NotesManager.Instance(monitorIndex).getReader().GetNoteList(); + foreach (NoteData item in noteList) + { + if (!item.type.isBreakScore()) + { + continue; + } + bool flag = false; + NoteJudge.ETiming[] array2 = NoteArray; + foreach (NoteJudge.ETiming eTiming in array2) + { + NoteScore.EScoreType eScoreType = GamePlayManager.NoteType2ScoreType(item.type.getEnum()); + if (0m <= (decimal)(num2 - NoteScore.GetJudgeScore(eTiming, NoteScore.EScoreType.Break)) && 0m <= (decimal)(num3 - NoteScore.GetJudgeScore(eTiming, NoteScore.EScoreType.BreakBonus))) + { + num2 -= NoteScore.GetJudgeScore(eTiming, eScoreType); + num3 -= NoteScore.GetJudgeScore(eTiming, NoteScore.EScoreType.BreakBonus); + __instance.SetResult(item.indexNote, eScoreType, eTiming); + flag = true; + break; + } + } + if (!flag) + { + __instance.SetResult(item.indexNote, NoteScore.EScoreType.Break, NoteJudge.ETiming.TooFast); + _Miss = true; + } + } + int num4 = 0; + int num5 = 0; + long num6 = 0L; + for (int j = 0; j < NoteArray.Length; j++) + { + long num7 = num2; + long num8 = 0L; + num8 += __instance.ScoreTotal.GetTapNum() * NoteScore.GetJudgeScore(NoteArray[j]); + num8 += __instance.ScoreTotal.GetHoldNum() * NoteScore.GetJudgeScore(NoteArray[j], NoteScore.EScoreType.Hold); + num8 += __instance.ScoreTotal.GetSlideNum() * NoteScore.GetJudgeScore(NoteArray[j], NoteScore.EScoreType.Slide); + num8 += __instance.ScoreTotal.GetTouchNum() * NoteScore.GetJudgeScore(NoteArray[j], NoteScore.EScoreType.Touch); + if (num8 <= num7) + { + num6 = num7 - num8; + num5 = ((num4 != 0) ? (num4 - 1) : 0); + break; + } + num4++; + } + if (num4 >= NoteArray.Length) + { + num4 = NoteArray.Length - 1; + } + foreach (NoteData item2 in noteList) + { + if (item2.type.isSlideScore()) + { + NoteScore.EScoreType eScoreType2 = GamePlayManager.NoteType2ScoreType(item2.type.getEnum()); + NoteJudge.ETiming eTiming2 = NoteArray[num4]; + NoteJudge.ETiming eTiming3 = NoteArray[num5]; + if (0m <= (decimal)num6 && 0m <= (decimal)(num2 - NoteScore.GetJudgeScore(eTiming3, eScoreType2))) + { + num6 -= NoteScore.GetJudgeScore(eTiming3, eScoreType2) - NoteScore.GetJudgeScore(eTiming2, eScoreType2); + num2 -= NoteScore.GetJudgeScore(eTiming3, eScoreType2); + __instance.SetResult(item2.indexNote, eScoreType2, eTiming3); + } + else if (0m <= (decimal)(num2 - NoteScore.GetJudgeScore(eTiming2, eScoreType2))) + { + num2 -= NoteScore.GetJudgeScore(eTiming2, eScoreType2); + __instance.SetResult(item2.indexNote, eScoreType2, eTiming2); + } + else + { + __instance.SetResult(item2.indexNote, eScoreType2, NoteJudge.ETiming.TooFast); + _Miss = true; + } + } + } + foreach (NoteData item3 in noteList) + { + if (item3.type.isHoldScore()) + { + NoteScore.EScoreType eScoreType3 = GamePlayManager.NoteType2ScoreType(item3.type.getEnum()); + NoteJudge.ETiming eTiming4 = NoteArray[num4]; + NoteJudge.ETiming eTiming5 = NoteArray[num5]; + if (0m <= (decimal)num6 && 0m <= (decimal)(num2 - NoteScore.GetJudgeScore(eTiming5, eScoreType3))) + { + num6 -= NoteScore.GetJudgeScore(eTiming5, eScoreType3) - NoteScore.GetJudgeScore(eTiming4, eScoreType3); + num2 -= NoteScore.GetJudgeScore(eTiming5, eScoreType3); + __instance.SetResult(item3.indexNote, eScoreType3, eTiming5); + } + else if (0m <= (decimal)(num2 - NoteScore.GetJudgeScore(eTiming4, eScoreType3))) + { + num2 -= NoteScore.GetJudgeScore(eTiming4, eScoreType3); + __instance.SetResult(item3.indexNote, eScoreType3, eTiming4); + } + else + { + __instance.SetResult(item3.indexNote, eScoreType3, NoteJudge.ETiming.TooFast); + _Miss = true; + } + } + } + foreach (NoteData item4 in noteList) + { + if (item4.type.isTapScore()) + { + NoteScore.EScoreType eScoreType4 = GamePlayManager.NoteType2ScoreType(item4.type.getEnum()); + NoteJudge.ETiming eTiming6 = NoteArray[num4]; + NoteJudge.ETiming eTiming7 = NoteArray[num5]; + if (Force1Miss && !_Miss) + { + __instance.SetResult(item4.indexNote, eScoreType4, NoteJudge.ETiming.TooFast); + _Miss = true; + continue; + } + if (0m < (decimal)num6 && 0m <= (decimal)(num2 - NoteScore.GetJudgeScore(eTiming7, eScoreType4))) + { + num6 -= NoteScore.GetJudgeScore(eTiming7, eScoreType4) - NoteScore.GetJudgeScore(eTiming6, eScoreType4); + num2 -= NoteScore.GetJudgeScore(eTiming7, eScoreType4); + __instance.SetResult(item4.indexNote, eScoreType4, eTiming7); + } + else if (0m <= (decimal)(num2 - NoteScore.GetJudgeScore(eTiming6, eScoreType4))) + { + num2 -= NoteScore.GetJudgeScore(eTiming6, eScoreType4); + __instance.SetResult(item4.indexNote, eScoreType4, eTiming6); + } + else if (0m < (decimal)num2) + { + num2 -= NoteScore.GetJudgeScore(eTiming6, eScoreType4); + __instance.SetResult(item4.indexNote, eScoreType4, eTiming6); + } + else + { + __instance.SetResult(item4.indexNote, eScoreType4, NoteJudge.ETiming.TooFast); + _Miss = true; + } + } + } + return false; + } +} \ No newline at end of file diff --git a/Cheat/ForceCurrentIsBest.cs b/Cheat/ForceCurrentIsBest.cs new file mode 100644 index 0000000..cdc5214 --- /dev/null +++ b/Cheat/ForceCurrentIsBest.cs @@ -0,0 +1,99 @@ +using HarmonyLib; +using Manager; +using Manager.UserDatas; +using MelonLoader; +using Process; +using System; +using System.Collections.Generic; +using System.Reflection.Emit; +using DB; +using Game; +using Datas; +using MAI2.Util; +using Manager.MaiStudio; +using Monitor; + +namespace SinmaiAssist.Cheat +{ + public class ForceCurrentIsBest + { + [HarmonyPostfix] + [HarmonyPatch(typeof(ResultProcess), "OnStart")] + + public static void Postfix(ResultProcess __instance) + { + try + { + var userDataField = AccessTools.Field(typeof(ResultProcess), "_userData"); + var userScoresField = AccessTools.Field(typeof(ResultProcess), "_userScores"); + var musicIDField = AccessTools.Field(typeof(ResultProcess), "_musicID"); + var isNewRecordField = AccessTools.Field(typeof(ResultProcess), "_isNewRecord"); + var monitorsField = AccessTools.Field(typeof(ResultProcess), "_monitors"); + var userData = (UserData[])userDataField.GetValue(__instance); + var userScores = (UserScore[])userScoresField.GetValue(__instance); + int musicID = (int)(musicIDField.GetValue(__instance)); + var isNewRecord = (bool[])(isNewRecordField.GetValue(__instance)); + var monitors = (ResultMonitor[])(monitorsField.GetValue(__instance)); + + MusicData music = Singleton.Instance.GetMusic(musicID); + if (music == null) return; + + for (int playerIndex = 0; playerIndex < userData.Length; playerIndex++) + { + if (userData[playerIndex] == null) continue; + + int difficulty = GameManager.SelectDifficultyID[playerIndex]; + + if (userData[playerIndex].ScoreDic[difficulty].TryGetValue(musicID, out UserScore historyScore)) + { + bool isDoublePlay = music.utagePlayStyle == UtagePlayStyle.DoublePlayerScore; + uint oldAchivement = historyScore.achivement; + uint oldDeluxscore = historyScore.deluxscore; + + historyScore.achivement = userScores[playerIndex].achivement; + historyScore.combo = userScores[playerIndex].combo; + historyScore.sync = userScores[playerIndex].sync; + historyScore.deluxscore = userScores[playerIndex].deluxscore; + historyScore.scoreRank = GameManager.GetClearRank( + (int)historyScore.achivement, + isDoublePlay + ); + + int theoreticalValue = isDoublePlay ? 2020000 : 1010000; + if (userScores[playerIndex].achivement >= theoreticalValue) + { + historyScore.extNum1++; + } + else + { + historyScore.extNum1 = 0;//Maybe not necessary + } + + isNewRecord[playerIndex] = true; + int dxFluctuation = (int)historyScore.deluxscore - (int)oldDeluxscore; + int totalNotes = music.notesData[difficulty].maxNotes * 3; + int percent = totalNotes > 0 ? + (int)(historyScore.deluxscore * 100) / totalNotes : 0; + DeluxcorerankrateID dxRank = GameManager.GetDeluxcoreRank(percent); + + monitors[playerIndex].SetDxScore( + historyScore.deluxscore, + dxFluctuation, + totalNotes, + dxRank + ); + monitors[playerIndex].SetMyBestAchievement( + oldAchivement, + historyScore.achivement - oldAchivement, + true + ); + } + } + } + catch (Exception ex) + { + MelonLogger.Error($"ForceCurrentIsBestMoudleError: {ex.Message}"); + } + } + } +} \ No newline at end of file diff --git a/Cheat/ResetLoginBonusRecord.cs b/Cheat/ResetLoginBonusRecord.cs new file mode 100644 index 0000000..8bb3ab3 --- /dev/null +++ b/Cheat/ResetLoginBonusRecord.cs @@ -0,0 +1,24 @@ +using System; +using System.Reflection; +using HarmonyLib; +using Net.Packet; +using Net.Packet.Mai2; +using Net.VO; +using Net.VO.Mai2; + +namespace SinmaiAssist.Cheat; + +public class ResetLoginBonusRecord +{ + [HarmonyPrefix] + [HarmonyPatch(typeof(PacketGetUserData), "SafeNullMember")] + public static bool SafeNullMember(PacketGetUserData __instance, UserDetailResponseVO src) + { + if (src.userData.lastLoginDate != null) + { + src.userData.lastLoginDate = "2000-01-01 00:00:00"; + src.userData.lastPairLoginDate = "2000-01-01 00:00:00"; + } + return true; + } +} \ No newline at end of file diff --git a/Cheat/RewriteLoginBonusStamp.cs b/Cheat/RewriteLoginBonusStamp.cs new file mode 100644 index 0000000..afc091d --- /dev/null +++ b/Cheat/RewriteLoginBonusStamp.cs @@ -0,0 +1,24 @@ +using System; +using System.Reflection; +using HarmonyLib; +using Net.Packet; +using Net.Packet.Mai2; +using Net.VO; +using Net.VO.Mai2; + +namespace SinmaiAssist.Cheat; + +public class RewriteLoginBonusStamp +{ + [HarmonyPrefix] + [HarmonyPatch(typeof(PacketGetUserLoginBonus), "SafeNullMember")] + public static bool SafeNullMember(PacketGetUserData __instance, UserLoginBonusResponseVO src) + { + if (src.userLoginBonusList == null) return true; + for (int i = 0; i < src.userLoginBonusList.Length; i++) + { + src.userLoginBonusList[i].point = SinmaiAssist.MainConfig.Cheat.RewriteLoginBonusStamp.Point; + } + return false; + } +} \ No newline at end of file diff --git a/Cheat/SetAllCharacterAsSameAndLock.cs b/Cheat/SetAllCharacterAsSameAndLock.cs new file mode 100644 index 0000000..4ad32fe --- /dev/null +++ b/Cheat/SetAllCharacterAsSameAndLock.cs @@ -0,0 +1,29 @@ +using System; +using System.Reflection; +using HarmonyLib; +using Net.Packet; +using Net.Packet.Mai2; +using Net.VO; +using Net.VO.Mai2; + +namespace SinmaiAssist.Cheat; + +public class SetAllCharacterAsSameAndLock +{ + [HarmonyPrefix] + [HarmonyPatch(typeof(PacketGetUserData), "SafeNullMember")] + public static bool SafeNullMember(PacketGetUserData __instance, UserDetailResponseVO src) + { + if (src.userData.charaSlot != null) + { + var newSlot = new[] + { + src.userData.charaSlot[0], src.userData.charaSlot[0], src.userData.charaSlot[0], + src.userData.charaSlot[0], src.userData.charaSlot[0] + }; + src.userData.charaSlot = newSlot; + src.userData.charaLockSlot = newSlot; + } + return true; + } +} \ No newline at end of file diff --git a/Cheat/UnlockEvent.cs b/Cheat/UnlockEvent.cs new file mode 100644 index 0000000..cb8676f --- /dev/null +++ b/Cheat/UnlockEvent.cs @@ -0,0 +1,64 @@ +using HarmonyLib; +using Net.Packet; +using Net.Packet.Mai2; +using Net.VO; +using Net.VO.Mai2; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; + +namespace SinmaiAssist.Cheat; + +public class UnlockEvent +{ + [HarmonyPostfix] + [HarmonyPatch(typeof(PacketGetGameEvent), "Proc")] + public static void Unlock(PacketGetGameEvent __instance, ref PacketState __result) + { + if (__result != PacketState.Done) return; + NetQuery netQuery = __instance.Query as NetQuery; + List list = new List(); + + ReadAllEvents("./Sinmai_Data/StreamingAssets/", ref list); + if (Directory.Exists("./option")) + { + ReadAllEvents("./option", ref list); + } + + netQuery.Response.gameEventList = list.ToArray(); + FieldInfo onDoneField = typeof(PacketGetGameEvent).GetField("_onDone", BindingFlags.NonPublic | BindingFlags.Instance); + Action onDone = (Action)onDoneField.GetValue(__instance); + onDone?.Invoke(netQuery.Response.gameEventList ?? Array.Empty()); + } + + private static void ReadAllEvents(string path, ref List list) + { + var rootDir = new DirectoryInfo(path); + + foreach (var optFolder in rootDir.EnumerateDirectories("*", SearchOption.AllDirectories)) + { + var eventPath = Path.Combine(optFolder.FullName, "event"); + if (int.TryParse(optFolder.Name.Substring(1), out var optNumber) && Directory.Exists(eventPath)) + { + var dir = new DirectoryInfo(eventPath); + foreach (var eventFolder in dir.EnumerateDirectories("*", SearchOption.AllDirectories)) + { + if (eventFolder.Name.StartsWith("event") && int.TryParse(eventFolder.Name.Replace("event", ""), out var eventId)) + { + if (eventId > 0) + { + list.Add(new GameEvent + { + id = eventId, + startDate = "2000-01-01 00:00:00", + endDate = "2077-07-21 11:45:14", + type = 1 + }); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/Cheat/UnlockMaster.cs b/Cheat/UnlockMaster.cs new file mode 100644 index 0000000..bdb58ab --- /dev/null +++ b/Cheat/UnlockMaster.cs @@ -0,0 +1,32 @@ +using HarmonyLib; +using MAI2.Util; +using Manager; +using SinmaiAssist.Utils; + +namespace SinmaiAssist.Cheat; + +public class UnlockMaster +{ + [HarmonyPostfix] + [HarmonyPatch(typeof(NotesListManager), "IsUnlockMaster")] + public static void IsUnlockMaster(ref bool __result, ref int id, ref int index) + { + if (__result == false && SinmaiAssist.MainConfig.Cheat.SaveUnlockMaster) + { + User.GetUserData(index).AddUnlockMusic(UserData.MusicUnlock.Master, id); + } + __result = true; + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(NotesListManager), "IsUnlockReMaster")] + public static void IsUnlockReMaster(ref bool __result, ref int id, ref int index) + { + if (Singleton.Instance.GetUserData(index).IsEntry) return; + if (__result == false && SinmaiAssist.MainConfig.Cheat.SaveUnlockMaster) + { + User.GetUserData(index).AddUnlockMusic(UserData.MusicUnlock.ReMaster, id); + } + __result = true; + } +} \ No newline at end of file diff --git a/Cheat/UnlockMusic.cs b/Cheat/UnlockMusic.cs new file mode 100644 index 0000000..b9e05d5 --- /dev/null +++ b/Cheat/UnlockMusic.cs @@ -0,0 +1,19 @@ +using HarmonyLib; +using Manager; +using SinmaiAssist.Utils; + +namespace SinmaiAssist.Cheat; + +public class UnlockMusic +{ + [HarmonyPostfix] + [HarmonyPatch(typeof(NotesListManager), "IsUnlockBase")] + public static void UnlockBase(ref bool __result,ref int id, ref int index) + { + if (__result == false && SinmaiAssist.MainConfig.Cheat.SaveUnlockMusic) + { + User.GetUserData(index).AddUnlockMusic(UserData.MusicUnlock.Base, id); + } + __result = true; + } +} \ No newline at end of file diff --git a/Cheat/UnlockUtage.cs b/Cheat/UnlockUtage.cs new file mode 100644 index 0000000..6296136 --- /dev/null +++ b/Cheat/UnlockUtage.cs @@ -0,0 +1,83 @@ +using System.Collections.Generic; +using System.Reflection; +using HarmonyLib; +using MAI2System; +using Manager; +using Manager.MaiStudio; +using MelonLoader; +using SinmaiAssist.Utils; + +namespace SinmaiAssist.Cheat; + +public class UnlockUtage +{ + private static List _needDoublePlayerMusicIdList = []; + + private static ForceUtageModeType _forceUtageMode = ForceUtageModeType.Normal; + + private static Dictionary _utagePathMap = new() + { + { ForceUtageModeType.Normal, "" }, + { ForceUtageModeType.Force1P, "_L.ma2" }, + { ForceUtageModeType.Force2P, "_R.ma2" } + }; + + private enum ForceUtageModeType + { + Normal, + Force1P, + Force2P + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(GameManager), "CanUnlockUtageTotalJudgement")] + public static bool ForceUnlockUtage(out ConstParameter.ResultOfUnlockUtageJudgement result1P, out ConstParameter.ResultOfUnlockUtageJudgement result2P) + { + result1P = ConstParameter.ResultOfUnlockUtageJudgement.Unlocked; + result2P = ConstParameter.ResultOfUnlockUtageJudgement.Unlocked; + return false; + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(GameScoreList), "InitalizeUtage")] + public static void InitalizeUtage(GameScoreList __instance, int monitorIndex) + { + if (_needDoublePlayerMusicIdList.Contains(__instance.SessionInfo.musicId)) + { + if (_forceUtageMode == 0) + { + if (monitorIndex == 0) __instance.SessionInfo.dPScoreFilePathAdd = "_L.ma2"; + if (monitorIndex == 1) __instance.SessionInfo.dPScoreFilePathAdd = "_R.ma2"; + } + else + { + __instance.SessionInfo.dPScoreFilePathAdd = _utagePathMap[_forceUtageMode]; + } + } + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(MusicData), "Init")] + public static void ForceSetUtagePlayStyle(MusicData __instance) + { + if(!SinmaiAssist.MainConfig.Cheat.UnlockUtage.UnlockDoublePlayerMusic) return; + if (__instance.utagePlayStyle == UtagePlayStyle.DoublePlayerScore) + { + PropertyInfo utagePlayStyleProperty = typeof(MusicData).GetProperty("utagePlayStyle", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); + utagePlayStyleProperty.SetValue(__instance, UtagePlayStyle.SinglePlayerScore); + _needDoublePlayerMusicIdList.Add(__instance.GetID()); + #if Debug + MelonLogger.Msg($"DoublePlayerScoreLoadEvent: {__instance.GetID()}"); + #endif + } + } + + public static void SwitchUtageMode() + { + int current = (int)_forceUtageMode; + int next = (current + 1) % 3; + _forceUtageMode = (ForceUtageModeType)next; + GameMessageManager.SendMessage(0, $"Force Utage Mode: \n{_forceUtageMode.ToString()}"); + GameMessageManager.SendMessage(1, $"Force Utage Mode: \n{_forceUtageMode.ToString()}"); + } +} \ No newline at end of file