diff --git a/AquaMai/Visual/BreakSlideJudgeBlink.cs b/AquaMai/Visual/BreakSlideJudgeBlink.cs index 56308f66..3c8ef6cf 100644 --- a/AquaMai/Visual/BreakSlideJudgeBlink.cs +++ b/AquaMai/Visual/BreakSlideJudgeBlink.cs @@ -13,12 +13,11 @@ public class BreakSlideJudgeBlink [HarmonyPostfix] [HarmonyPatch(typeof(SlideJudge), "UpdateBreakEffectAdd")] private static void FixBreakSlideJudgeBlink( - SpriteRenderer ___SpriteRenderAdd, SpriteRenderer ___SpriteRender, - SlideJudge.SlideJudgeType ____judgeType, SlideJudge.SlideAngle ____angle + SpriteRenderer ___SpriteRenderAdd, int ____addEffectCount ) { if (!___SpriteRenderAdd.gameObject.activeSelf) return; - float num = ___SpriteRenderAdd.color.r; + float num = (____addEffectCount & 0b10) >> 1; ___SpriteRenderAdd.color = new Color(num, num, num, 1f); } } diff --git a/AquaMai/Visual/Config.cs b/AquaMai/Visual/Config.cs index 8e1f436b..173419c9 100644 --- a/AquaMai/Visual/Config.cs +++ b/AquaMai/Visual/Config.cs @@ -84,4 +84,24 @@ 这个 Patch 让 BreakSlide 的 Critical 判定也可以像 BreakTap 一样闪 推荐与自定义皮肤一起使用 (否则视觉效果可能并不好) """)] public bool BreakSlideJudgeBlink { get; set; } + + [ConfigComment( + en: """ + Make the Slide Track disappear with an inward-shrinking animation, similar to AstroDX + """, + zh: """ + 使 Slide Track 消失时有类似 AstroDX 一样的向内缩入的动画 + """)] + public bool SlideArrowAnimation { get; set; } + + [ConfigComment( + en: """ + Invert the Slide hierarchy, so that the new Slide appears on top like Maimai classic + Enable to support color changing effects achieved by overlaying multiple stars + """, + zh: """ + 反转 Slide 层级, 使新出现的 Slide 像旧框一样显示在上层 + 启用以支持通过叠加多个星星达成的变色效果 + """)] + public bool SlideLayerReverse { get; set; } } diff --git a/AquaMai/Visual/DisableTrackStartTabs.cs b/AquaMai/Visual/DisableTrackStartTabs.cs index a02376e2..92823a48 100644 --- a/AquaMai/Visual/DisableTrackStartTabs.cs +++ b/AquaMai/Visual/DisableTrackStartTabs.cs @@ -1,5 +1,6 @@ using HarmonyLib; using Monitor; +using TMPro; using UI; using UnityEngine; @@ -13,7 +14,8 @@ public class DisableTrackStartTabs [HarmonyPatch(typeof(TrackStartMonitor), "SetTrackStart")] private static void DisableTabs( SpriteCounter ____trackNumber, SpriteCounter ____bossTrackNumber, SpriteCounter ____utageTrackNumber, - MultipleImage ____musicTabImage, GameObject[] ____musicTabObj, GameObject ____derakkumaRoot + MultipleImage ____musicTabImage, GameObject[] ____musicTabObj, GameObject ____derakkumaRoot, + TimelineRoot ____musicDetail ) { ____trackNumber.transform.parent.gameObject.SetActive(false); @@ -24,5 +26,15 @@ private static void DisableTabs( ____musicTabObj[1].gameObject.SetActive(false); ____musicTabObj[2].gameObject.SetActive(false); ____derakkumaRoot.SetActive(false); + var traverse = Traverse.Create(____musicDetail); + traverse.Field("_achivement_Base").Value.ChangeSprite(1); + traverse.Field("_clearRank_Base").Value.ChangeSprite(1); + traverse.Field("_achivement_Text").Value.gameObject.SetActive(false); + traverse.Field("_achivement_decimal_Text").Value.gameObject.SetActive(false); + traverse.Field("_achivement_percent_Text").Value.gameObject.SetActive(false); + traverse.Field("_clearRank_Image").Value.gameObject.SetActive(false); + traverse.Field("_deluxScore_Obj").Value.SetActive(false); + traverse.Field("_comboRank_Image").Value.ChangeSprite(0); + traverse.Field("_syncRank_Image").Value.ChangeSprite(0); } } diff --git a/AquaMai/Visual/SlideArrowAnimation.cs b/AquaMai/Visual/SlideArrowAnimation.cs new file mode 100644 index 00000000..c8e4b542 --- /dev/null +++ b/AquaMai/Visual/SlideArrowAnimation.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using HarmonyLib; +using Manager; +using Monitor; +using Monitor.Game; +using Process; +using UnityEngine; + +namespace AquaMai.Visual; + +public class SlideArrowAnimation +{ + private static List _animatingSpriteRenderers = []; + + [HarmonyTranspiler] + [HarmonyPatch(typeof(SlideRoot), "NoteCheck")] + private static IEnumerable GetUnVisibleColorHook(IEnumerable instructions) + { + var methodGetUnVisibleColor = AccessTools.Method(typeof(SlideRoot), "GetUnVisibleColor"); + + var oldInstList = new List(instructions); + var newInstList = new List(); + + for (var i = 0; i < oldInstList.Count; i++) + { + var inst = oldInstList[i]; + if (inst.Calls(methodGetUnVisibleColor)) + { + // 现在栈上应该有: SpriteRenderer, SlideRoot(this) + // 这一条 IL 会消耗 this, 调用 GetUnVisibleColor(), 推一个 Color 到栈上 + // 然后接下来的一条 IL 是调用 SpriteRenderer.color 的 setter 把 SpriteRenderer 和 Color 一起消耗掉 + // 我们现在直接用一个 static method 消耗掉 SpriteRenderer 和 this + // 所以要忽略当前 IL, 再忽略下一条 IL, 然后构造一个 Call + + // ReSharper disable once ConvertClosureToMethodGroup + var redirect = CodeInstruction.Call((SpriteRenderer r, SlideRoot s) => OnSlideArrowDisable(r, s)); + newInstList.Add(redirect); + i++; // 跳过下一条 IL + } + else + { + newInstList.Add(inst); + } + } + return newInstList; + } + + public static void OnSlideArrowDisable(SpriteRenderer renderer, SlideRoot slideRoot) + { + _animatingSpriteRenderers.Add(renderer); + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(SlideRoot), "SetArrowObject")] + private static void RemoveArrowAnimation(GameObject arrowobj) + { + var spriteRenderer = arrowobj.GetComponent(); + spriteRenderer.transform.localScale = Vector3.one; + if (_animatingSpriteRenderers.Contains(spriteRenderer)) + { + _animatingSpriteRenderers.Remove(spriteRenderer); + } + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(SlideRoot), "SetBreakArrowObject")] + private static void RemoveBreakArrowAnimation(GameObject breakArrowobj) + { + var breakSlideObj = breakArrowobj.GetComponent(); + breakSlideObj.SpriteRender.transform.localScale = Vector3.one; + if (_animatingSpriteRenderers.Contains(breakSlideObj.SpriteRender)) + { + _animatingSpriteRenderers.Remove(breakSlideObj.SpriteRender); + } + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(GameCtrl), "UpdateNotes")] + private static void OnGameCtrlUpdateNotesLast() + { + for (var num = _animatingSpriteRenderers.Count - 1; num >= 0; num--) + { + var spriteRenderer = _animatingSpriteRenderers[num]; + if (spriteRenderer == null) + { + _animatingSpriteRenderers.RemoveAt(num); + } + else + { + var localScale = spriteRenderer.transform.localScale; + var scale = localScale.y - NotesManager.GetAddMSec() / 150f; + if (scale <= 0) + { + spriteRenderer.transform.localScale = new Vector3(1f, 0f, 1f); + spriteRenderer.color = new Color(1f, 1f, 1f, 0f); + _animatingSpriteRenderers.RemoveAt(num); + } + else + { + localScale.y = scale; + spriteRenderer.color = new Color(1f, 1f, 1f, Mathf.Sqrt(scale)); + spriteRenderer.transform.localScale = localScale; + } + } + } + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(GameProcess), "SetRelease")] + private static void OnBeforeGameProcessSetRelease() + { + _animatingSpriteRenderers.Clear(); + } +} \ No newline at end of file diff --git a/AquaMai/Visual/SlideLayerReverse.cs b/AquaMai/Visual/SlideLayerReverse.cs new file mode 100644 index 00000000..1e6958ab --- /dev/null +++ b/AquaMai/Visual/SlideLayerReverse.cs @@ -0,0 +1,66 @@ +using System.Collections.Generic; +using HarmonyLib; +using Monitor; +using UnityEngine; + +namespace AquaMai.Visual; + +public class SlideLayerReverse +{ + [HarmonyPostfix] + [HarmonyPatch(typeof(SlideRoot), "Initialize")] + private static void CalcArrowLayer( + bool ___BreakFlag, List ____spriteRenders, List ____breakSpriteRenders, + int ___SlideIndex, int ____baseArrowSortingOrder + ) + { + // 原本的 sortingOrder 是 -(SlideIndex + _baseArrowSortingOrder + index) + // 令 orderBase = SlideIndex + _baseArrowSortingOrder + // 分配给这条 slide 的 sortingOrder 范围是 -(orderBase + count - 1) ~ -(orderBase) + // 现在要保留 slide 内部箭头顺序, 但使得 slide 间次序反转 + // 范围会变成 orderBase ~ orderBase + count - 1 + // 其中原本是 -(orderBase) 的箭头应该调整为 orderBase + count - 1 + + var orderBase = ___SlideIndex + ____baseArrowSortingOrder; // SlideIndex + _baseArrowSortingOrder + if (!___BreakFlag) + { + var lastIdx = ____spriteRenders.Count - 1; + for (var index = 0; index < ____spriteRenders.Count; index++) + { + var renderer = ____spriteRenders[index]; + renderer.sortingOrder = -32700 + orderBase + lastIdx - index; + } + } + else + { + var lastIdx = ____breakSpriteRenders.Count - 1; + for (var index = 0; index < ____breakSpriteRenders.Count; index++) + { + var breakSlide = ____breakSpriteRenders[index]; + breakSlide.SetSortingOrder(-32700 + orderBase + lastIdx - index); + } + } + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(SlideFan), "Initialize")] + private static void CalcFanArrowLayer( + SpriteRenderer[] ____spriteLines, SpriteRenderer[] ____effectSprites, + int ___SlideIndex, int ____baseArrowSortingOrder + ) + { + var orderBase = ___SlideIndex + ____baseArrowSortingOrder; // SlideIndex + _baseArrowSortingOrder + var lastIdx = ____spriteLines.Length - 1; + for (var index = 0; index < ____spriteLines.Length; index++) + { + var renderer = ____spriteLines[index]; + renderer.sortingOrder = -32700 + orderBase + lastIdx - index; + } + lastIdx = ____effectSprites.Length - 1; + for (var index = 0; index < ____effectSprites.Length; index++) + { + var renderer = ____effectSprites[index]; + renderer.sortingOrder = 1000 + orderBase + lastIdx - index; + } + } +} \ No newline at end of file