using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using AquaMai.Core.Resources;
using AquaMai.Core.Types;
using DB;
using HarmonyLib;
using Manager.UserDatas;
using Monitor;
using Monitor.Game;
using Process.SubSequence;
using UnityEngine;
using Util;
namespace AquaMai.Core.Helpers;
public static class GameSettingsManager
{
///
/// <#ffffff>
///
public static readonly string DefaultTag = OptionRootID.DefaultColorTag.GetName();
///
/// <#02f6ff>
///
public static readonly string NormalTag = OptionRootID.NormalColorTag.GetName();
private static bool isPatched = false;
private static readonly List settings = [];
public static void RegisterSetting(IPlayerSettingsItem setting)
{
settings.Add(setting);
settings.Sort((a, b) => a.Sort.CompareTo(b.Sort));
if (!isPatched)
{
isPatched = true;
var applyPatchMethod = typeof(Core.Startup).GetMethod("ApplyPatch", BindingFlags.Static | BindingFlags.NonPublic);
applyPatchMethod.Invoke(null, [typeof(GameSettingsManager)]);
applyPatchMethod.Invoke(null, [typeof(PatchOptionCategoryIDExtension)]);
}
}
public static void UnregisterSetting(IPlayerSettingsItem setting)
{
settings.Remove(setting);
}
public static TabDataBase tab;
[HarmonyPostfix]
[HarmonyPatch(typeof(MusicSelectMonitor), nameof(MusicSelectMonitor.Initialize))]
private static void Init(Sprite ____otherSprite)
{
if (tab != null) return;
tab = new TabDataBase(new Color(172 / 255f, 181 / 255f, 250 / 255f), ____otherSprite, Locale.GameSettingsMod);
}
///
/// 由于在进入设置界面时 UserOption 会被复制一份,所以我们在这边存储对应关系,方便后面查询
///
private static Dictionary userOptionToPlayer = new();
[HarmonyPostfix]
[HarmonyPatch(typeof(OptionSelectSequence), nameof(OptionSelectSequence.OnStartSequence))]
private static void OnStartSequence(UserOption ____userOption, int ___PlayerIndex)
{
userOptionToPlayer[____userOption] = ___PlayerIndex;
}
[HarmonyPostfix]
[HarmonyPatch(typeof(OptionSelectSequence), nameof(OptionSelectSequence.OnGameStart))]
private static void OnGameStart(UserOption ____userOption)
{
userOptionToPlayer.Remove(____userOption);
}
private const int ModCategoryId = 5;
private const int CategoryIdMax = ModCategoryId + 1;
[HarmonyPostfix]
[HarmonyPatch(typeof(MusicSelectMonitor), nameof(MusicSelectMonitor.SetOptionCard))]
private static void SetOptionCard(GenreSelectController ____genreTabController, Dictionary ____optionSprite)
{
List list = new List();
for (OptionCategoryID optionCategoryID = OptionCategoryID.SpeedSetting; optionCategoryID < OptionCategoryID.End; optionCategoryID++)
{
Sprite sprite = ____optionSprite[(int)optionCategoryID];
string title = optionCategoryID.GetName();
Color baseColor = Utility.ConvertColor(optionCategoryID.GetMainColor());
list.Add(new TabDataBase(baseColor, sprite, title));
}
list.Add(tab);
____genreTabController.SortType2Genre(list, 0);
____genreTabController.Change(0);
}
// 改字节码,有意思
[HarmonyTranspiler]
[HarmonyPatch(typeof(OptionSelectSequence), "Update")]
private static IEnumerable OptionSelectSequenceUpdate(IEnumerable instructions)
{
var insts = instructions.ToArray();
var load5s = insts.Where(inst => inst.opcode == OpCodes.Ldc_I4_5).ToArray();
if (load5s.Length < 9)
{
throw new Exception("无法找到 OptionSelectSequence.Update 的 ldc.i4.5 指令");
}
// TODO 应该有更好的寻找方式
load5s[1].opcode = OpCodes.Ldc_I4_6;
load5s[load5s.Length - 1].opcode = OpCodes.Ldc_I4_6; // 最后一个
var load4s = insts.Where(inst => inst.opcode == OpCodes.Ldc_I4_4).ToArray();
if (load4s.Length < 2)
{
throw new Exception("无法找到 OptionSelectSequence.Update 的 ldc.i4.4 指令");
}
load4s[0].opcode = OpCodes.Ldc_I4_5;
load4s[1].opcode = OpCodes.Ldc_I4_5;
return insts;
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(OptionSelectSequence), nameof(OptionSelectSequence.IsOptionBoundary))]
private static IEnumerable IsOptionBoundary(IEnumerable instructions)
{
var insts = instructions.ToArray();
insts.First(inst => inst.opcode == OpCodes.Ldc_I4_5).opcode = OpCodes.Ldc_I4_6;
insts.First(inst => inst.opcode == OpCodes.Ldc_I4_4).opcode = OpCodes.Ldc_I4_5;
return insts;
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(OptionSelectSequence), nameof(OptionSelectSequence.GetCategory))]
private static IEnumerable GetCategory(IEnumerable instructions)
{
var insts = instructions.ToArray();
insts.First(inst => inst.opcode == OpCodes.Ldc_I4_5).opcode = OpCodes.Ldc_I4_6;
insts.First(inst => inst.opcode == OpCodes.Ldc_I4_4).opcode = OpCodes.Ldc_I4_5;
return insts;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(OptionSelectSequence), nameof(OptionSelectSequence.GetCategoryName))]
private static bool GetCategoryName(int diffIndex, ref string __result, OptionSelectSequence __instance)
{
int optionCategoryID = (int)__instance.CurrentOptionCategory + diffIndex;
if (optionCategoryID >= CategoryIdMax)
{
optionCategoryID -= CategoryIdMax;
}
if (optionCategoryID < 0)
{
optionCategoryID = CategoryIdMax + optionCategoryID;
}
if (optionCategoryID == ModCategoryId)
{
__result = Locale.GameSettingsMod;
}
__result = ((OptionCategoryID)optionCategoryID).GetName();
return false;
}
[HarmonyPostfix]
[HarmonyPatch(typeof(OptionSelectSequence), nameof(OptionSelectSequence.GetCategory))]
private static void GetCategory(ref bool isLeftButtonActive, ref bool isRightButtonActive, string __result)
{
var option = settings.FirstOrDefault(it => it.Name == __result);
if (option == null) return;
isLeftButtonActive = option.IsLeftButtonActive;
isRightButtonActive = option.IsRightButtonActive;
}
///
/// mai 的代码会传入无效的 index(GetCategory 处)
/// 所以接下来的访问都必须判断 index
///
///
///
private static bool IsValidIndex(int index)
{
return index >= 0 && index < settings.Count;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(UserOption), "GetOptionName")]
private static bool GetOptionName(OptionCategoryID category, int optionIndex, ref string __result)
{
if ((int)category != ModCategoryId) return true;
if (!IsValidIndex(optionIndex)) return false;
__result = settings[optionIndex].Name;
return false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(UserOption), "GetOptionDetail")]
private static bool GetOptionDetail(OptionCategoryID category, int optionIndex, ref string __result)
{
if ((int)category != ModCategoryId) return true;
if (!IsValidIndex(optionIndex)) return false;
__result = settings[optionIndex].Detail;
return false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(UserOption), "GetFilePath")]
private static bool GetFilePath(OptionCategoryID category, int currentOptionIndex, ref string __result)
{
if ((int)category != ModCategoryId) return true;
if (!IsValidIndex(currentOptionIndex)) return false;
__result = settings[currentOptionIndex].SpriteFile;
return false;
}
// 由于加减按钮由我们自己控制,所以这个没有意义,只要让游戏觉得在范围内就行
// 这两个关系到点击屏幕的事件是否被响应
[HarmonyPrefix]
[HarmonyPatch(typeof(UserOption), "GetOptionValueIndex")]
private static bool GetOptionValueIndex(OptionCategoryID category, int currentOptionIndex, ref int __result)
{
if ((int)category != ModCategoryId) return true;
__result = 114514;
return false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(UserOption), "GetOptionMax")]
private static bool GetOptionMax(OptionCategoryID category, int currentOptionIndex, ref int __result)
{
if ((int)category != ModCategoryId) return true;
__result = 1919810;
return false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(UserOption), "GetOptionValue")]
private static bool GetOptionValue(OptionCategoryID category, int optionIndex, ref string __result, string ___DefaultTag, string ___NormalTag, UserOption __instance)
{
if ((int)category != ModCategoryId) return true;
if (!IsValidIndex(optionIndex)) return false;
if (!userOptionToPlayer.TryGetValue(__instance, out var player))
{
__result = "错误的玩家 ID";
return false;
}
__result = settings[optionIndex].GetOptionValue(player);
return false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(UserOption), "AddOption")]
private static bool AddOption(OptionCategoryID category, int currentOptionIndex, UserOption __instance)
{
if ((int)category != ModCategoryId) return true;
if (!IsValidIndex(currentOptionIndex)) return false;
if (!userOptionToPlayer.TryGetValue(__instance, out var player))
{
return false;
}
settings[currentOptionIndex].AddOption(player);
return false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(UserOption), "SubOption")]
private static bool SubOption(OptionCategoryID category, int currentOptionIndex, UserOption __instance)
{
if ((int)category != ModCategoryId) return true;
if (!IsValidIndex(currentOptionIndex)) return false;
if (!userOptionToPlayer.TryGetValue(__instance, out var player))
{
return false;
}
settings[currentOptionIndex].SubOption(player);
return false;
}
[HarmonyPatch]
private class PatchOptionCategoryIDExtension
{
public static IEnumerable TargetMethods()
{
// Origin typo
var clas = Assembly.GetAssembly(typeof(GameCtrl)).GetType("Manager.UserDatas.OptionCategoryIDExcenstion");
yield return clas.GetMethod("GetCategoryMax");
}
public static bool Prefix(ref int __result, OptionCategoryID category)
{
if ((int)category != ModCategoryId) return true;
__result = settings.Count;
return false;
}
}
}