forked from Cookies_Github_mirror/AquaDX
[RF] AquaMai configuration refactor (#82)
更新了配置文件格式,原有的配置文件将被自动无缝迁移,详情请见新的配置文件中的注释(例外:`SlideJudgeTweak` 不再默认启用) 旧配置文件将被重命名备份,如果更新到此版本遇到 Bug 请联系我们 Updated configuration file schema. The old config file will be migrated automatically and seamlessly. See the comments in the new configuration file for details. (Except for `SlideJudgeTweak` is no longer enabled by default) Your old configuration file will be renamed as a backup. If you encounter any bug with this version, please contact us.
This commit is contained in:
139
AquaMai/AquaMai.Mods/Fix/Common.cs
Normal file
139
AquaMai/AquaMai.Mods/Fix/Common.cs
Normal file
@@ -0,0 +1,139 @@
|
||||
using System.Net;
|
||||
using HarmonyLib;
|
||||
using Manager;
|
||||
using Net;
|
||||
using UnityEngine;
|
||||
using AquaMai.Config.Attributes;
|
||||
using AquaMai.Core.Attributes;
|
||||
|
||||
namespace AquaMai.Mods.Fix;
|
||||
|
||||
[ConfigSection(exampleHidden: true, defaultOn: true)]
|
||||
public class Common
|
||||
{
|
||||
[ConfigEntry]
|
||||
private readonly static bool preventIniFileClear = true;
|
||||
|
||||
[EnableIf(nameof(preventIniFileClear))]
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(MAI2System.IniFile), "clear")]
|
||||
private static bool PreIniFileClear()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
[ConfigEntry]
|
||||
private readonly static bool fixDebugInput = true;
|
||||
|
||||
[EnableIf(nameof(fixDebugInput))]
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(DebugInput), "GetKey")]
|
||||
private static bool GetKey(ref bool __result, KeyCode name)
|
||||
{
|
||||
__result = UnityEngine.Input.GetKey(name);
|
||||
return false;
|
||||
}
|
||||
|
||||
[EnableIf(nameof(fixDebugInput))]
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(DebugInput), "GetKeyDown")]
|
||||
private static bool GetKeyDown(ref bool __result, KeyCode name)
|
||||
{
|
||||
__result = UnityEngine.Input.GetKeyDown(name);
|
||||
return false;
|
||||
}
|
||||
|
||||
[EnableIf(nameof(fixDebugInput))]
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(DebugInput), "GetMouseButton")]
|
||||
private static bool GetMouseButton(ref bool __result, int button)
|
||||
{
|
||||
__result = UnityEngine.Input.GetMouseButton(button);
|
||||
return false;
|
||||
}
|
||||
|
||||
[EnableIf(nameof(fixDebugInput))]
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(DebugInput), "GetMouseButtonDown")]
|
||||
private static bool GetMouseButtonDown(ref bool __result, int button)
|
||||
{
|
||||
__result = UnityEngine.Input.GetMouseButtonDown(button);
|
||||
return false;
|
||||
}
|
||||
|
||||
[ConfigEntry]
|
||||
private readonly static bool bypassCakeHashCheck = true;
|
||||
|
||||
[EnableIf(nameof(bypassCakeHashCheck))]
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(NetHttpClient), MethodType.Constructor)]
|
||||
private static void OnNetHttpClientConstructor(NetHttpClient __instance)
|
||||
{
|
||||
// Bypass Cake.dll hash check
|
||||
var tInstance = Traverse.Create(__instance).Field("isTrueDll");
|
||||
if (tInstance.FieldExists())
|
||||
{
|
||||
tInstance.SetValue(true);
|
||||
}
|
||||
}
|
||||
|
||||
[ConfigEntry]
|
||||
private readonly static bool restoreCertificateValidation = true;
|
||||
|
||||
[EnableIf(nameof(restoreCertificateValidation))]
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(NetHttpClient), "Create")]
|
||||
private static void OnNetHttpClientCreate()
|
||||
{
|
||||
// Unset the certificate validation callback (SSL pinning) to restore the default behavior
|
||||
ServicePointManager.ServerCertificateValidationCallback = null;
|
||||
}
|
||||
|
||||
[ConfigEntry]
|
||||
private readonly static bool forceNonTarget = true;
|
||||
|
||||
[EnableIf(nameof(forceNonTarget))]
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(MAI2System.Config), "IsTarget", MethodType.Getter)]
|
||||
private static bool PreIsTarget(ref bool __result)
|
||||
{
|
||||
// Who is teaching others to set `Target = 1`?!
|
||||
__result = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
[ConfigEntry]
|
||||
private readonly static bool forceIgnoreError = true;
|
||||
|
||||
[EnableIf(nameof(forceIgnoreError))]
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(MAI2System.Config), "IsIgnoreError", MethodType.Getter)]
|
||||
private static bool PreIsIgnoreError(ref bool __result)
|
||||
{
|
||||
__result = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
[ConfigEntry]
|
||||
private readonly static bool bypassSpecialNumCheck = true;
|
||||
|
||||
public static void OnAfterPatch(HarmonyLib.Harmony h)
|
||||
{
|
||||
if (bypassSpecialNumCheck)
|
||||
{
|
||||
if (typeof(GameManager).GetMethod("CalcSpecialNum") is null) return;
|
||||
h.PatchAll(typeof(CalcSpecialNumPatch));
|
||||
}
|
||||
}
|
||||
|
||||
private class CalcSpecialNumPatch
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(GameManager), "CalcSpecialNum")]
|
||||
private static bool CalcSpecialNum(ref int __result)
|
||||
{
|
||||
__result = 1024;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
212
AquaMai/AquaMai.Mods/Fix/DebugFeature.cs
Normal file
212
AquaMai/AquaMai.Mods/Fix/DebugFeature.cs
Normal file
@@ -0,0 +1,212 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using AquaMai.Config.Attributes;
|
||||
using HarmonyLib;
|
||||
using MAI2.Util;
|
||||
using Manager;
|
||||
using MelonLoader;
|
||||
using Monitor;
|
||||
using Process;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AquaMai.Mods.Fix;
|
||||
|
||||
[ConfigSection(exampleHidden: true, defaultOn: true)]
|
||||
public class DebugFeature
|
||||
{
|
||||
public static bool IsPolyfill { get; private set; }
|
||||
private static GameProcess _gameProcess;
|
||||
private static MovieController _gameMovie;
|
||||
private static GameMonitor[] _monitors;
|
||||
private static object _debugFeatureOriginal;
|
||||
private static System.Type _debugFeatureType;
|
||||
|
||||
[HarmonyPatch(typeof(GameProcess), "OnStart")]
|
||||
[HarmonyPostfix]
|
||||
public static void Init(GameProcess __instance, MovieController ____gameMovie, GameMonitor[] ____monitors)
|
||||
{
|
||||
_gameProcess = __instance;
|
||||
_gameMovie = ____gameMovie;
|
||||
_monitors = ____monitors;
|
||||
PolyFill.timer = 0;
|
||||
}
|
||||
|
||||
public static void OnBeforePatch(HarmonyLib.Harmony h)
|
||||
{
|
||||
var original = typeof(GameProcess).GetField("debugFeature", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
if (original is null)
|
||||
{
|
||||
MelonLogger.Msg(" > [DebugFeature] Running Polyfill");
|
||||
IsPolyfill = true;
|
||||
h.PatchAll(typeof(PolyFill));
|
||||
}
|
||||
else
|
||||
{
|
||||
MelonLogger.Msg(" > [DebugFeature] Already included");
|
||||
_debugFeatureType = typeof(GameProcess).GetNestedType("DebugFeature", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
h.PatchAll(typeof(GetOriginal));
|
||||
}
|
||||
}
|
||||
|
||||
public static bool Pause
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsPolyfill)
|
||||
{
|
||||
return PolyFill.isPause;
|
||||
}
|
||||
|
||||
return (bool)_debugFeatureType.GetField("_debugPause", BindingFlags.Instance | BindingFlags.Public).GetValue(_debugFeatureOriginal);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (IsPolyfill)
|
||||
{
|
||||
PolyFill.isPause = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
_debugFeatureType.GetField("_debugPause", BindingFlags.Instance | BindingFlags.Public).SetValue(_debugFeatureOriginal, value);
|
||||
}
|
||||
|
||||
SoundManager.PauseMusic(value);
|
||||
_gameMovie.Pause(value);
|
||||
NotesManager.Pause(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Seek(int msec)
|
||||
{
|
||||
Singleton<GamePlayManager>.Instance.Initialize();
|
||||
if (IsPolyfill)
|
||||
{
|
||||
PolyFill.DebugTimeSkip(msec);
|
||||
}
|
||||
else
|
||||
{
|
||||
_debugFeatureType.GetMethod("DebugTimeSkip", BindingFlags.Instance | BindingFlags.Public).Invoke(_debugFeatureOriginal, new object[] { msec });
|
||||
}
|
||||
}
|
||||
|
||||
public static double CurrentPlayMsec
|
||||
{
|
||||
[Obsolete("不要用它,它有问题。用 PracticeMode.CurrentPlayMsec")]
|
||||
get
|
||||
{
|
||||
if (IsPolyfill)
|
||||
{
|
||||
return PolyFill.timer;
|
||||
}
|
||||
|
||||
return (double)_debugFeatureType.GetField("_debugTimer", BindingFlags.Instance | BindingFlags.Public).GetValue(_debugFeatureOriginal);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (IsPolyfill)
|
||||
{
|
||||
PolyFill.timer = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
_debugFeatureType.GetField("_debugTimer", BindingFlags.Instance | BindingFlags.Public).SetValue(_debugFeatureOriginal, value);
|
||||
}
|
||||
|
||||
Seek(0);
|
||||
}
|
||||
}
|
||||
|
||||
private static class GetOriginal
|
||||
{
|
||||
[HarmonyPatch(typeof(GameProcess), "OnStart")]
|
||||
[HarmonyPostfix]
|
||||
public static void Postfix(object ___debugFeature)
|
||||
{
|
||||
_debugFeatureOriginal = ___debugFeature;
|
||||
}
|
||||
}
|
||||
|
||||
private static class PolyFill
|
||||
{
|
||||
public static bool isPause;
|
||||
public static double timer;
|
||||
|
||||
public static void DebugTimeSkip(int addMsec)
|
||||
{
|
||||
_gameMovie.Pause(pauseFlag: true);
|
||||
NotesManager.Pause(true);
|
||||
if (addMsec >= 0)
|
||||
{
|
||||
timer += addMsec;
|
||||
}
|
||||
else
|
||||
{
|
||||
timer = timer + addMsec >= 0.0 ? timer + addMsec : 0.0;
|
||||
}
|
||||
|
||||
_gameMovie.SetSeekFrame(timer);
|
||||
SoundManager.SeekMusic((int)timer);
|
||||
for (int i = 0; i < _monitors.Length; i++)
|
||||
{
|
||||
_monitors[i].Seek((int)timer);
|
||||
}
|
||||
|
||||
// magic number, dont know why
|
||||
NotesManager.StartPlay((int)timer + 91);
|
||||
NotesManager.Pause(isPause);
|
||||
if (!isPause)
|
||||
{
|
||||
SoundManager.PauseMusic(pause: false);
|
||||
_gameMovie.Pause(pauseFlag: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
_gameMovie.Pause(pauseFlag: true);
|
||||
}
|
||||
|
||||
_gameProcess.UpdateNotes();
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(GameProcess), "OnUpdate")]
|
||||
[HarmonyPostfix]
|
||||
public static void Postfix(byte ____sequence)
|
||||
{
|
||||
if (____sequence != 4) return;
|
||||
// GameSequence.Play
|
||||
if (!isPause)
|
||||
{
|
||||
timer += GameManager.GetGameMSecAddD();
|
||||
}
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.Home))
|
||||
{
|
||||
GameManager.AutoPlay = (GameManager.AutoPlayMode)((int)(GameManager.AutoPlay + 1) % Enum.GetNames(typeof(GameManager.AutoPlayMode)).Length);
|
||||
}
|
||||
else if (Input.GetKeyDown(KeyCode.Return))
|
||||
{
|
||||
isPause = !isPause;
|
||||
SoundManager.PauseMusic(isPause);
|
||||
_gameMovie.Pause(isPause);
|
||||
NotesManager.Pause(isPause);
|
||||
}
|
||||
else if (DebugInput.GetKeyDown(KeyCode.LeftArrow) || DebugInput.GetKeyDown(KeyCode.RightArrow))
|
||||
{
|
||||
var num23 = 0;
|
||||
if (DebugInput.GetKeyDown(KeyCode.LeftArrow))
|
||||
{
|
||||
num23 = -1000;
|
||||
}
|
||||
|
||||
if (DebugInput.GetKeyDown(KeyCode.RightArrow))
|
||||
{
|
||||
num23 = 1000;
|
||||
}
|
||||
|
||||
int addMsec = ((!DebugInput.GetKey(KeyCode.LeftShift) && !DebugInput.GetKey(KeyCode.RightShift)) ? ((!DebugInput.GetKey(KeyCode.LeftControl) && !DebugInput.GetKey(KeyCode.RightControl)) ? num23 : (num23 * 10)) : (num23 * 5));
|
||||
Singleton<GamePlayManager>.Instance.Initialize();
|
||||
DebugTimeSkip(addMsec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
87
AquaMai/AquaMai.Mods/Fix/DisableReboot.cs
Normal file
87
AquaMai/AquaMai.Mods/Fix/DisableReboot.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using AquaMai.Config.Attributes;
|
||||
using HarmonyLib;
|
||||
using Manager.Operation;
|
||||
|
||||
namespace AquaMai.Mods.Fix;
|
||||
|
||||
[ConfigSection(exampleHidden: true, defaultOn: true)]
|
||||
public class DisableReboot
|
||||
{
|
||||
// IsAutoRebootNeeded
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(MaintenanceTimer), "IsAutoRebootNeeded")]
|
||||
public static bool IsAutoRebootNeeded(ref bool __result)
|
||||
{
|
||||
__result = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// IsUnderServerMaintenance
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(MaintenanceTimer), "IsUnderServerMaintenance")]
|
||||
public static bool IsUnderServerMaintenance(ref bool __result)
|
||||
{
|
||||
__result = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// RemainingMinutes
|
||||
// Original: private int RemainingMinutes => (this._secServerMaintenance + 59) / 60;
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(MaintenanceTimer), "RemainingMinutes", MethodType.Getter)]
|
||||
public static bool RemainingMinutes(ref int __result)
|
||||
{
|
||||
__result = 600;
|
||||
return false;
|
||||
}
|
||||
|
||||
// GetAutoRebootSec
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(MaintenanceTimer), "GetAutoRebootSec")]
|
||||
public static bool GetAutoRebootSec(ref int __result)
|
||||
{
|
||||
__result = 60 * 60 * 10;
|
||||
return false;
|
||||
}
|
||||
|
||||
// GetServerMaintenanceSec
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(MaintenanceTimer), "GetServerMaintenanceSec")]
|
||||
public static bool GetServerMaintenanceSec(ref int __result)
|
||||
{
|
||||
__result = 60 * 60 * 10;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Execute
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(MaintenanceTimer), "Execute")]
|
||||
public static bool Execute(MaintenanceTimer __instance)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// UpdateTimes
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(MaintenanceTimer), "UpdateTimes")]
|
||||
public static bool UpdateTimes(MaintenanceTimer __instance)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(ClosingTimer), "IsShowRemainingMinutes")]
|
||||
public static bool IsShowRemainingMinutes(ref bool __result)
|
||||
{
|
||||
__result = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(ClosingTimer), "IsClosed")]
|
||||
public static bool IsClosed(ref bool __result)
|
||||
{
|
||||
__result = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
21
AquaMai/AquaMai.Mods/Fix/FixCheckAuth.cs
Normal file
21
AquaMai/AquaMai.Mods/Fix/FixCheckAuth.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using AMDaemon.Allnet;
|
||||
using AquaMai.Config.Attributes;
|
||||
using HarmonyLib;
|
||||
using Manager;
|
||||
using Manager.Operation;
|
||||
|
||||
namespace AquaMai.Mods.Fix;
|
||||
|
||||
[ConfigSection(exampleHidden: true, defaultOn: true)]
|
||||
public class FixCheckAuth
|
||||
{
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(OperationManager), "CheckAuth_Proc")]
|
||||
private static void PostCheckAuthProc(ref OperationData ____operationData)
|
||||
{
|
||||
if (Auth.GameServerUri.StartsWith("http://") || Auth.GameServerUri.StartsWith("https://"))
|
||||
{
|
||||
____operationData.ServerUri = Auth.GameServerUri;
|
||||
}
|
||||
}
|
||||
}
|
||||
66
AquaMai/AquaMai.Mods/Fix/FixConnSlide.cs
Normal file
66
AquaMai/AquaMai.Mods/Fix/FixConnSlide.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using AquaMai.Core.Attributes;
|
||||
using AquaMai.Config.Attributes;
|
||||
using HarmonyLib;
|
||||
using Manager;
|
||||
|
||||
namespace AquaMai.Mods.Fix.Legacy;
|
||||
|
||||
[ConfigSection(exampleHidden: true, defaultOn: true)]
|
||||
[EnableGameVersion(23000)]
|
||||
public class FixConnSlide
|
||||
{
|
||||
/* 这个 Patch 用于修复以下 bug:
|
||||
* 非 ConnSlide 被错误解析为 ConnSlide (Fes 首日刹那旅程爆机 bug)
|
||||
* 原 method 逻辑如下:
|
||||
*
|
||||
* if (this.IsSlideAll(noteData1.type) && (index1 + 1 < this._note._noteData.Count ? 1 : 0) != 0)
|
||||
* {
|
||||
* int targetNote = noteData1.slideData.targetNote;
|
||||
* if (noteData1.slideData != null)
|
||||
* targetNote = noteData1.slideData.targetNote;
|
||||
* for (int index3 = index1; index3 < this._note._noteData.Count; ++index3)
|
||||
* {
|
||||
* NoteData noteData3 = this._note._noteData[index3];
|
||||
* if (this.IsSlideAll(noteData3.type) && noteData3.time == noteData1.end && noteData3.startButtonPos == targetNote && noteData3.parent == null)
|
||||
* {
|
||||
* noteData3.parent = noteData1.parent;
|
||||
* noteData1.child.Add(noteData3);
|
||||
* noteData3.isUsed = true;
|
||||
* noteData3.isJudged = true;
|
||||
* break;
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* 修复 bug 需要把第二次调用 this.IsSlideAll() 更改为 this.IsConnectNote(), 这里使用 Transpiler 解决
|
||||
*/
|
||||
[HarmonyTranspiler]
|
||||
[HarmonyPatch(typeof(NotesReader), "calcSlide")]
|
||||
private static IEnumerable<CodeInstruction> Fix(IEnumerable<CodeInstruction> instructions)
|
||||
{
|
||||
List<CodeInstruction> instList = new List<CodeInstruction>(instructions);
|
||||
bool found = false;
|
||||
MethodInfo methodIsSlideAll = AccessTools.Method(typeof(NotesReader), "IsSlideAll");
|
||||
MethodInfo methodIsConnectNote = AccessTools.Method(typeof(NotesReader), "IsConnectNote");
|
||||
|
||||
for (int i = 0; i < instList.Count; i++)
|
||||
{
|
||||
CodeInstruction inst = instList[i];
|
||||
if (!found && inst.Calls(methodIsSlideAll))
|
||||
{
|
||||
found = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (found && inst.Calls(methodIsSlideAll))
|
||||
{
|
||||
inst.operand = methodIsConnectNote;
|
||||
// MelonLogger.Msg($"[FixConnSlide] Successfully patched NotesReader::calcSlide");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return instList;
|
||||
}
|
||||
}
|
||||
80
AquaMai/AquaMai.Mods/Fix/FixLevelDisplay.cs
Normal file
80
AquaMai/AquaMai.Mods/Fix/FixLevelDisplay.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using AquaMai.Core.Attributes;
|
||||
using AquaMai.Config.Attributes;
|
||||
using HarmonyLib;
|
||||
using MAI2.Util;
|
||||
using Manager;
|
||||
using Monitor;
|
||||
using Monitor.MusicSelect.ChainList;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AquaMai.Mods.Fix;
|
||||
|
||||
[EnableGameVersion(24000)]
|
||||
[ConfigSection(exampleHidden: true, defaultOn: true)]
|
||||
public class FixLevelDisplay
|
||||
{
|
||||
// Fix wrong position of level number's display for music levels with non-consistant display level and rate level (difficuly constant)
|
||||
// Stock game charts have no such inconsistency, but custom charts may have (e.g. 10+ but unrated)
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(MusicChainCardObejct), "SetLevel")]
|
||||
private static void FixLevelShiftMusicChainCardObejct(MusicLevelID levelID, SpriteCounter ____digitLevel, SpriteCounter ____doubleDigitLevel, bool utage, GameObject ____difficultyUtageQuesionMarkSingleDigit, GameObject ____difficultyUtageQuesionMarkDoubleDigit)
|
||||
{
|
||||
switch (levelID)
|
||||
{
|
||||
case > MusicLevelID.Level9P:
|
||||
____digitLevel.gameObject.SetActive(value: false);
|
||||
____doubleDigitLevel.gameObject.SetActive(value: true);
|
||||
____doubleDigitLevel.ChangeText(levelID.GetLevelNum().PadRight(3));
|
||||
break;
|
||||
case >= MusicLevelID.None:
|
||||
____digitLevel.gameObject.SetActive(value: true);
|
||||
____doubleDigitLevel.gameObject.SetActive(value: false);
|
||||
____digitLevel.ChangeText(levelID.GetLevelNum().PadRight(2));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!utage) return;
|
||||
switch (levelID)
|
||||
{
|
||||
case > MusicLevelID.Level9P:
|
||||
____difficultyUtageQuesionMarkSingleDigit.SetActive(value: false);
|
||||
____difficultyUtageQuesionMarkDoubleDigit.SetActive(value: true);
|
||||
break;
|
||||
case >= MusicLevelID.None:
|
||||
____difficultyUtageQuesionMarkSingleDigit.SetActive(value: true);
|
||||
____difficultyUtageQuesionMarkDoubleDigit.SetActive(value: false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(SingleResultCardController), "SetLevel")]
|
||||
private static void FixLevelShiftSingleResultCardController(MusicLevelID levelID, bool isUtage, ref SpriteCounter ____difficultySingle, ref SpriteCounter ____difficultyDouble, GameObject ____utageQuestionMarkSingleDigit, GameObject ____utageQuestionMarkDoubleDigit)
|
||||
{
|
||||
FixLevelShiftMusicChainCardObejct(levelID, ____difficultySingle, ____difficultyDouble, isUtage, ____utageQuestionMarkSingleDigit, ____utageQuestionMarkDoubleDigit);
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(TotalResultPlayer), "SetLevel")]
|
||||
private static void FixLevelShiftTotalResultPlayer(MusicLevelID levelID, bool isUtage, ref SpriteCounter ____difficultySingle, ref SpriteCounter ____difficultyDouble, GameObject ____utageQuestionMarkSingleDigit, GameObject ____utageQuestionMarkDoubleDigit)
|
||||
{
|
||||
FixLevelShiftMusicChainCardObejct(levelID, ____difficultySingle, ____difficultyDouble, isUtage, ____utageQuestionMarkSingleDigit, ____utageQuestionMarkDoubleDigit);
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(ResultMonitor), "SetLevel")]
|
||||
private static void FixLevelShiftTotalResultPlayer(MusicLevelID levelID, ref SpriteCounter ____difficultySingle, ref SpriteCounter ____difficultyDouble)
|
||||
{
|
||||
FixLevelShiftMusicChainCardObejct(levelID, ____difficultySingle, ____difficultyDouble, false, null, null);
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(TrackStartMonitor), "SetTrackStart")]
|
||||
private static void FixLevelShiftTrackStartMonitor(int ___monitorIndex, ref SpriteCounter ____difficultySingle, ref SpriteCounter ____difficultyDouble, GameObject ____utageQuestionSingleDigit, GameObject ____utageQuestionDoubleDigit)
|
||||
{
|
||||
var music = Singleton<DataManager>.Instance.GetMusic(GameManager.SelectMusicID[___monitorIndex]);
|
||||
var levelID = (MusicLevelID)music.notesData[GameManager.SelectDifficultyID[___monitorIndex]].musicLevelID;
|
||||
FixLevelShiftMusicChainCardObejct(levelID, ____difficultySingle, ____difficultyDouble, music.name.id >= 100000, ____utageQuestionSingleDigit, ____utageQuestionDoubleDigit);
|
||||
}
|
||||
}
|
||||
104
AquaMai/AquaMai.Mods/Fix/FixSlideAutoPlay.cs
Normal file
104
AquaMai/AquaMai.Mods/Fix/FixSlideAutoPlay.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
using System.Collections.Generic;
|
||||
using AquaMai.Config.Attributes;
|
||||
using HarmonyLib;
|
||||
using Manager;
|
||||
using Monitor;
|
||||
|
||||
namespace AquaMai.Mods.Fix;
|
||||
|
||||
[ConfigSection(exampleHidden: true, defaultOn: true)]
|
||||
public class FixSlideAutoPlay
|
||||
{
|
||||
/* 这个 Patch 用于修复以下 bug:
|
||||
* SlideFan 在 AutoPlay 时, 只有第一个箭头会消失
|
||||
* 原 method 逻辑如下:
|
||||
*
|
||||
* if (this.IsNoteCheckTimeStartIgnoreJudgeWait())
|
||||
* {
|
||||
* // do something ...
|
||||
* if (!GameManager.IsAutoPlay())
|
||||
* {
|
||||
* // do something ...
|
||||
* for (int index = 0; index < this._arrowPrefubs.Length && (double) index < (double) num2 * 11.0; ++index)
|
||||
* {
|
||||
* // do something about displaying arrows ...
|
||||
* }
|
||||
* }
|
||||
* else
|
||||
* {
|
||||
* float num4 = (currentMsec - this.StarLaunchMsec) / (this.StarArriveMsec - this.StarLaunchMsec - this.lastWaitTime);
|
||||
* for (int index = 0; index < this._arrowPrefubs.Length && (double) index < (double) num4 * 1.0; ++index)
|
||||
* {
|
||||
* // do something about displaying arrows ...
|
||||
* }
|
||||
* if ((double) num4 > 1.0)
|
||||
* num1 = 3;
|
||||
* }
|
||||
* // do something ...
|
||||
* }
|
||||
*
|
||||
* 导致这个 bug 的原因是 else 分支的 for 循环终止条件写错了, 应该是 11.0 (因为有 11 个箭头), SBGA 写成了 1.0
|
||||
* 这个 method 中一共只有 5 处 ldc.r4 的 IL Code, 依次为 10.0, 11.0, 1.0, 1.0, 0.0
|
||||
* 修复 bug 需要把第三处的 1.0 更改为 11.0, 这里使用 Transpiler 解决
|
||||
*/
|
||||
[HarmonyTranspiler]
|
||||
[HarmonyPatch(typeof(SlideFan), "NoteCheck")]
|
||||
private static IEnumerable<CodeInstruction> FixFanAutoPlayArrow(IEnumerable<CodeInstruction> instructions)
|
||||
{
|
||||
List<CodeInstruction> instList = new List<CodeInstruction>(instructions);
|
||||
bool found = false;
|
||||
for (int i = 0; i < instList.Count; i++)
|
||||
{
|
||||
CodeInstruction inst = instList[i];
|
||||
if (inst.LoadsConstant(11.0))
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (found && inst.LoadsConstant(1.0))
|
||||
{
|
||||
inst.operand = 11.0f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return instList;
|
||||
}
|
||||
|
||||
/* 这个 Patch 让 Slide 在 AutoPlay 的时候, 每个区仍然会分按下和松开两段进行推进 (加上 this._hitIn 的变化)
|
||||
* 原 method 逻辑如下:
|
||||
*
|
||||
* if (!GameManager.IsAutoPlay())
|
||||
* {
|
||||
* // do somethings ...
|
||||
* }
|
||||
* else
|
||||
* {
|
||||
* float num1 = (currentMsec - this.StarLaunchMsec) / (this.StarArriveMsec - this.StarLaunchMsec - this.lastWaitTime);
|
||||
* this._hitIndex = (int) ((double) this._hitAreaList.Count * (double) num1);
|
||||
* if (this._hitIndex >= this._hitAreaList.Count)
|
||||
* this._hitIndex = this._hitAreaList.Count - 1;
|
||||
* if (this._hitIndex < 0)
|
||||
* this._hitIndex = 0;
|
||||
* int num2 = (int) ((double) this._dispLaneNum * this.GetDeleteArrowDistance());
|
||||
* // do somethings ...
|
||||
* }
|
||||
*
|
||||
* 现在要在 this.GetDeleteArrowDistance() 之前插入
|
||||
* this._hitIn = ((float)this._hitAreaList.Count * num1 > (float)this._hitIndex + 0.5f);
|
||||
* 这段代码, 可以采用 Prefix, GetDeleteArrowDistance() 只在两个地方调用过, 另一处就在上面的 if 分支中 (即非 AutoPlay 情况)
|
||||
*/
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(SlideRoot), "GetDeleteArrowDistance")]
|
||||
private static void FixSlideAutoPlayArrow(
|
||||
SlideRoot __instance, ref bool ____hitIn, int ____hitIndex, List<SlideManager.HitArea> ____hitAreaList,
|
||||
float ___StarLaunchMsec, float ___StarArriveMsec, float ___lastWaitTime
|
||||
)
|
||||
{
|
||||
if (GameManager.IsAutoPlay())
|
||||
{
|
||||
float prop = (NotesManager.GetCurrentMsec() - ___StarLaunchMsec) / (___StarArriveMsec - ___StarLaunchMsec - ___lastWaitTime);
|
||||
____hitIn = ____hitAreaList.Count * prop > ____hitIndex + 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
30
AquaMai/AquaMai.Mods/Fix/Legacy/FixQuickRetry130.cs
Normal file
30
AquaMai/AquaMai.Mods/Fix/Legacy/FixQuickRetry130.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using AquaMai.Core.Attributes;
|
||||
using AquaMai.Config.Attributes;
|
||||
using HarmonyLib;
|
||||
using Manager;
|
||||
|
||||
namespace AquaMai.Mods.Fix.Legacy;
|
||||
|
||||
[ConfigSection(exampleHidden: true, defaultOn: true)]
|
||||
[EnableGameVersion(23000, 23499, noWarn: true)]
|
||||
public class FixQuickRetry130
|
||||
{
|
||||
// Fix for the game not resetting Fast and Late counts when quick retrying
|
||||
// For game version < 1.35.0
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(GamePlayManager), "SetQuickRetryFrag")]
|
||||
public static void PostGamePlayManagerSetQuickRetryFrag(GamePlayManager __instance, bool flag)
|
||||
{
|
||||
// Since 1.35.0, `GameScoreList.Initialize()` resets the Fast and Late counts
|
||||
if (flag && !Traverse.Create(typeof(GameScoreList)).Methods().Contains("Initialize"))
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
var gameScoreList = __instance.GetGameScore(i);
|
||||
var traverse = Traverse.Create(gameScoreList);
|
||||
traverse.Property("Fast").SetValue((uint)0);
|
||||
traverse.Property("Late").SetValue((uint)0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
7
AquaMai/AquaMai.Mods/Fix/README.md
Normal file
7
AquaMai/AquaMai.Mods/Fix/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Fix
|
||||
|
||||
Fix of the game's bugs or removal of the game's annoying unuseful "features".
|
||||
|
||||
Non-removal "Fix" patches should have no (negative, or any visual changing) side-effects on the original game.
|
||||
|
||||
All patches under "Fix" should enabled by default and hide in example. They could be still turned off manually in the config.
|
||||
45
AquaMai/AquaMai.Mods/Fix/Stability/FixMissingCharaCrash.cs
Normal file
45
AquaMai/AquaMai.Mods/Fix/Stability/FixMissingCharaCrash.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using AquaMai.Config.Attributes;
|
||||
using HarmonyLib;
|
||||
using Process;
|
||||
using Util;
|
||||
|
||||
namespace AquaMai.Mods.Fix.Stability;
|
||||
|
||||
/**
|
||||
* Fix character selection crashing due to missing character data
|
||||
*/
|
||||
[ConfigSection(exampleHidden: true, defaultOn: true)]
|
||||
public class FixMissingCharaCrash
|
||||
{
|
||||
// Check if the return is null. If it is, make up a color
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(CharacterSelectProces), "GetMapColorData")]
|
||||
public static void GetMapColorData(ref CharacterSelectProces __instance, ref CharacterMapColorData __result)
|
||||
{
|
||||
if (__result != null) return;
|
||||
|
||||
// 1 is a color that definitely exists
|
||||
if (MapMaster.GetSlotData(1) == null)
|
||||
{
|
||||
MapMaster.GetSlotData(1).Load();
|
||||
}
|
||||
__result = MapMaster.GetSlotData(1);
|
||||
}
|
||||
|
||||
// This is called when loading the music selection screen, to display characters on the top screen
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(Monitor.CommonMonitor), "SetCharacterSlot", new Type[] { typeof(MessageCharactorInfomationData) })]
|
||||
public static bool SetCharacterSlot(ref MessageCharactorInfomationData data, Dictionary<int, CharacterSlotData> ____characterSlotData)
|
||||
{
|
||||
// Some characters are not found in this dictionary. We simply skip loading those characters
|
||||
if (!____characterSlotData.ContainsKey(data.MapKey))
|
||||
{
|
||||
Console.Log($"Could not get CharacterSlotData for character [Index={data.Index}, MapKey={data.MapKey}], ignoring...");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user