From 4d25b6a43ca692b349bd2ca38243d8804bd9b84d Mon Sep 17 00:00:00 2001 From: Minepig <30530115+Minepig@users.noreply.github.com> Date: Tue, 19 Nov 2024 01:32:18 +0800 Subject: [PATCH] Accuracy info and other features (#84) * tweaks slide fade in * judge accuracy info * Update SlideArrowAnimation.cs --- AquaMai/Main.cs | 4 + AquaMai/UX/JudgeAccuracyInfo.cs | 243 ++++++++++++++++++++++++++ AquaMai/Visual/SlideArrowAnimation.cs | 2 +- AquaMai/Visual/SlideFadeInTweak.cs | 125 +++++++++++++ 4 files changed, 373 insertions(+), 1 deletion(-) create mode 100644 AquaMai/UX/JudgeAccuracyInfo.cs create mode 100644 AquaMai/Visual/SlideFadeInTweak.cs diff --git a/AquaMai/Main.cs b/AquaMai/Main.cs index 8efe9559..fe4ed29e 100644 --- a/AquaMai/Main.cs +++ b/AquaMai/Main.cs @@ -188,6 +188,10 @@ public override void OnInitializeMelon() Patch(typeof(TestProof)); Patch(typeof(PractiseMode)); Patch(typeof(HideSelfMadeCharts)); + + Patch(typeof(SlideFadeInTweak)); + Patch(typeof(JudgeAccuracyInfo)); + # if CI Patch(typeof(CiBuildAlert)); # endif diff --git a/AquaMai/UX/JudgeAccuracyInfo.cs b/AquaMai/UX/JudgeAccuracyInfo.cs new file mode 100644 index 00000000..1772d0fd --- /dev/null +++ b/AquaMai/UX/JudgeAccuracyInfo.cs @@ -0,0 +1,243 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Reflection; +using HarmonyLib; +using Manager; +using Monitor; +using Monitor.Result; +using Process; +using TMPro; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace AquaMai.UX; + +public class JudgeAccuracyInfo +{ + public class AccuracyEntryList + { + public List[] DiffList = new List[TableRowNames.Length]; + public List[] RawDiffList = new List[TableRowNames.Length]; + public HashSet NoteIndices = new(); + + public AccuracyEntryList() + { + for (int i = 0; i < TableRowNames.Length; i++) + { + DiffList[i] = new List(); + RawDiffList[i] = new List(); + } + } + } + + public static AccuracyEntryList[] EntryList = new AccuracyEntryList[2]; + + [HarmonyPostfix] + [HarmonyPatch(typeof(GameProcess), "OnStart")] + private static void OnGameProcessStartFinish() + { + for (int i = 0; i < EntryList.Length; i++) + { + EntryList[i] = new AccuracyEntryList(); + } + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(ResultProcess), "OnRelease")] + private static void OnResultProcessReleaseFinish() + { + for (int i = 0; i < EntryList.Length; i++) + { + EntryList[i] = null; + Controllers[i] = null; + } + } + + [HarmonyPatch] + public static class NoteBaseJudgePatch + { + public static IEnumerable TargetMethods() + { + return + [ + AccessTools.Method(typeof(NoteBase), "Judge"), + AccessTools.Method(typeof(HoldNote), "JudgeHoldHead"), + AccessTools.Method(typeof(BreakHoldNote), "JudgeHoldHead"), + AccessTools.Method(typeof(TouchNoteB), "Judge"), + AccessTools.Method(typeof(TouchHoldC), "JudgeHoldHead"), + ]; + } + + public static void Postfix( + NoteBase __instance, bool __result, + float ___JudgeTimingDiffMsec, float ___AppearMsec, NoteJudge.EJudgeType ___JudgeType, int ___NoteIndex + ) + { + var monitor = __instance.MonitorId; + if (!__result || EntryList[monitor].NoteIndices.Contains(___NoteIndex)) return; + + EntryList[monitor].NoteIndices.Add(___NoteIndex); + + var raw = (NotesManager.GetCurrentMsec() - ___AppearMsec) - NoteJudge.JudgeAdjustMs; + switch (___JudgeType) + { + case NoteJudge.EJudgeType.Tap: + case NoteJudge.EJudgeType.Break: + { + EntryList[monitor].DiffList[0].Add(___JudgeTimingDiffMsec); + EntryList[monitor].RawDiffList[0].Add(raw); + break; + } + case NoteJudge.EJudgeType.Touch: + { + EntryList[monitor].DiffList[2].Add(___JudgeTimingDiffMsec); + EntryList[monitor].RawDiffList[2].Add(raw); + break; + } + case NoteJudge.EJudgeType.ExTap: + { + EntryList[monitor].DiffList[3].Add(___JudgeTimingDiffMsec); + EntryList[monitor].RawDiffList[3].Add(raw); + break; + } + } + + // MelonLogger.Msg($"{___JudgeType}: {___JudgeTimingDiffMsec}, {raw}"); + } + } + + + [HarmonyPostfix] + [HarmonyPatch(typeof(SlideRoot), "Judge")] + private static void SlideRootJudgePatch( + SlideRoot __instance, bool __result, + float ___JudgeTimingDiffMsec, float ___TailMsec, float ___lastWaitTimeForJudge, + NoteJudge.EJudgeType ___JudgeType, int ___NoteIndex + ) + { + var monitor = __instance.MonitorId; + if (!__result || EntryList[monitor].NoteIndices.Contains(___NoteIndex)) return; + + EntryList[monitor].NoteIndices.Add(___NoteIndex); + + var raw = (NotesManager.GetCurrentMsec() - ___TailMsec + ___lastWaitTimeForJudge) - NoteJudge.JudgeAdjustMs; + EntryList[monitor].DiffList[1].Add(___JudgeTimingDiffMsec - NoteJudge.JudgeAdjustMs); + EntryList[monitor].RawDiffList[1].Add(raw); + + // MelonLogger.Msg($"{___JudgeType}: {___JudgeTimingDiffMsec}, {raw}"); + } + + + [HarmonyPostfix] + [HarmonyPatch(typeof(ResultProcess), "OnStart")] + private static void OnResultProcessStartFinish( + ResultMonitor[] ____monitors, ResultProcess.ResultScoreViewType[] ____resultScoreViewType, UserData[] ____userData + ) + { + foreach (var monitor in ____monitors) + { + var idx = monitor.MonitorIndex; + if (!____userData[idx].IsEntry) continue; + + var fileName = $"Acc_Track_{GameManager.MusicTrackNumber}_Player_{idx}.txt"; + var filePath = Path.Combine(Environment.CurrentDirectory, fileName); + + using (var writer = new StreamWriter(filePath)) + { + for (int i = 0; i < TableRowNames.Length; i++) + { + writer.WriteLine($"Row: {TableRowNames[i]}"); + writer.WriteLine(" DiffList:"); + writer.WriteLine($" {string.Join(", ", EntryList[idx].DiffList[i])}"); + writer.WriteLine(" RawDiffList:"); + writer.WriteLine($" {string.Join(", ", EntryList[idx].RawDiffList[i])}"); + writer.WriteLine(); + } + } + + var controller = Traverse.Create(monitor).Field("_scoreBoardController").Value; + var newController = Object.Instantiate(controller, controller.transform); + newController.gameObject.GetComponent().enabled = false; + newController.transform.localPosition = Vector3.zero; + var table = ExtractTextObjs(newController); + for (var i = 0; i < TableHead.Length; i++) + { + table[0, i].text = TableHead[i]; + } + + for (var i = 0; i < TableRowNames.Length; i++) + { + table[i + 1, 0].text = TableRowNames[i]; + var num = EntryList[idx].DiffList[i].Count; + table[i + 1, 1].text = num.ToString(); + if (num <= 0) + { + table[i + 1, 2].text = "——"; + table[i + 1, 3].text = "——"; + table[i + 1, 4].text = "——"; + continue; + } + + var average = EntryList[idx].DiffList[i].Average(); + var averageFrame = average * 0.06f; + table[i + 1, 2].text = averageFrame.ToString("+0.00;-0.00;0.00", CultureInfo.InvariantCulture); + var averageRawFrame = EntryList[idx].RawDiffList[i].Average() * 0.06f; + table[i + 1, 3].text = averageRawFrame.ToString("+0.00;-0.00;0.00", CultureInfo.InvariantCulture); + + if (num <= 1) + { + table[i + 1, 4].text = "——"; + } + else + { + var deviSqr = EntryList[idx].DiffList[i].Sum(x => (x - average) * (x - average)) / (num - 1); + var devi = Mathf.Sqrt(deviSqr) * 0.06f; + table[i + 1, 4].text = devi.ToString("0.00", CultureInfo.InvariantCulture); + } + } + + newController.gameObject.SetActive(____resultScoreViewType[idx] == ResultProcess.ResultScoreViewType.VSResult); + Controllers[idx] = newController; + } + } + + private static readonly ScoreBoardController[] Controllers = new ScoreBoardController[2]; + + [HarmonyPostfix] + [HarmonyPatch(typeof(ResultMonitor), "ChangeScoreBoard")] + private static void OnChangeScoreBoard( + ResultMonitor __instance, ResultProcess.ResultScoreViewType resultScoreType + ) + { + Controllers[__instance.MonitorIndex].gameObject.SetActive(resultScoreType == ResultProcess.ResultScoreViewType.VSResult); + } + + + private static readonly string[] RowNames = ["_tap", "_hold", "_slide", "_touch", "_break"]; + private static readonly string[] ColumnNames = ["_critical", "_perfect", "_great", "_good", "_miss"]; + private static readonly string[] TableHead = ["", "NUM", "AVG", "RAW", "S.D."]; + private static readonly string[] TableRowNames = ["TAP", "SLD", "TCH", "EX"]; + + private static TextMeshProUGUI[,] ExtractTextObjs(ScoreBoardController controller) + { + var result = new TextMeshProUGUI[RowNames.Length, ColumnNames.Length]; + for (var i = 0; i < RowNames.Length; i++) + { + for (int j = 0; j < ColumnNames.Length; j++) + { + var trav = Traverse.Create(controller) + .Field(RowNames[i]) + .Field(ColumnNames[j]); + var text = trav.Field("_numberText").Value; + text.color = Color.black; + result[i, j] = text; + trav.GetValue().SetVisibleCloseBox(false); + } + } + return result; + } + +} \ No newline at end of file diff --git a/AquaMai/Visual/SlideArrowAnimation.cs b/AquaMai/Visual/SlideArrowAnimation.cs index c8e4b542..7439ca25 100644 --- a/AquaMai/Visual/SlideArrowAnimation.cs +++ b/AquaMai/Visual/SlideArrowAnimation.cs @@ -82,7 +82,7 @@ private static void OnGameCtrlUpdateNotesLast() for (var num = _animatingSpriteRenderers.Count - 1; num >= 0; num--) { var spriteRenderer = _animatingSpriteRenderers[num]; - if (spriteRenderer == null) + if (spriteRenderer == null || !spriteRenderer.gameObject.activeSelf) { _animatingSpriteRenderers.RemoveAt(num); } diff --git a/AquaMai/Visual/SlideFadeInTweak.cs b/AquaMai/Visual/SlideFadeInTweak.cs new file mode 100644 index 00000000..26f9dec0 --- /dev/null +++ b/AquaMai/Visual/SlideFadeInTweak.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using HarmonyLib; +using MAI2.Util; +using Manager; +using Monitor; +using UnityEngine; + +namespace AquaMai.Visual; + +public class SlideFadeInTweak +{ + [HarmonyPrefix] + [HarmonyPatch(typeof(SlideRoot), "UpdateAlpha")] + private static bool UpdateAlphaOverwrite( + SlideRoot __instance, + ref bool ___UpdateAlphaFlag, + float ___StartMsec, float ___AppearMsec, float ___StarLaunchMsec, float ___DefaultMsec, + int ____dispLaneNum, bool ___BreakFlag, + List ____spriteRenders, List ____breakSpriteRenders + ) + { + if (!___UpdateAlphaFlag) + return false; + + var currentMsec = NotesManager.GetCurrentMsec(); + var slideSpeed = (int) Singleton.Instance.GetGameScore(__instance.MonitorId).UserOption.SlideSpeed; + var defaultFadeInLength = (21 - slideSpeed) / 10.5f * ___DefaultMsec; + var fadeInFirstMsec = Math.Max(___StartMsec, ___AppearMsec - defaultFadeInLength); + var fadeInSecondMsec = Math.Max(___AppearMsec, ___StarLaunchMsec - defaultFadeInLength); + // var fadeInSecondMsec = ___AppearMsec; + var color = new Color(1f, 1f, 1f, 1f); + + if (currentMsec >= ___StarLaunchMsec) + { + ___UpdateAlphaFlag = false; + } + else if (currentMsec < fadeInFirstMsec) + { + color.a = 0.0f; + } + else if (fadeInFirstMsec <= currentMsec && currentMsec < ___AppearMsec) + { + var fadeInLength = Math.Min(200.0f, ___AppearMsec - fadeInFirstMsec); + color.a = 0.5f * Math.Min(1f, (currentMsec - fadeInFirstMsec) / fadeInLength); + } + else if (___AppearMsec <= currentMsec && currentMsec < fadeInSecondMsec) + { + color.a = 0.5f; + } + else if (fadeInSecondMsec <= currentMsec && currentMsec < ___StarLaunchMsec) + { + var fadeInLength = Math.Min(200.0f, ___StarLaunchMsec - fadeInSecondMsec); + // var fadeInLength = ___StarLaunchMsec - fadeInSecondMsec; + color.a = 0.5f + 0.5f * Math.Min(1f, (currentMsec - fadeInSecondMsec) / fadeInLength); + } + + if (!___BreakFlag) + { + for (var index = 0; index < ____dispLaneNum; ++index) + { + if (index >= ____spriteRenders.Count) break; + ____spriteRenders[index].color = color; + } + } + else + { + for (var index = 0; index < ____dispLaneNum; ++index) + { + if (index >= ____breakSpriteRenders.Count) break; + ____breakSpriteRenders[index].SpriteRender.color = color; + } + } + + return false; + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(SlideFan), "UpdateAlpha")] + private static bool UpdateFanAlphaOverwrite( + SlideRoot __instance, + float ___StartMsec, float ___AppearMsec, float ___StarLaunchMsec, float ___DefaultMsec, + Color ____defaultColor, SpriteRenderer[] ____spriteLines + ) + { + var currentMsec = NotesManager.GetCurrentMsec(); + + var slideSpeed = (int) Singleton.Instance.GetGameScore(__instance.MonitorId).UserOption.SlideSpeed; + var defaultFadeInLength = (21 - slideSpeed) / 10.5f * ___DefaultMsec; + var fadeInFirstMsec = Math.Max(___StartMsec, ___AppearMsec - defaultFadeInLength); + var fadeInSecondMsec = Math.Max(___AppearMsec, ___StarLaunchMsec - defaultFadeInLength); + // var fadeInSecondMsec = ___AppearMsec; + var color = ____defaultColor; + + if (currentMsec < fadeInFirstMsec) + { + color.a = 0.0f; + } + else if (fadeInFirstMsec <= currentMsec && currentMsec < ___AppearMsec) + { + var fadeInLength = Math.Min(200.0f, ___AppearMsec - fadeInFirstMsec); + color.a = 0.3f * Math.Min(1f, (currentMsec - fadeInFirstMsec) / fadeInLength); + } + else if (___AppearMsec <= currentMsec && currentMsec < fadeInSecondMsec) + { + color.a = 0.3f; + } + else if (fadeInSecondMsec <= currentMsec && currentMsec < ___StarLaunchMsec) + { + var fadeInLength = Math.Min(200.0f, ___StarLaunchMsec - fadeInSecondMsec); + // var fadeInLength = ___StarLaunchMsec - fadeInSecondMsec; + color.a = 0.3f + 0.3f * Math.Min(1f, (currentMsec - fadeInSecondMsec) / fadeInLength); + } + else + { + color.a = 0.6f; + } + + foreach (SpriteRenderer spriteLine in ____spriteLines) + spriteLine.color = color; + + return false; + } + +} \ No newline at end of file