mirror of
https://github.com/MewoLab/AquaDX.git
synced 2026-02-07 10:57:26 +08:00
[+] Configurable mod key map manager
This commit is contained in:
54
AquaMai/ModKeyMap/Config.cs
Normal file
54
AquaMai/ModKeyMap/Config.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using AquaMai.Attributes;
|
||||
|
||||
namespace AquaMai.ModKeyMap;
|
||||
|
||||
public class Config
|
||||
{
|
||||
[ConfigComment(
|
||||
en: "Skip to next step",
|
||||
zh: """
|
||||
跳过登录过程中的界面直接进入选歌界面
|
||||
在选歌界面直接结束游戏
|
||||
""")]
|
||||
public ModKeyCode QuickSkip { get; set; } = ModKeyCode.None;
|
||||
|
||||
public bool QuickSkipLongPress { get; set; }
|
||||
|
||||
[ConfigComment(
|
||||
en: "Quick retry in-game",
|
||||
zh: "游戏内快速重试")]
|
||||
public ModKeyCode InGameRetry { get; set; } = ModKeyCode.None;
|
||||
|
||||
public bool InGameRetryLongPress { get; set; }
|
||||
|
||||
[ConfigComment(
|
||||
en: "Quick skip in-game",
|
||||
zh: "游戏内快速跳过")]
|
||||
public ModKeyCode InGameSkip { get; set; } = ModKeyCode.None;
|
||||
|
||||
public bool InGameSkipLongPress { get; set; }
|
||||
|
||||
[ConfigComment(
|
||||
en: "Enter game test mode",
|
||||
zh: "进入游戏测试模式")]
|
||||
public ModKeyCode TestMode { get; set; } = ModKeyCode.Test;
|
||||
|
||||
public bool TestModeLongPress { get; set; }
|
||||
|
||||
[ConfigComment(
|
||||
zh: "练习模式")]
|
||||
public ModKeyCode PractiseMode { get; set; } = ModKeyCode.None;
|
||||
|
||||
public bool PractiseModeLongPress { get; set; }
|
||||
|
||||
[ConfigComment(
|
||||
zh: "选歌界面隐藏所有自制谱")]
|
||||
public ModKeyCode HideSelfMadeCharts { get; set; } = ModKeyCode.None;
|
||||
|
||||
public bool HideSelfMadeChartsLongPress { get; set; }
|
||||
|
||||
[ConfigComment(
|
||||
en: "Hold the bottom four buttons (3456) for official quick retry (non-utage only)",
|
||||
zh: "按住下方四个按钮(3456)使用官方快速重开(仅对非宴谱有效)")]
|
||||
public bool EnableNativeQuickRetry { get; set; }
|
||||
}
|
||||
19
AquaMai/ModKeyMap/EnableNativeQuickRetry.cs
Normal file
19
AquaMai/ModKeyMap/EnableNativeQuickRetry.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using AquaMai.Attributes;
|
||||
using HarmonyLib;
|
||||
using Manager;
|
||||
using Monitor;
|
||||
|
||||
namespace AquaMai.ModKeyMap;
|
||||
|
||||
[GameVersion(23000)]
|
||||
public class EnableNativeQuickRetry
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(QuickRetry), "IsQuickRetryEnable")]
|
||||
public static bool OnQuickRetryIsQuickRetryEnable(ref bool __result)
|
||||
{
|
||||
var isUtageProperty = Traverse.Create(typeof(GameManager)).Property("IsUtage");
|
||||
__result = !isUtageProperty.PropertyExists() || !isUtageProperty.GetValue<bool>();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
113
AquaMai/ModKeyMap/HideSelfMadeCharts.cs
Normal file
113
AquaMai/ModKeyMap/HideSelfMadeCharts.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using AquaMai.Helpers;
|
||||
using HarmonyLib;
|
||||
using MAI2.Util;
|
||||
using Manager;
|
||||
using MelonLoader;
|
||||
using Process;
|
||||
using UnityEngine;
|
||||
using Util;
|
||||
|
||||
namespace AquaMai.ModKeyMap;
|
||||
|
||||
public class HideSelfMadeCharts
|
||||
{
|
||||
private static Safe.ReadonlySortedDictionary<int, Manager.MaiStudio.MusicData> _musics;
|
||||
private static Safe.ReadonlySortedDictionary<int, Manager.MaiStudio.MusicData> _musicsNoneSelfMade;
|
||||
|
||||
private static bool isShowSelfMadeCharts = true;
|
||||
private static bool isForceDisable;
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(DataManager), "GetMusics")]
|
||||
public static void GetMusics(ref Safe.ReadonlySortedDictionary<int, Manager.MaiStudio.MusicData> __result, List<string> ____targetDirs)
|
||||
{
|
||||
if (_musics is null)
|
||||
{
|
||||
// init musics for the first time
|
||||
if (__result.Count == 0) return;
|
||||
_musics = __result;
|
||||
var nonSelfMadeList = new SortedDictionary<int, Manager.MaiStudio.MusicData>();
|
||||
var officialDirs = ____targetDirs.Where(it => File.Exists(Path.Combine(it, "DataConfig.xml")) || File.Exists(Path.Combine(it, "OfficialChartsMark.txt")));
|
||||
foreach (var music in __result)
|
||||
{
|
||||
if (officialDirs.Any(it => MusicDirHelper.LookupPath(music.Value).StartsWith(it)))
|
||||
{
|
||||
nonSelfMadeList.Add(music.Key, music.Value);
|
||||
}
|
||||
}
|
||||
|
||||
_musicsNoneSelfMade = new Safe.ReadonlySortedDictionary<int, Manager.MaiStudio.MusicData>(nonSelfMadeList);
|
||||
MelonLogger.Msg($"[HideSelfMadeCharts] All music count: {__result.Count}, Official music count: {_musicsNoneSelfMade.Count}");
|
||||
}
|
||||
|
||||
var stackTrace = new StackTrace(); // get call stack
|
||||
var stackFrames = stackTrace.GetFrames(); // get method calls (frames)
|
||||
if (stackFrames.All(it => it.GetMethod().DeclaringType.Name != "MusicSelectProcess")) return;
|
||||
if (isShowSelfMadeCharts && !isForceDisable) return;
|
||||
__result = _musicsNoneSelfMade;
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(MusicSelectProcess), "OnUpdate")]
|
||||
public static void MusicSelectProcessOnUpdate(ref MusicSelectProcess __instance)
|
||||
{
|
||||
if (isForceDisable) return;
|
||||
if (!ModKeyListener.GetKeyDownOrLongPress(AquaMai.AppConfig.ModKeyMap.HideSelfMadeCharts, AquaMai.AppConfig.ModKeyMap.HideSelfMadeChartsLongPress)) return;
|
||||
isShowSelfMadeCharts = !isShowSelfMadeCharts;
|
||||
MelonLogger.Msg($"[HideSelfMadeCharts] isShowSelfMadeCharts: {isShowSelfMadeCharts}");
|
||||
SharedInstances.ProcessDataContainer.processManager.AddProcess(new FadeProcess(SharedInstances.ProcessDataContainer, __instance, new MusicSelectProcess(SharedInstances.ProcessDataContainer)));
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
MessageHelper.ShowMessage($"{(isShowSelfMadeCharts ? "Show" : "Hide")} Self-Made Charts");
|
||||
});
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(MusicSelectProcess), "OnStart")]
|
||||
public static void MusicSelectProcessOnStart(ref MusicSelectProcess __instance)
|
||||
{
|
||||
if (File.Exists(Path.Combine(Environment.CurrentDirectory, "LocalAssets", "SelfMadeChartsDenyUsers.txt")))
|
||||
{
|
||||
var userIds = File.ReadAllLines(Path.Combine(Environment.CurrentDirectory, "LocalAssets", "SelfMadeChartsDenyUsers.txt"));
|
||||
for (var i = 0; i < 2; i++)
|
||||
{
|
||||
var user = Singleton<UserDataManager>.Instance.GetUserData(i);
|
||||
if (!user.IsEntry) continue;
|
||||
if (!userIds.Contains(user.Detail.UserID.ToString())) continue;
|
||||
isForceDisable = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (File.Exists(Path.Combine(Environment.CurrentDirectory, "LocalAssets", "SelfMadeChartsWhiteListUsers.txt")))
|
||||
{
|
||||
var userIds = File.ReadAllLines(Path.Combine(Environment.CurrentDirectory, "LocalAssets", "SelfMadeChartsWhiteListUsers.txt"));
|
||||
for (var i = 0; i < 2; i++)
|
||||
{
|
||||
var user = Singleton<UserDataManager>.Instance.GetUserData(i);
|
||||
if (!user.IsEntry) continue;
|
||||
if (userIds.Contains(user.Detail.UserID.ToString())) continue;
|
||||
isForceDisable = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
isForceDisable = false;
|
||||
}
|
||||
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(EntryProcess), "OnStart")]
|
||||
public static void EntryProcessOnStart(ref EntryProcess __instance)
|
||||
{
|
||||
// reset status on login
|
||||
isShowSelfMadeCharts = true;
|
||||
}
|
||||
}
|
||||
53
AquaMai/ModKeyMap/ModKeyCode.cs
Normal file
53
AquaMai/ModKeyMap/ModKeyCode.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
namespace AquaMai.ModKeyMap;
|
||||
|
||||
public enum ModKeyCode
|
||||
{
|
||||
None,
|
||||
Alpha0,
|
||||
Alpha1,
|
||||
Alpha2,
|
||||
Alpha3,
|
||||
Alpha4,
|
||||
Alpha5,
|
||||
Alpha6,
|
||||
Alpha7,
|
||||
Alpha8,
|
||||
Alpha9,
|
||||
Keypad0,
|
||||
Keypad1,
|
||||
Keypad2,
|
||||
Keypad3,
|
||||
Keypad4,
|
||||
Keypad5,
|
||||
Keypad6,
|
||||
Keypad7,
|
||||
Keypad8,
|
||||
Keypad9,
|
||||
F1,
|
||||
F2,
|
||||
F3,
|
||||
F4,
|
||||
F5,
|
||||
F6,
|
||||
F7,
|
||||
F8,
|
||||
F9,
|
||||
F10,
|
||||
F11,
|
||||
F12,
|
||||
Insert,
|
||||
Delete,
|
||||
Home,
|
||||
End,
|
||||
PageUp,
|
||||
PageDown,
|
||||
UpArrow,
|
||||
DownArrow,
|
||||
LeftArrow,
|
||||
RightArrow,
|
||||
|
||||
Select1P,
|
||||
Select2P,
|
||||
Service,
|
||||
Test,
|
||||
}
|
||||
145
AquaMai/ModKeyMap/ModKeyListener.cs
Normal file
145
AquaMai/ModKeyMap/ModKeyListener.cs
Normal file
@@ -0,0 +1,145 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using HarmonyLib;
|
||||
using Main;
|
||||
using Manager;
|
||||
using MelonLoader;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AquaMai.ModKeyMap;
|
||||
|
||||
public static class ModKeyListener
|
||||
{
|
||||
private static readonly Dictionary<ModKeyCode, int> _keyPressFrames = new();
|
||||
private static readonly Dictionary<ModKeyCode, int> _keyPressFramesPrev = new();
|
||||
|
||||
static ModKeyListener()
|
||||
{
|
||||
foreach (ModKeyCode key in Enum.GetValues(typeof(ModKeyCode)))
|
||||
{
|
||||
_keyPressFrames[key] = 0;
|
||||
_keyPressFramesPrev[key] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(GameMainObject), "Update")]
|
||||
public static void CheckLongPush()
|
||||
{
|
||||
foreach (ModKeyCode key in Enum.GetValues(typeof(ModKeyCode)))
|
||||
{
|
||||
_keyPressFramesPrev[key] = _keyPressFrames[key];
|
||||
if (GetKeyPush(key))
|
||||
{
|
||||
# if DEBUG
|
||||
MelonLogger.Msg($"CheckLongPush {key} is push {_keyPressFrames[key]}");
|
||||
# endif
|
||||
_keyPressFrames[key]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
_keyPressFrames[key] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool GetKeyPush(ModKeyCode key) =>
|
||||
key switch
|
||||
{
|
||||
ModKeyCode.None => false,
|
||||
< ModKeyCode.Select1P => Input.GetKey(key.GetKeyCode()),
|
||||
ModKeyCode.Test => InputManager.GetSystemInputPush(InputManager.SystemButtonSetting.ButtonTest),
|
||||
ModKeyCode.Service => InputManager.GetSystemInputPush(InputManager.SystemButtonSetting.ButtonService),
|
||||
ModKeyCode.Select1P => InputManager.GetButtonPush(0, InputManager.ButtonSetting.Select),
|
||||
ModKeyCode.Select2P => InputManager.GetButtonPush(1, InputManager.ButtonSetting.Select),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(key), key, "我也不知道这是什么键")
|
||||
};
|
||||
|
||||
public static bool GetKeyDown(ModKeyCode key)
|
||||
{
|
||||
// return key switch
|
||||
// {
|
||||
// ModKeyCode.None => false,
|
||||
// < ModKeyCode.Select1P => Input.GetKeyDown(key.GetKeyCode()),
|
||||
// ModKeyCode.Test => InputManager.GetSystemInputDown(InputManager.SystemButtonSetting.ButtonTest),
|
||||
// ModKeyCode.Service => InputManager.GetSystemInputDown(InputManager.SystemButtonSetting.ButtonService),
|
||||
// ModKeyCode.Select1P => InputManager.GetButtonDown(0, InputManager.ButtonSetting.Select),
|
||||
// ModKeyCode.Select2P => InputManager.GetButtonDown(1, InputManager.ButtonSetting.Select),
|
||||
// _ => throw new ArgumentOutOfRangeException(nameof(key), key, "我也不知道这是什么键")
|
||||
// };
|
||||
|
||||
// 不用这个,我们检测按键是否弹起以及弹起之前按下的时间是否小于 30,这样可以防止要长按时按下的时候就触发
|
||||
return _keyPressFrames[key] == 0 && 0 < _keyPressFramesPrev[key] && _keyPressFramesPrev[key] < 30;
|
||||
}
|
||||
|
||||
public static bool GetKeyDownOrLongPress(ModKeyCode key, bool isLongPress)
|
||||
{
|
||||
bool ret;
|
||||
if (isLongPress)
|
||||
{
|
||||
ret = _keyPressFrames[key] == 60;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = GetKeyDown(key);
|
||||
}
|
||||
|
||||
# if DEBUG
|
||||
if (ret)
|
||||
{
|
||||
MelonLogger.Msg($"Key {key} is pressed");
|
||||
MelonLogger.Msg(new StackTrace());
|
||||
}
|
||||
# endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static KeyCode GetKeyCode(this ModKeyCode modKeyCode) =>
|
||||
modKeyCode switch
|
||||
{
|
||||
ModKeyCode.Alpha0 => KeyCode.Alpha0,
|
||||
ModKeyCode.Alpha1 => KeyCode.Alpha1,
|
||||
ModKeyCode.Alpha2 => KeyCode.Alpha2,
|
||||
ModKeyCode.Alpha3 => KeyCode.Alpha3,
|
||||
ModKeyCode.Alpha4 => KeyCode.Alpha4,
|
||||
ModKeyCode.Alpha5 => KeyCode.Alpha5,
|
||||
ModKeyCode.Alpha6 => KeyCode.Alpha6,
|
||||
ModKeyCode.Alpha7 => KeyCode.Alpha7,
|
||||
ModKeyCode.Alpha8 => KeyCode.Alpha8,
|
||||
ModKeyCode.Alpha9 => KeyCode.Alpha9,
|
||||
ModKeyCode.Keypad0 => KeyCode.Keypad0,
|
||||
ModKeyCode.Keypad1 => KeyCode.Keypad1,
|
||||
ModKeyCode.Keypad2 => KeyCode.Keypad2,
|
||||
ModKeyCode.Keypad3 => KeyCode.Keypad3,
|
||||
ModKeyCode.Keypad4 => KeyCode.Keypad4,
|
||||
ModKeyCode.Keypad5 => KeyCode.Keypad5,
|
||||
ModKeyCode.Keypad6 => KeyCode.Keypad6,
|
||||
ModKeyCode.Keypad7 => KeyCode.Keypad7,
|
||||
ModKeyCode.Keypad8 => KeyCode.Keypad8,
|
||||
ModKeyCode.Keypad9 => KeyCode.Keypad9,
|
||||
ModKeyCode.F1 => KeyCode.F1,
|
||||
ModKeyCode.F2 => KeyCode.F2,
|
||||
ModKeyCode.F3 => KeyCode.F3,
|
||||
ModKeyCode.F4 => KeyCode.F4,
|
||||
ModKeyCode.F5 => KeyCode.F5,
|
||||
ModKeyCode.F6 => KeyCode.F6,
|
||||
ModKeyCode.F7 => KeyCode.F7,
|
||||
ModKeyCode.F8 => KeyCode.F8,
|
||||
ModKeyCode.F9 => KeyCode.F9,
|
||||
ModKeyCode.F10 => KeyCode.F10,
|
||||
ModKeyCode.F11 => KeyCode.F11,
|
||||
ModKeyCode.F12 => KeyCode.F12,
|
||||
ModKeyCode.Insert => KeyCode.Insert,
|
||||
ModKeyCode.Delete => KeyCode.Delete,
|
||||
ModKeyCode.Home => KeyCode.Home,
|
||||
ModKeyCode.End => KeyCode.End,
|
||||
ModKeyCode.PageUp => KeyCode.PageUp,
|
||||
ModKeyCode.PageDown => KeyCode.PageDown,
|
||||
ModKeyCode.UpArrow => KeyCode.UpArrow,
|
||||
ModKeyCode.DownArrow => KeyCode.DownArrow,
|
||||
ModKeyCode.LeftArrow => KeyCode.LeftArrow,
|
||||
ModKeyCode.RightArrow => KeyCode.RightArrow,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(modKeyCode), modKeyCode, "游戏功能键需要单独处理")
|
||||
};
|
||||
}
|
||||
250
AquaMai/ModKeyMap/PractiseMode.cs
Normal file
250
AquaMai/ModKeyMap/PractiseMode.cs
Normal file
@@ -0,0 +1,250 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using AquaMai.Fix;
|
||||
using AquaMai.Helpers;
|
||||
using AquaMai.Resources;
|
||||
using AquaMai.Utils;
|
||||
using HarmonyLib;
|
||||
using Manager;
|
||||
using Monitor;
|
||||
using Monitor.Game;
|
||||
using Process;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AquaMai.ModKeyMap;
|
||||
|
||||
public class PractiseMode
|
||||
{
|
||||
public static double repeatStart = -1;
|
||||
public static double repeatEnd = -1;
|
||||
public static float speed = 1;
|
||||
private static CriAtomExPlayer player;
|
||||
private static MovieMaterialMai2 movie;
|
||||
public static GameCtrl gameCtrl;
|
||||
public static bool keepNoteSpeed = false;
|
||||
|
||||
public static void SetRepeatEnd(double time)
|
||||
{
|
||||
if (repeatStart == -1)
|
||||
{
|
||||
MessageHelper.ShowMessage(Locale.RepeatStartTimeNotSet);
|
||||
return;
|
||||
}
|
||||
|
||||
if (time < repeatStart)
|
||||
{
|
||||
MessageHelper.ShowMessage(Locale.RepeatEndTimeLessThenStartTime);
|
||||
return;
|
||||
}
|
||||
|
||||
repeatEnd = time;
|
||||
}
|
||||
|
||||
public static void ClearRepeat()
|
||||
{
|
||||
repeatStart = -1;
|
||||
repeatEnd = -1;
|
||||
}
|
||||
|
||||
public static void SetSpeed()
|
||||
{
|
||||
player.SetPitch((float)(1200 * Math.Log(speed, 2)));
|
||||
// player.SetDspTimeStretchRatio(1 / speed);
|
||||
player.UpdateAll();
|
||||
|
||||
movie.player.SetSpeed(speed);
|
||||
gameCtrl?.ResetOptionSpeed();
|
||||
}
|
||||
|
||||
private static IEnumerator SetSpeedCoroutineInner()
|
||||
{
|
||||
yield return null;
|
||||
SetSpeed();
|
||||
}
|
||||
|
||||
public static void SetSpeedCoroutine()
|
||||
{
|
||||
SharedInstances.GameMainObject.StartCoroutine(SetSpeedCoroutineInner());
|
||||
}
|
||||
|
||||
public static void SpeedUp()
|
||||
{
|
||||
speed += .05f;
|
||||
if (speed > 2)
|
||||
{
|
||||
speed = 2;
|
||||
}
|
||||
|
||||
SetSpeed();
|
||||
}
|
||||
|
||||
public static void SpeedDown()
|
||||
{
|
||||
speed -= .05f;
|
||||
if (speed < 0.05)
|
||||
{
|
||||
speed = 0.05f;
|
||||
}
|
||||
|
||||
SetSpeed();
|
||||
}
|
||||
|
||||
public static void SpeedReset()
|
||||
{
|
||||
speed = 1;
|
||||
SetSpeed();
|
||||
}
|
||||
|
||||
public static void Seek(int addMsec)
|
||||
{
|
||||
// Debug feature 里面那个 timer 不能感知变速
|
||||
// 为了和魔改版本统一,polyfill 里面不修这个
|
||||
// 这里重新实现一个能感知变速的 Seek
|
||||
var msec = CurrentPlayMsec + addMsec;
|
||||
if (msec < 0)
|
||||
{
|
||||
msec = 0;
|
||||
}
|
||||
|
||||
CurrentPlayMsec = msec;
|
||||
}
|
||||
|
||||
public static double CurrentPlayMsec
|
||||
{
|
||||
get => NotesManager.GetCurrentMsec() - 91;
|
||||
set
|
||||
{
|
||||
DebugFeature.CurrentPlayMsec = value;
|
||||
SetSpeedCoroutine();
|
||||
}
|
||||
}
|
||||
|
||||
public static PractiseModeUI ui;
|
||||
|
||||
[HarmonyPatch]
|
||||
public class PatchNoteSpeed
|
||||
{
|
||||
public static IEnumerable<MethodBase> TargetMethods()
|
||||
{
|
||||
yield return AccessTools.Method(typeof(GameManager), "GetNoteSpeed");
|
||||
yield return AccessTools.Method(typeof(GameManager), "GetTouchSpeed");
|
||||
}
|
||||
|
||||
public static void Postfix(ref float __result)
|
||||
{
|
||||
if (!keepNoteSpeed) return;
|
||||
__result /= speed;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(GameProcess), "OnStart")]
|
||||
[HarmonyPostfix]
|
||||
public static void GameProcessPostStart()
|
||||
{
|
||||
repeatStart = -1;
|
||||
repeatEnd = -1;
|
||||
speed = 1;
|
||||
ui = null;
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(GameCtrl), "Initialize")]
|
||||
[HarmonyPostfix]
|
||||
public static void GameCtrlPostInitialize(GameCtrl __instance)
|
||||
{
|
||||
gameCtrl = __instance;
|
||||
}
|
||||
|
||||
# if DEBUG
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(GenericProcess), "OnUpdate")]
|
||||
public static void OnGenericProcessUpdate(GenericMonitor[] ____monitors)
|
||||
{
|
||||
if (Input.GetKeyDown(KeyCode.F11))
|
||||
{
|
||||
____monitors[0].gameObject.AddComponent<PractiseModeUI>();
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
[HarmonyPatch(typeof(GameProcess), "OnUpdate")]
|
||||
[HarmonyPostfix]
|
||||
public static void GameProcessPostUpdate(GameProcess __instance, GameMonitor[] ____monitors)
|
||||
{
|
||||
if (ModKeyListener.GetKeyDownOrLongPress(AquaMai.AppConfig.ModKeyMap.PractiseMode, AquaMai.AppConfig.ModKeyMap.PractiseModeLongPress) && ui is null)
|
||||
{
|
||||
ui = ____monitors[0].gameObject.AddComponent<PractiseModeUI>();
|
||||
}
|
||||
|
||||
if (repeatStart >= 0 && repeatEnd >= 0)
|
||||
{
|
||||
if (CurrentPlayMsec >= repeatEnd)
|
||||
{
|
||||
CurrentPlayMsec = repeatStart;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static float startGap = -1f;
|
||||
|
||||
[HarmonyPatch(typeof(NotesManager), "StartPlay")]
|
||||
[HarmonyPostfix]
|
||||
public static void NotesManagerPostUpdateTimer(float msecStartGap)
|
||||
{
|
||||
startGap = msecStartGap;
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(NotesManager), "UpdateTimer")]
|
||||
[HarmonyPrefix]
|
||||
public static bool NotesManagerPostUpdateTimer(bool ____isPlaying, Stopwatch ____stopwatch, ref float ____curMSec, ref float ____curMSecPre, float ____msecStartGap)
|
||||
{
|
||||
if (startGap != -1f)
|
||||
{
|
||||
____curMSec = startGap;
|
||||
____curMSecPre = startGap;
|
||||
____stopwatch?.Reset();
|
||||
startGap = -1f;
|
||||
}
|
||||
else
|
||||
{
|
||||
____curMSecPre = ____curMSec;
|
||||
if (____isPlaying && ____stopwatch != null && !DebugFeature.Pause)
|
||||
{
|
||||
var num = (double)____stopwatch.ElapsedTicks / Stopwatch.Frequency * 1000.0 * speed;
|
||||
____curMSec += (float)num;
|
||||
____stopwatch.Reset();
|
||||
____stopwatch.Start();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(SoundCtrl), "Initialize")]
|
||||
[HarmonyPostfix]
|
||||
public static void SoundCtrlPostInitialize(SoundCtrl.InitParam param, Dictionary<int, object> ____players)
|
||||
{
|
||||
var wrapper = ____players[2];
|
||||
player = (CriAtomExPlayer)wrapper.GetType().GetField("Player").GetValue(wrapper);
|
||||
// var pool = new CriAtomExStandardVoicePool(1, 8, 96000, true, 2);
|
||||
// pool.AttachDspTimeStretch();
|
||||
// player.SetVoicePoolIdentifier(pool.identifier);
|
||||
|
||||
// debug
|
||||
// var wrapper1 = ____players[7];
|
||||
// var player1 = (CriAtomExPlayer)wrapper1.GetType().GetField("Player").GetValue(wrapper1);
|
||||
// var pool = new CriAtomExStandardVoicePool(1, 8, 96000, true, 2);
|
||||
// pool.AttachDspTimeStretch();
|
||||
// player1.SetVoicePoolIdentifier(pool.identifier);
|
||||
// player1.SetDspTimeStretchRatio(2);
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(MovieController), "Awake")]
|
||||
[HarmonyPostfix]
|
||||
public static void MovieControllerPostAwake(MovieMaterialMai2 ____moviePlayers)
|
||||
{
|
||||
movie = ____moviePlayers;
|
||||
}
|
||||
}
|
||||
151
AquaMai/ModKeyMap/PractiseModeUI.cs
Normal file
151
AquaMai/ModKeyMap/PractiseModeUI.cs
Normal file
@@ -0,0 +1,151 @@
|
||||
using System;
|
||||
using AquaMai.Fix;
|
||||
using AquaMai.Helpers;
|
||||
using AquaMai.Resources;
|
||||
using Manager;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AquaMai.ModKeyMap;
|
||||
|
||||
public class PractiseModeUI : MonoBehaviour
|
||||
{
|
||||
private static float windowTop => Screen.height - GuiSizes.PlayerWidth + GuiSizes.PlayerWidth * .22f;
|
||||
private static float controlHeight => GuiSizes.PlayerWidth * .13f;
|
||||
private static float sideButtonWidth => GuiSizes.PlayerWidth * .1f;
|
||||
private static float centerButtonWidth => GuiSizes.PlayerWidth * .28f;
|
||||
private static int fontSize => (int)(GuiSizes.PlayerWidth * .02f);
|
||||
|
||||
private static Rect GetButtonRect(int pos, int row)
|
||||
{
|
||||
float x;
|
||||
float width;
|
||||
switch (pos)
|
||||
{
|
||||
case 0:
|
||||
x = GuiSizes.PlayerCenter - centerButtonWidth / 2 - sideButtonWidth - GuiSizes.Margin;
|
||||
width = sideButtonWidth;
|
||||
break;
|
||||
case 1:
|
||||
x = GuiSizes.PlayerCenter - centerButtonWidth / 2;
|
||||
width = centerButtonWidth;
|
||||
break;
|
||||
case 2:
|
||||
x = GuiSizes.PlayerCenter + centerButtonWidth / 2 + GuiSizes.Margin;
|
||||
width = sideButtonWidth;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(pos), pos, null);
|
||||
}
|
||||
|
||||
return new Rect(x, windowTop + (GuiSizes.Margin + controlHeight) * row + GuiSizes.Margin, width, controlHeight);
|
||||
}
|
||||
|
||||
public void OnGUI()
|
||||
{
|
||||
var labelStyle = GUI.skin.GetStyle("label");
|
||||
labelStyle.fontSize = fontSize;
|
||||
labelStyle.alignment = TextAnchor.MiddleCenter;
|
||||
|
||||
var buttonStyle = GUI.skin.GetStyle("button");
|
||||
buttonStyle.fontSize = fontSize;
|
||||
|
||||
GUI.Box(new Rect(
|
||||
GuiSizes.PlayerCenter - centerButtonWidth / 2 - sideButtonWidth - GuiSizes.Margin * 2,
|
||||
windowTop,
|
||||
centerButtonWidth + sideButtonWidth * 2 + GuiSizes.Margin * 4,
|
||||
controlHeight * 4 + GuiSizes.Margin * 5
|
||||
), "");
|
||||
|
||||
GUI.Button(GetButtonRect(0, 0), Locale.SeekBackward);
|
||||
GUI.Button(GetButtonRect(1, 0), Locale.Pause);
|
||||
GUI.Button(GetButtonRect(2, 0), Locale.SeekForward);
|
||||
|
||||
if (PractiseMode.repeatStart == -1)
|
||||
{
|
||||
GUI.Button(GetButtonRect(0, 1), Locale.MarkRepeatStart);
|
||||
GUI.Label(GetButtonRect(1, 1), Locale.RepeatNotSet);
|
||||
}
|
||||
else if (PractiseMode.repeatEnd == -1)
|
||||
{
|
||||
GUI.Button(GetButtonRect(0, 1), Locale.MarkRepeatEnd);
|
||||
GUI.Label(GetButtonRect(1, 1), Locale.RepeatStartSet);
|
||||
GUI.Button(GetButtonRect(2, 1), Locale.RepeatReset);
|
||||
}
|
||||
else
|
||||
{
|
||||
GUI.Label(GetButtonRect(1, 1), Locale.RepeatStartEndSet);
|
||||
GUI.Button(GetButtonRect(2, 1), Locale.RepeatReset);
|
||||
}
|
||||
|
||||
GUI.Button(GetButtonRect(0, 2), Locale.SpeedDown);
|
||||
GUI.Label(GetButtonRect(1, 2), $"{Locale.Speed} {PractiseMode.speed * 100:000}%");
|
||||
GUI.Button(GetButtonRect(2, 2), Locale.SpeedUp);
|
||||
GUI.Button(GetButtonRect(1, 3), Locale.SpeedReset);
|
||||
|
||||
GUI.Label(GetButtonRect(0, 3), $"{TimeSpan.FromMilliseconds(PractiseMode.CurrentPlayMsec):mm\\:ss\\.fff}\n{TimeSpan.FromMilliseconds(NotesManager.Instance().getPlayFinalMsec()):mm\\:ss\\.fff}");
|
||||
GUI.Button(GetButtonRect(2, 3), $"保持流速\n{(PractiseMode.keepNoteSpeed ? "ON" : "OFF")}");
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.E8))
|
||||
{
|
||||
PractiseMode.Seek(-1000);
|
||||
}
|
||||
else if (InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.E2))
|
||||
{
|
||||
PractiseMode.Seek(1000);
|
||||
}
|
||||
else if (InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.B8) || InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.B1))
|
||||
{
|
||||
DebugFeature.Pause = !DebugFeature.Pause;
|
||||
if (!DebugFeature.Pause)
|
||||
{
|
||||
PractiseMode.Seek(0);
|
||||
}
|
||||
}
|
||||
else if (InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.B7) && PractiseMode.repeatStart == -1)
|
||||
{
|
||||
PractiseMode.repeatStart = PractiseMode.CurrentPlayMsec;
|
||||
}
|
||||
else if (InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.B7) && PractiseMode.repeatEnd == -1)
|
||||
{
|
||||
PractiseMode.SetRepeatEnd(PractiseMode.CurrentPlayMsec);
|
||||
}
|
||||
else if (InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.B2))
|
||||
{
|
||||
PractiseMode.ClearRepeat();
|
||||
}
|
||||
else if (InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.B6))
|
||||
{
|
||||
PractiseMode.SpeedDown();
|
||||
}
|
||||
else if (InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.B3))
|
||||
{
|
||||
PractiseMode.SpeedUp();
|
||||
}
|
||||
else if (InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.B5) || InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.B4))
|
||||
{
|
||||
PractiseMode.SpeedReset();
|
||||
}
|
||||
else if (InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.E4))
|
||||
{
|
||||
PractiseMode.keepNoteSpeed = !PractiseMode.keepNoteSpeed;
|
||||
PractiseMode.gameCtrl?.ResetOptionSpeed();
|
||||
}
|
||||
else if (
|
||||
InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.A1) ||
|
||||
InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.A2) ||
|
||||
InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.A3) ||
|
||||
InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.A4) ||
|
||||
InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.A5) ||
|
||||
InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.A6) ||
|
||||
InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.A7) ||
|
||||
InputManager.GetTouchPanelAreaDown(InputManager.TouchPanelArea.A8)
|
||||
)
|
||||
{
|
||||
PractiseMode.ui = null;
|
||||
Destroy(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
74
AquaMai/ModKeyMap/QuickSkip.cs
Normal file
74
AquaMai/ModKeyMap/QuickSkip.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using System.Collections.Generic;
|
||||
using AquaMai.Helpers;
|
||||
using HarmonyLib;
|
||||
using Mai2.Mai2Cue;
|
||||
using MAI2.Util;
|
||||
using Main;
|
||||
using Manager;
|
||||
using MelonLoader;
|
||||
using Process;
|
||||
|
||||
namespace AquaMai.ModKeyMap;
|
||||
|
||||
public class QuickSkip
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(GameMainObject), "Update")]
|
||||
public static void OnGameMainObjectUpdate()
|
||||
{
|
||||
if (!ModKeyListener.GetKeyDownOrLongPress(AquaMai.AppConfig.ModKeyMap.QuickSkip, AquaMai.AppConfig.ModKeyMap.QuickSkipLongPress)) return;
|
||||
MelonLogger.Msg("[QuickSkip] Activated");
|
||||
|
||||
var traverse = Traverse.Create(SharedInstances.ProcessDataContainer.processManager);
|
||||
var processList = traverse.Field("_processList").GetValue<LinkedList<ProcessManager.ProcessControle>>();
|
||||
|
||||
ProcessBase processToRelease = null;
|
||||
|
||||
foreach (ProcessManager.ProcessControle process in processList)
|
||||
{
|
||||
switch (process.Process.ToString())
|
||||
{
|
||||
// After login
|
||||
case "Process.ModeSelect.ModeSelectProcess":
|
||||
case "Process.LoginBonus.LoginBonusProcess":
|
||||
case "Process.RegionalSelectProcess":
|
||||
case "Process.CharacterSelectProcess":
|
||||
case "Process.TicketSelect.TicketSelectProcess":
|
||||
processToRelease = process.Process;
|
||||
break;
|
||||
|
||||
case "Process.MusicSelectProcess":
|
||||
// Skip to save
|
||||
SoundManager.PreviewEnd();
|
||||
SoundManager.PlayBGM(Cue.BGM_COLLECTION, 2);
|
||||
SharedInstances.ProcessDataContainer.processManager.AddProcess(new FadeProcess(SharedInstances.ProcessDataContainer, process.Process, new UnlockMusicProcess(SharedInstances.ProcessDataContainer)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (processToRelease != null)
|
||||
{
|
||||
GameManager.SetMaxTrack();
|
||||
SharedInstances.ProcessDataContainer.processManager.AddProcess(new FadeProcess(SharedInstances.ProcessDataContainer, processToRelease, new MusicSelectProcess(SharedInstances.ProcessDataContainer)));
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(GameProcess), "OnUpdate")]
|
||||
public static void PostGameProcessUpdate(GameProcess __instance, Message[] ____message, ProcessDataContainer ___container)
|
||||
{
|
||||
if (ModKeyListener.GetKeyDownOrLongPress(AquaMai.AppConfig.ModKeyMap.InGameSkip, AquaMai.AppConfig.ModKeyMap.InGameSkipLongPress))
|
||||
{
|
||||
var traverse = Traverse.Create(__instance);
|
||||
___container.processManager.SendMessage(____message[0]);
|
||||
Singleton<GamePlayManager>.Instance.SetSyncResult(0);
|
||||
traverse.Method("SetRelease").GetValue();
|
||||
}
|
||||
|
||||
if (ModKeyListener.GetKeyDownOrLongPress(AquaMai.AppConfig.ModKeyMap.InGameRetry, AquaMai.AppConfig.ModKeyMap.InGameRetryLongPress) && GameInfo.GameVersion >= 23000)
|
||||
{
|
||||
// This is original typo in Assembly-CSharp
|
||||
Singleton<GamePlayManager>.Instance.SetQuickRetryFrag(flag: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
28
AquaMai/ModKeyMap/TestProof.cs
Normal file
28
AquaMai/ModKeyMap/TestProof.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using HarmonyLib;
|
||||
using Manager;
|
||||
|
||||
namespace AquaMai.ModKeyMap;
|
||||
|
||||
public class TestProof
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(InputManager), "GetSystemInputDown")]
|
||||
public static bool GetSystemInputDown(ref bool __result, InputManager.SystemButtonSetting button, bool[] ___SystemButtonDown)
|
||||
{
|
||||
__result = ___SystemButtonDown[(int)button];
|
||||
if (button != InputManager.SystemButtonSetting.ButtonTest)
|
||||
return false;
|
||||
|
||||
var stackTrace = new StackTrace(); // get call stack
|
||||
var stackFrames = stackTrace.GetFrames(); // get method calls (frames)
|
||||
|
||||
if (stackFrames.Any(it => it.GetMethod().Name == "DMD<Main.GameMainObject::Update>"))
|
||||
{
|
||||
__result = ModKeyListener.GetKeyDownOrLongPress(AquaMai.AppConfig.ModKeyMap.TestMode, AquaMai.AppConfig.ModKeyMap.TestModeLongPress);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user