diff --git a/AquaMai/AquaMai.csproj b/AquaMai/AquaMai.csproj
index 2e833770..c29301a1 100644
--- a/AquaMai/AquaMai.csproj
+++ b/AquaMai/AquaMai.csproj
@@ -37,6 +37,11 @@
true
x64
+
+ bin\Debug\
+ SDGA145
+DEBUG
+
Libs\0Harmony.dll
@@ -314,6 +319,7 @@
+
diff --git a/AquaMai/AquaMai.sln b/AquaMai/AquaMai.sln
index d350c808..4e5cbf75 100644
--- a/AquaMai/AquaMai.sln
+++ b/AquaMai/AquaMai.sln
@@ -9,12 +9,15 @@ Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Release|Any CPU = Release|Any CPU
SDGA145|Any CPU = SDGA145|Any CPU
+ Debug|Any CPU = Debug|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{788BC472-59F7-46F6-B760-65C18BA74389}.Release|Any CPU.ActiveCfg = Release|Any CPU
{788BC472-59F7-46F6-B760-65C18BA74389}.Release|Any CPU.Build.0 = Release|Any CPU
{788BC472-59F7-46F6-B760-65C18BA74389}.SDGA145|Any CPU.ActiveCfg = SDGA145|Any CPU
{788BC472-59F7-46F6-B760-65C18BA74389}.SDGA145|Any CPU.Build.0 = SDGA145|Any CPU
+ {788BC472-59F7-46F6-B760-65C18BA74389}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {788BC472-59F7-46F6-B760-65C18BA74389}.Debug|Any CPU.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/AquaMai/Config.cs b/AquaMai/Config.cs
index 746d7bc8..8b441a81 100644
--- a/AquaMai/Config.cs
+++ b/AquaMai/Config.cs
@@ -60,6 +60,7 @@ namespace AquaMai
public bool Windowed { get; set; }
public int Width { get; set; }
public int Height { get; set; }
+ public bool PractiseMode { get; set; }
}
public class TimeSavingConfig
diff --git a/AquaMai/Fix/DebugFeature.cs b/AquaMai/Fix/DebugFeature.cs
index 523f2670..783c0661 100644
--- a/AquaMai/Fix/DebugFeature.cs
+++ b/AquaMai/Fix/DebugFeature.cs
@@ -17,6 +17,7 @@ public class DebugFeature
private static MovieController _gameMovie;
private static GameMonitor[] _monitors;
private static object _debugFeatureOriginal;
+ private static System.Type _debugFeatureType;
[HarmonyPatch(typeof(GameProcess), "OnStart")]
[HarmonyPostfix]
@@ -39,28 +40,79 @@ public class DebugFeature
else
{
MelonLogger.Msg(" > [DebugFeature] Already included");
+ _debugFeatureType = typeof(GameProcess).GetNestedType("DebugFeature", BindingFlags.Instance | BindingFlags.NonPublic);
h.PatchAll(typeof(GetOriginal));
}
}
- public static void SetPause(bool pause)
+ public static bool Pause
{
+ get
+ {
+ if (IsPolyfill)
+ {
+ return PolyFill.isPause;
+ }
+
+ return (bool)_debugFeatureType.GetField("_debugPause", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(_debugFeatureOriginal);
+ }
+
+ set
+ {
+ if (IsPolyfill)
+ {
+ PolyFill.isPause = value;
+ }
+ else
+ {
+ _debugFeatureType.GetField("_debugPause", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(_debugFeatureOriginal, value);
+ }
+
+ SoundManager.PauseMusic(value);
+ _gameMovie.Pause(value);
+ NotesManager.Pause(value);
+ }
+ }
+
+ public static void Seek(int msec)
+ {
+ Singleton.Instance.Initialize();
if (IsPolyfill)
{
- PolyFill.isPause = pause;
+ PolyFill.DebugTimeSkip(msec);
}
else
{
- var debugFeatureClass = typeof(GameProcess).GetNestedType("DebugFeature", BindingFlags.Instance | BindingFlags.NonPublic);
- debugFeatureClass?.GetField("_debugPause", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(_debugFeatureOriginal, pause);
+ _debugFeatureType.GetMethod("DebugTimeSkip", BindingFlags.Instance | BindingFlags.NonPublic)?.Invoke(_debugFeatureOriginal, new object[] { msec });
+ }
+ }
+
+ public static double CurrentPlayMsec
+ {
+ get
+ {
+ if (IsPolyfill)
+ {
+ return PolyFill.timer;
+ }
+
+ return (double)_debugFeatureType.GetField("_debugTimer", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(_debugFeatureOriginal);
+ }
+ set
+ {
+ if (IsPolyfill)
+ {
+ PolyFill.timer = value;
+ }
+ else
+ {
+ _debugFeatureType.GetField("_debugTimer", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(_debugFeatureOriginal, value);
+ }
+
+ Seek(0);
}
-
- SoundManager.PauseMusic(pause);
- _gameMovie.Pause(pause);
- NotesManager.Pause(pause);
}
- [HarmonyPatch]
private static class GetOriginal
{
[HarmonyPatch(typeof(GameProcess), "OnStart")]
@@ -71,7 +123,6 @@ public class DebugFeature
}
}
- [HarmonyPatch]
private static class PolyFill
{
public static bool isPause;
diff --git a/AquaMai/Helpers/MessageHelper.cs b/AquaMai/Helpers/MessageHelper.cs
index 6dbacfef..0d66c603 100644
--- a/AquaMai/Helpers/MessageHelper.cs
+++ b/AquaMai/Helpers/MessageHelper.cs
@@ -31,7 +31,7 @@ public class MessageHelper
replaceText = true,
text = message,
changeSize = true,
- sizeID = size
+ sizeID = size,
});
}
}
diff --git a/AquaMai/Utils/PractiseMode.cs b/AquaMai/Utils/PractiseMode.cs
new file mode 100644
index 00000000..efc714d4
--- /dev/null
+++ b/AquaMai/Utils/PractiseMode.cs
@@ -0,0 +1,89 @@
+using AquaMai.Fix;
+using AquaMai.Helpers;
+using HarmonyLib;
+using MelonLoader;
+using Monitor;
+using Process;
+using UnityEngine;
+using UrGUI.GUIWindow;
+
+namespace AquaMai.Utils;
+
+public class PractiseMode
+{
+ private static double repeatStart = -1;
+ private static double repeatEnd = -1;
+
+ private static void SetRepeatEnd(double time)
+ {
+ if (repeatStart == -1)
+ {
+ MessageHelper.ShowMessage("Please set repeat start time first");
+ return;
+ }
+
+ if (time < repeatStart)
+ {
+ MessageHelper.ShowMessage("Repeat end time cannot be less than repeat start time");
+ return;
+ }
+
+ repeatEnd = time;
+ }
+
+ private static void ClearRepeat()
+ {
+ repeatStart = -1;
+ repeatEnd = -1;
+ }
+
+ private class DebugWindow : MonoBehaviour
+ {
+ private GUIWindow window;
+
+ public void Start()
+ {
+ window = GUIWindow.Begin("练习模式 测试");
+ window.Button("暂停", () => DebugFeature.Pause = !DebugFeature.Pause);
+ window.SameLine();
+ window.Button("向前", () => DebugFeature.Seek(-1000));
+ window.Button("向后", () => DebugFeature.Seek(1000));
+ window.SameLine(1, 1, 1);
+ window.Button("循环开始", () => repeatStart = DebugFeature.CurrentPlayMsec);
+ window.Button("循环结束", () => SetRepeatEnd(DebugFeature.CurrentPlayMsec));
+ window.Button("循环解除", ClearRepeat);
+ }
+
+ private void OnGUI()
+ {
+ window?.Draw();
+ }
+ }
+
+ private static DebugWindow debugWin;
+
+ [HarmonyPatch(typeof(GameProcess), "OnUpdate")]
+ [HarmonyPostfix]
+ public static void GameProcessPostUpdate(GameProcess __instance, GameMonitor[] ____monitors)
+ {
+ if (Input.GetKeyDown(KeyCode.F12))
+ {
+ if (debugWin is null)
+ {
+ debugWin = ____monitors[0].gameObject.AddComponent();
+ }
+ else
+ {
+ MelonLogger.Msg("[PractiseMode] 调试窗口作用中");
+ }
+ }
+
+ if (repeatStart >= 0 && repeatEnd >= 0)
+ {
+ if (DebugFeature.CurrentPlayMsec >= repeatEnd)
+ {
+ DebugFeature.CurrentPlayMsec = repeatStart;
+ }
+ }
+ }
+}