From 606e372ebbe262ee61b9ca3e51dde7e2b9201be3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=97=E9=9B=B2=E3=80=80=E3=81=82=E3=81=A5=E3=81=8D?= Date: Sun, 21 Dec 2025 15:08:17 +0800 Subject: [PATCH 01/16] =?UTF-8?q?=E5=8A=A0=E5=85=A5=E5=8A=9B=E5=A4=A7?= =?UTF-8?q?=E7=A0=96=E9=A3=9E=E6=A8=A1=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement CustomSkinsPlusStatic for full-scene background replacement with static injection. Includes methods for loading custom sprites and textures, and patches for rendering graphics. --- AquaMai.Mods/Fancy/RsOverride.cs | 161 +++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 AquaMai.Mods/Fancy/RsOverride.cs diff --git a/AquaMai.Mods/Fancy/RsOverride.cs b/AquaMai.Mods/Fancy/RsOverride.cs new file mode 100644 index 00000000..53b448c5 --- /dev/null +++ b/AquaMai.Mods/Fancy/RsOverride.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections.Generic; +using System.IO; +using AquaMai.Config.Attributes; +using AquaMai.Core.Helpers; +using HarmonyLib; +using MelonLoader; +using UnityEngine; +using UnityEngine.UI; + +namespace AquaMai.Mods.Fancy; + +[ConfigSection( + name: "【危险功能】力大砖飞", + en: "Full-scene background replacement. Static injection.", + zh: "适用于便捷魔改的自定义全场景图片。警告:可能对游戏造成未知性能影响")] +public class CustomSkinsPlusStatic +{ + [ConfigEntry(name: "目录")] + private static string skinsDir = "LocalAssets/Skins"; + + private static readonly Dictionary SpritePool = new Dictionary(); + private static readonly Dictionary TexturePool = new Dictionary(); + + public static void OnBeforePatch() + { + string resolvedPath = ""; + try { resolvedPath = FileSystem.ResolvePath(skinsDir); } + catch { resolvedPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, skinsDir); } + + if (!Directory.Exists(resolvedPath)) { + Directory.CreateDirectory(resolvedPath); + return; + } + + foreach (var file in Directory.GetFiles(resolvedPath, "*.png")) + { + try { + byte[] data = File.ReadAllBytes(file); + var tex = new Texture2D(1, 1, TextureFormat.RGBA32, false); + if (tex.LoadImage(data)) { + var name = Path.GetFileNameWithoutExtension(file).ToLower(); + var sprite = Sprite.Create(tex, new Rect(0f, 0f, tex.width, tex.height), new Vector2(0.5f, 0.5f)); + sprite.name = name; + tex.name = name; + + SpritePool[name] = sprite; + TexturePool[name] = tex; + } + } catch { } + } + } + + + // 渲染底层 + + + [HarmonyPatch(typeof(Graphic), "Rebuild")] + [HarmonyPrefix] + private static void OnGraphicRebuild(Graphic __instance) + { + if (__instance == null) return; + + // 处理 Image 组件 + if (__instance is Image img && img.sprite != null) + { + if (SpritePool.TryGetValue(img.sprite.name.ToLower(), out var customSprite)) + { + if (img.sprite != customSprite) + { + img.sprite = customSprite; + // 强制恢复 + img.canvasRenderer.SetColor(Color.white); + } + } + } + // 处理RawImage + else if (__instance is RawImage rImg && rImg.texture != null) + { + if (TexturePool.TryGetValue(rImg.texture.name.ToLower(), out var customTex)) + { + if (rImg.texture != customTex) + { + rImg.texture = customTex; + rImg.canvasRenderer.SetColor(Color.white); + } + } + } + } + + + //全量暴力同步 + + + [HarmonyPatch(typeof(CanvasScaler), "OnEnable")] + [HarmonyPostfix] + private static void OnCanvasEnable(CanvasScaler __instance) + { + // 立即扫描 + var images = __instance.GetComponentsInChildren(true); + foreach (var img in images) + { + if (img.sprite != null && SpritePool.TryGetValue(img.sprite.name.ToLower(), out var s)) + img.sprite = s; + } + + var rImages = __instance.GetComponentsInChildren(true); + foreach (var rImg in rImages) + { + if (rImg.texture != null && TexturePool.TryGetValue(rImg.texture.name.ToLower(), out var t)) + rImg.texture = t; + } + } +} +/* +这是一大坨屎山,原理上和 CustomSkinsPlus 差不多? +后续补:功能上差不多,原理上大概完全不一样了。 +这个东西可能有点危险,毕竟是静态劫持所有图片赋值操作,理论上会影响性能 +不过好处是可以彻底更换大概几乎可能的所有图片资源 +Powered By AkiACG Team +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣠⣤⣄⣀⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠛⠻⢿⣿⣿⣷⣦⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠻⣿⣿⣿⣿⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣠⣤⣤⣤⣴⣶⣶⣦⣤⣤⣤⣤⣀⣀⠹⣿⡇⣀⣀⣀⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣤⣶⣶⣦⣤⣄⡀⢀⡤⠶⠿⠿⠟⢛⣛⠿⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣇⢋⣼⣿⣿⣿⣿⣿⣿⣿⣶⡦⢤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⢀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄⠀⠀⠀⠀⠀⣁⣤⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣦⣌⡙⠲⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⢟⣭⣶⣶⣶⣮⣝⢿⣿⣿⣿⣧⠀⣠⣶⣿⣿⣿⣿⣿⣿⣿⣿⡿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠻⢿⣿⣿⣿⣿⣿⣿⣷⣌⠻⣷⣦⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⡸⠟⣛⣛⢿⣿⣿⣧⢻⣿⣿⣿⣇⢻⣿⣿⣿⡿⢋⣸⣿⣿⣿⣤⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣷⣶⣿⣿⣿⣿⣿⣿⣿⣿⣧⡘⢿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣾⣿⣿⡇⣻⣿⣿⢸⣿⣿⣿⣿⢸⣿⣿⠟⣠⣿⣿⣿⡿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡟⢻⣿⣿⣿⣿⣿⣿⣷⡈⢿⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⢩⣾⠟⣫⣵⣿⣿⣿⣿⣿⢸⡿⢃⣼⣿⣿⣿⡟⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡈⢿⣿⣿⣿⣿⣿⣿⣷⡈⢿⣿⣿⣿⣧⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠈⢿⣿⣿⣿⣿⣿⣿⣿⣦⠭⣭⣿⣿⣿⣿⣿⣿⡟⡼⢡⣾⣿⣿⣿⡟⡴⠀⣿⣿⣿⣿⣿⣿⠉⣿⣿⣿⠉⣿⣿⣿⣿⣿⣿⣿⣷⠘⣿⣿⡿⢿⣿⣿⣿⣧⠘⣿⣿⣿⣿⣷⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠈⠻⣿⣿⣿⣿⣿⣿⣇⠾⢆⣽⣿⣿⣿⣿⠟⡴⢡⣿⣿⣿⣿⡟⣼⡇⢸⣿⣿⣿⣿⣿⣿⢰⣿⣿⣿⢠⢻⣿⣿⣿⣿⣿⡿⢟⣃⡭⠶⢚⣡⣿⣿⣿⣿⡆⢻⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠈⠙⠿⣿⣿⣿⣿⣿⣿⣿⡿⢛⣋⣵⡾⢠⣿⣿⣿⣿⡿⣰⣿⠀⣿⣿⣿⣿⣿⣿⣿⢸⣿⣿⡇⣿⡜⣿⣿⣿⣿⣿⣧⣭⣴⡀⢟⣋⣭⠙⣿⣿⣿⣷⠘⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⢩⣥⣶⣬⣍⣼⣿⣿⠃⣾⣿⣿⢛⣿⢣⣿⣿⢠⣿⣿⣿⣿⣿⣿⣿⢸⣿⣿⡇⣿⡧⠿⠿⣿⣿⣿⣿⡏⠵⠚⢋⣥⣶⣾⣿⣿⣿⣿⡄⣿⣿⣿⣿⣿⠙⢿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⣿⡟⢰⣉⣭⠄⣤⣆⣾⣿⣷⢠⣿⣿⣿⣿⣿⣿⣿⠘⣿⣿⢹⣿⣿⡔⣶⣶⣤⣬⣉⡙⠻⢧⢸⣿⣿⣿⣿⣿⣿⣿⡇⢸⣿⣿⣿⣿⡈⠀⠑⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⠃⣾⣿⣏⣴⡿⣼⣿⣿⣿⢸⣿⣿⣿⣿⣿⣿⣿⡇⣿⣿⣸⣿⣿⣷⢻⣿⣿⣿⣿⣿⣿⣶⠘⣿⣿⣿⣿⣿⣿⣿⡇⢸⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⠀⠛⠛⠚⠘⠓⠿⠿⠿⣯⢈⡛⢹⣿⣿⣿⣿⣿⣧⢿⡏⣿⢿⣿⣿⣯⢿⡿⣿⣿⣿⣿⣿⠀⣿⣿⣿⣿⣿⣿⣿⡇⢸⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⡏⢸⣿⣿⣿⣿⣟⠀⠀⣠⣄⣀⡤⣀⣀⣀⣀⡀⠉⠈⣿⣿⣿⣿⣿⣿⡸⣧⣿⡿⠉⠉⠉⠈⠁⠈⠉⠉⠙⠛⠀⣿⣿⣿⣿⣿⣿⣿⡇⢸⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⠠⠘⣿⣿⣿⣿⣿⠀⢠⣿⣿⣸⢱⣿⣿⣿⣿⣿⣧⠸⣎⢿⣿⣿⣿⣿⣧⢹⣿⣷⡖⣴⣶⣶⣶⣶⣦⢰⣄⢠⠀⣿⣿⣿⣿⣿⣿⣿⠁⣿⣿⣿⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡄⡿⠀⣀⠹⣿⣿⣿⣿⡄⢸⣿⣿⡏⣿⣿⣿⣿⣿⣿⣿⡄⢻⣷⡹⢿⣿⡿⣿⣞⣿⣿⣼⣿⣿⣿⣿⣿⣿⡦⣝⠲⢘⣿⣿⣿⣿⣿⣿⠏⠀⣿⣿⣿⣿⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠀⠀⠀⣿⣦⠙⣿⣿⣿⣷⠘⣿⣿⡿⣿⣿⣿⣿⣿⣿⣿⢷⣷⣿⣿⣷⣽⣓⣐⣭⣬⡿⣿⣿⣿⣿⣿⣿⣿⣇⣿⣷⢸⣿⣿⡟⢻⣿⠏⡴⢠⣿⣿⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⠿⣷⣌⠻⡟⠻⣇⢻⣿⣧⢻⣿⣿⣿⣿⣿⡟⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣹⣿⡇⣾⣿⠟⠀⡼⢃⣾⠇⣸⣿⣿⣿⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢹⢠⡙⢿⣷⣤⡄⣬⣑⣙⣿⣷⣝⡻⠿⢿⣫⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣮⡻⢿⣿⡿⢟⣵⣿⡟⣸⠟⣡⠎⢀⣴⣿⠋⣼⣿⣿⣿⣿⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢸⡇⣦⣉⡛⠳⠘⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣯⣭⣭⣭⣭⣭⣍⣻⣿⣿⣿⣷⡶⣾⣿⣿⠏⣈⠅⣼⡿⠖⣁⠘⣡⣾⣿⠛⢿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠓⢿⣿⠀⠱⣌⠛⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣽⣿⣿⡿⣿⣷⣿⣿⣿⣿⣿⣿⣿⣿⣵⣿⣩⣬⡴⢂⣾⣿⣿⣿⡿⠁⠀⢸⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⠀⠀⠈⠿⣦⣌⡙⠻⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠟⢋⡡⢴⣿⣿⣿⡿⠋⠀⠀⠀⢸⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⠀⠀⠀⠀⠈⠻⢿⣿⣶⣶⣬⣍⠙⠛⣛⣋⣉⠹⡏⠉⣉⣉⣛⣛⣛⣛⡛⠉⢥⣤⣶⠿⠋⢂⣾⣿⠟⠋⠀⠀⠀⠀⠀⢸⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⠇⠀⠀⠀⠀⠀⠀⠀⠈⠙⠻⠿⠅⠈⢿⣿⣿⠏⣼⣷⡀⣿⣿⣿⣿⣿⣿⣿⣇⠘⠋⠁⠀⠀⠚⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠻⠿⢦⡍⢱⡇⣿⣿⣿⡿⠿⠟⠛⢋⣠⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣒⡖⠶⣤⠇⣼⠇⣎⣉⣤⣴⣶⣾⣿⣿⣿⣿⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⡾⢿⣿⣿⣶⣤⣅⠙⡂⠿⠿⣿⣿⣿⣿⣿⣿⣿⡏⢹⠃⡤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⢸⡇⢸⣿⣿⣿⣿⣿⠀⣧⢰⣶⣤⣤⣉⣙⣿⣿⣿⡇⡈⢰⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⡇⢸⣿⣿⣿⣿⣿⢸⣿⢸⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣞⡇⠀⢸⣿⣿⣿⣿⣿⢸⡇⢸⣿⣿⣿⣿⣿⣿⣿⣿⠀⢀⣿⣿⣿⣿⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣷⠀⢸⣿⣿⣿⣿⣿⢸⡇⢸⣿⣿⣿⣿⣿⣿⣿⣿⡆⢸⣿⣿⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⣿⡆⠘⣿⣿⣿⣿⣟⢸⡇⢼⣿⣿⣿⣿⣿⣿⣿⣿⣇⠸⣿⣿⣿⣿⣿⣿⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⠀⣿⣿⣿⣿⣿⢸⡇⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⣿⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⡄⢹⣿⣿⣿⣿⢸⡇⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄⢻⣿⣿⣿⣿⣿⣿⣷⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +*/ From e514c7b5427a729265e2fc3b437cddc539f19da9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=97=E9=9B=B2=E3=80=80=E3=81=82=E3=81=A5=E3=81=8D?= Date: Sun, 21 Dec 2025 15:09:43 +0800 Subject: [PATCH 02/16] =?UTF-8?q?[+]=20=E4=BF=AE=E6=94=B9=E5=8A=9B?= =?UTF-8?q?=E5=A4=A7=E7=A0=96=E9=A3=9E=E8=AD=A6=E5=91=8A=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updated warning message for potential performance impact and conflicts. --- AquaMai.Mods/Fancy/RsOverride.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AquaMai.Mods/Fancy/RsOverride.cs b/AquaMai.Mods/Fancy/RsOverride.cs index 53b448c5..74eb2635 100644 --- a/AquaMai.Mods/Fancy/RsOverride.cs +++ b/AquaMai.Mods/Fancy/RsOverride.cs @@ -13,10 +13,10 @@ namespace AquaMai.Mods.Fancy; [ConfigSection( name: "【危险功能】力大砖飞", en: "Full-scene background replacement. Static injection.", - zh: "适用于便捷魔改的自定义全场景图片。警告:可能对游戏造成未知性能影响")] + zh: "适用于便捷魔改的自定义全场景图片。警告:可能对游戏造成未知性能影响,可能与其他模组冲突?")] public class CustomSkinsPlusStatic { - [ConfigEntry(name: "目录")] + [ConfigEntry(name: "资源目录")] private static string skinsDir = "LocalAssets/Skins"; private static readonly Dictionary SpritePool = new Dictionary(); From 5acfb37b8bdd91cff9f9a7f6dab2a5c5580fc24c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=97=E9=9B=B2=E3=80=80=E3=81=82=E3=81=A5=E3=81=8D?= Date: Sun, 21 Dec 2025 18:25:43 +0800 Subject: [PATCH 03/16] Update AquaMai.Mods/Fancy/RsOverride.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 凌莞~(=^▽^=) --- AquaMai.Mods/Fancy/RsOverride.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AquaMai.Mods/Fancy/RsOverride.cs b/AquaMai.Mods/Fancy/RsOverride.cs index 74eb2635..02ba8889 100644 --- a/AquaMai.Mods/Fancy/RsOverride.cs +++ b/AquaMai.Mods/Fancy/RsOverride.cs @@ -47,7 +47,7 @@ public static void OnBeforePatch() SpritePool[name] = sprite; TexturePool[name] = tex; } - } catch { } + } catch (Exception e) { MelonLogger.Error($"Failed to load custom skin '{Path.GetFileName(file)}': {e.Message}"); } } } From 9af16afd5cc1e89b2284b81e23645a815b52fd58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=97=E9=9B=B2=E3=80=80=E3=81=82=E3=81=A5=E3=81=8D?= Date: Sun, 21 Dec 2025 18:25:55 +0800 Subject: [PATCH 04/16] Update AquaMai.Mods/Fancy/RsOverride.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 凌莞~(=^▽^=) --- AquaMai.Mods/Fancy/RsOverride.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/AquaMai.Mods/Fancy/RsOverride.cs b/AquaMai.Mods/Fancy/RsOverride.cs index 02ba8889..7103df63 100644 --- a/AquaMai.Mods/Fancy/RsOverride.cs +++ b/AquaMai.Mods/Fancy/RsOverride.cs @@ -25,8 +25,7 @@ public class CustomSkinsPlusStatic public static void OnBeforePatch() { string resolvedPath = ""; - try { resolvedPath = FileSystem.ResolvePath(skinsDir); } - catch { resolvedPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, skinsDir); } + resolvedPath = FileSystem.ResolvePath(skinsDir); if (!Directory.Exists(resolvedPath)) { Directory.CreateDirectory(resolvedPath); From a0f4e65f95afa39ba83adfe40a560ddfcb8f4414 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=97=E9=9B=B2=E3=80=80=E3=81=82=E3=81=A5=E3=81=8D?= Date: Sun, 21 Dec 2025 18:26:03 +0800 Subject: [PATCH 05/16] Update AquaMai.Mods/Fancy/RsOverride.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 凌莞~(=^▽^=) --- AquaMai.Mods/Fancy/RsOverride.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AquaMai.Mods/Fancy/RsOverride.cs b/AquaMai.Mods/Fancy/RsOverride.cs index 7103df63..57037e87 100644 --- a/AquaMai.Mods/Fancy/RsOverride.cs +++ b/AquaMai.Mods/Fancy/RsOverride.cs @@ -17,7 +17,7 @@ namespace AquaMai.Mods.Fancy; public class CustomSkinsPlusStatic { [ConfigEntry(name: "资源目录")] - private static string skinsDir = "LocalAssets/Skins"; + private static string skinsDir = "LocalAssets/GlobalTextureOverride"; private static readonly Dictionary SpritePool = new Dictionary(); private static readonly Dictionary TexturePool = new Dictionary(); From 7b69c29109e77e4ca3e74f17bcea4aa9a2eb25c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=97=E9=9B=B2=E3=80=80=E3=81=82=E3=81=A5=E3=81=8D?= Date: Sun, 21 Dec 2025 18:26:13 +0800 Subject: [PATCH 06/16] Update AquaMai.Mods/Fancy/RsOverride.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 凌莞~(=^▽^=) --- AquaMai.Mods/Fancy/RsOverride.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/AquaMai.Mods/Fancy/RsOverride.cs b/AquaMai.Mods/Fancy/RsOverride.cs index 57037e87..52a5a514 100644 --- a/AquaMai.Mods/Fancy/RsOverride.cs +++ b/AquaMai.Mods/Fancy/RsOverride.cs @@ -11,9 +11,9 @@ namespace AquaMai.Mods.Fancy; [ConfigSection( - name: "【危险功能】力大砖飞", - en: "Full-scene background replacement. Static injection.", - zh: "适用于便捷魔改的自定义全场景图片。警告:可能对游戏造成未知性能影响,可能与其他模组冲突?")] + name: "力大砖飞", + en: "[Dangerous] Full-scene background replacement. Static injection.", + zh: "【危险功能】适用于便捷魔改的自定义全场景图片。警告:可能对游戏造成未知性能影响,可能与其他模组冲突?")] public class CustomSkinsPlusStatic { [ConfigEntry(name: "资源目录")] From 692d10bb7d76c0145bab4479c5934e38317f4f70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=87=8C=E8=8E=9E=7E=28=3D=5E=E2=96=BD=5E=3D=29?= Date: Sun, 21 Dec 2025 18:27:54 +0800 Subject: [PATCH 07/16] Apply suggestion from @clansty --- AquaMai.Mods/Fancy/RsOverride.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AquaMai.Mods/Fancy/RsOverride.cs b/AquaMai.Mods/Fancy/RsOverride.cs index 52a5a514..20008f98 100644 --- a/AquaMai.Mods/Fancy/RsOverride.cs +++ b/AquaMai.Mods/Fancy/RsOverride.cs @@ -12,7 +12,7 @@ namespace AquaMai.Mods.Fancy; [ConfigSection( name: "力大砖飞", - en: "[Dangerous] Full-scene background replacement. Static injection.", + en: "[Dangerous] Full-scene texture / sprite replacement. Static injection.", zh: "【危险功能】适用于便捷魔改的自定义全场景图片。警告:可能对游戏造成未知性能影响,可能与其他模组冲突?")] public class CustomSkinsPlusStatic { From 17c953e0db2037571a6d290c7b6705b8f80b1a08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=97=E9=9B=B2=E3=80=80=E3=81=82=E3=81=A5=E3=81=8D?= Date: Sun, 21 Dec 2025 18:30:55 +0800 Subject: [PATCH 08/16] =?UTF-8?q?[F]=20=E4=BF=AE=E6=94=B9=E7=9B=AE?= =?UTF-8?q?=E5=BD=95=E9=81=BF=E5=85=8D=E6=A8=A1=E7=BB=84=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AquaMai.Mods/Fancy/RsOverride.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AquaMai.Mods/Fancy/RsOverride.cs b/AquaMai.Mods/Fancy/RsOverride.cs index 20008f98..9d57ab34 100644 --- a/AquaMai.Mods/Fancy/RsOverride.cs +++ b/AquaMai.Mods/Fancy/RsOverride.cs @@ -17,7 +17,7 @@ namespace AquaMai.Mods.Fancy; public class CustomSkinsPlusStatic { [ConfigEntry(name: "资源目录")] - private static string skinsDir = "LocalAssets/GlobalTextureOverride"; + private static string skinsDir = "LocalAssets/ResourcesOverride"; private static readonly Dictionary SpritePool = new Dictionary(); private static readonly Dictionary TexturePool = new Dictionary(); From 80e835c93c08f7ce845b2b8c3bbd6ff18728a1fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=97=E9=9B=B2=E3=80=80=E3=81=82=E3=81=A5=E3=81=8D?= Date: Mon, 5 Jan 2026 20:08:29 +0800 Subject: [PATCH 09/16] =?UTF-8?q?[+]=20=E5=AF=B9=E8=BD=AC=E5=9C=BA?= =?UTF-8?q?=E5=8A=A8=E7=94=BB=E5=8A=9F=E8=83=BD=E8=BF=9B=E8=A1=8C=E4=BA=86?= =?UTF-8?q?=E5=8F=B2=E8=AF=97=E7=BA=A7=E5=8A=A0=E9=92=B1=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor SetFade class for better organization and functionality, including JSON loading and resource management. --- AquaMai.Mods/Fancy/SetFade.cs | 256 ++++++++++++++++++++++++---------- 1 file changed, 184 insertions(+), 72 deletions(-) diff --git a/AquaMai.Mods/Fancy/SetFade.cs b/AquaMai.Mods/Fancy/SetFade.cs index 83d941e0..e5df8f21 100644 --- a/AquaMai.Mods/Fancy/SetFade.cs +++ b/AquaMai.Mods/Fancy/SetFade.cs @@ -4,118 +4,230 @@ using Process; using UnityEngine; using UnityEngine.UI; -using System.Reflection; using System; +using System.IO; +using Manager; using MelonLoader; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using Monitor; namespace AquaMai.Mods.Fancy; -[ConfigSection( - name: "转场动画", +[ConfigSection(name: "转场动画PLUS", en: "Set Fade Animation", - zh: "修改转场动画为其他变种" -)] - + zh: "修改转场动画为其他变种")] public class SetFade { - [ConfigEntry( - name: "转场类型", - en: "Type: Non-Plus 0, Plus 1. (If SDEZ 1.60 can choose Festa 2)", - zh: "类型: Non-Plus 0, Plus 1. (SDEZ 1.60 限定可选 Festa 2)")] + [ConfigEntry(name: "转场类型", zh: "0:Normal, 1:Plus, 2:Festa(仅限1.60+)")] public static readonly int FadeType = 0; + [ConfigEntry(name: "[仅限1.55+]启用特殊KLD转场", zh: "仅在配置过的歌曲启用KLD转场。1.50及以下版本无效。1.50我不想适配了如果有人想适配可以dd我)@力大砖飞")] + public static readonly bool isKLDEnabled = true; + + private static readonly string JSONDir = "LocalAssets"; + private static readonly string JSONFileName = "CommonFadeList.json"; - private static bool isInitialized = false; private static bool isResourcePatchEnabled = false; + private static bool _isInitialized = false; private static Sprite[] subBGs = new Sprite[3]; + private static List cachedEntries = new List(); + + // 计数锁定逻辑变量 + private static int _kldRemainingCharges = 0; + private static CommonFadeEntry _activeKldConfig = null; + public class CommonFadeEntry { public int ID; public int isBlack; public int Type; public int FadeType; } [HarmonyPrepare] - public static bool SetFade_Prepare() + public static bool Prepare() { - SetFade_Initialize(); - if (!isInitialized) - MelonLogger.Msg("[SetFade] Initialization failed, this patch will not be applied."); - return isInitialized; + if (_isInitialized) return true; + subBGs[0] = Resources.Load("Process/ChangeScreen/Sprites/Sub_01"); + subBGs[1] = Resources.Load("Process/ChangeScreen/Sprites/Sub_02"); + subBGs[2] = (GameInfo.GameVersion >= 26000) ? Resources.Load("Process/ChangeScreen/Sprites/Sub_03") : subBGs[0]; + LoadJsonManual(); + _isInitialized = true; + return true; } - private static void SetFade_Initialize() + // --- 1. 实时监听选曲:充能点 --- + [HarmonyPostfix] + [HarmonyPatch(typeof(MusicSelectMonitor), "UpdateRivalScore")] + [HarmonyPatch(typeof(MusicSelectMonitor), "SetRivalScore")] + public static void OnMusicSelectionChanged(MusicSelectProcess ____musicSelect) { - bool areSubBGsValid; - bool isFadeTypeValid; + if (!isKLDEnabled || ____musicSelect == null) return; - if (GameInfo.GameVersion != 26000) - { - subBGs[0] = Resources.Load("Process/ChangeScreen/Sprites/Sub_01"); - subBGs[1] = Resources.Load("Process/ChangeScreen/Sprites/Sub_02"); - areSubBGsValid = subBGs[0] != null && subBGs[1] != null; - isFadeTypeValid = FadeType == 0 || FadeType == 1; - } - else - { - subBGs[0] = Resources.Load("Process/ChangeScreen/Sprites/Sub_01"); - subBGs[1] = Resources.Load("Process/ChangeScreen/Sprites/Sub_02"); - subBGs[2] = Resources.Load("Process/ChangeScreen/Sprites/Sub_03"); - areSubBGsValid = subBGs[0] != null && subBGs[1] != null && subBGs[2] != null; - isFadeTypeValid = FadeType == 0 || FadeType == 1 || FadeType == 2; - } - - if (!areSubBGsValid) - MelonLogger.Msg($"[SwitchFade] Couldn't find SubBG sprites."); - - if (!isFadeTypeValid) - MelonLogger.Msg($"[SwitchFade] Invalid FadeType."); - - isInitialized = areSubBGsValid && isFadeTypeValid; + try { + var musicData = ____musicSelect.GetMusic(0)?.MusicData; + if (musicData != null) + { + var matched = cachedEntries.Find(e => e.ID == musicData.name.id); + if (matched != null) + { + if (_activeKldConfig != matched) + { + _activeKldConfig = matched; + _kldRemainingCharges = 3; // 锁定 3 次机会 + MelonLogger.Msg($"[SetFade] 目标锁定:ID {matched.ID},KLD 已充能 (3次)"); + } + } + else + { + _activeKldConfig = null; + _kldRemainingCharges = 0; + } + } + } catch { } } - - // 在显示转场前启用patch + // --- 2. 资源拦截触发 --- [HarmonyPrefix] [HarmonyPatch(typeof(FadeProcess), "OnStart")] - public static void FadeProcessOnStartPreFix() { isResourcePatchEnabled = true; } - [HarmonyPrefix] [HarmonyPatch(typeof(AdvertiseProcess), "InitFade")] - public static void AdvertiseProcessInitFadePreFix() { isResourcePatchEnabled = true; } - [HarmonyPrefix] [HarmonyPatch(typeof(NextTrackProcess), "OnStart")] - public static void NextTrackProcessOnStartPreFix() { isResourcePatchEnabled = true; } + public static void StartFadePrefix() + { + // 只有在有充能次数且配置存在时才开启拦截 + isResourcePatchEnabled = (_kldRemainingCharges > 0 && _activeKldConfig != null); + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(Resources), "Load", new[] { typeof(string), typeof(global::System.Type) })] + public static bool ResourcesLoadPrefix(ref string path, global::System.Type systemTypeInstance, ref UnityEngine.Object __result) + { + // 如果 KLD 拦截未激活,则执行普通重定向(力大砖飞) + if (!isResourcePatchEnabled) + { + if (FadeType >= 0 && FadeType <= 2) + { + string targetPath = $"Process/ChangeScreen/Prefabs/ChangeScreen_0{FadeType + 1}"; + if (path.StartsWith("Process/ChangeScreen/Prefabs/ChangeScreen_0") && path != targetPath) + { + if (GameInfo.GameVersion < 26000 && FadeType == 2) return true; + __result = Resources.Load(targetPath, systemTypeInstance); + return false; + } + } + return true; + } + + // KLD 资源拦截 + if (path.StartsWith("Process/ChangeScreen/Prefabs/ChangeScreen_0")) + { + __result = Resources.Load("Process/Kaleidxscope/Prefab/UI_KLD_ChangeScreen", systemTypeInstance); + return false; + } + if (path.StartsWith("Process/ChangeScreen/Prefabs/Sub_ChangeScreen")) + { + __result = Resources.Load("Process/Kaleidxscope/Prefab/UI_KLD_Sub_ChangeScreen", systemTypeInstance); + return false; + } + return true; + } - // 在显示转场后禁用patch + // --- 3. 后置处理:消耗次数与动画播放 --- [HarmonyPostfix] [HarmonyPatch(typeof(FadeProcess), "OnStart")] - public static void FadeProcessOnStartPostFix(GameObject[] ___fadeObject) { ReplaceSubBG(___fadeObject); } - [HarmonyPostfix] [HarmonyPatch(typeof(AdvertiseProcess), "InitFade")] - public static void AdvertiseProcessInitFadePostFix(GameObject[] ___fadeObject) { ReplaceSubBG(___fadeObject); } - [HarmonyPostfix] [HarmonyPatch(typeof(NextTrackProcess), "OnStart")] - public static void NextTrackProcessOnStartPostFix(GameObject[] ___fadeObject) { ReplaceSubBG(___fadeObject); } - - - private static void ReplaceSubBG(GameObject[] fadeObjects) + public static void GlobalPostfix(GameObject[] ___fadeObject) { - isResourcePatchEnabled = false; - foreach (var monitor in fadeObjects) + if (isResourcePatchEnabled && _activeKldConfig != null) { - var subBG = monitor.transform.Find("Canvas/Sub/Sub_ChangeScreen(Clone)/Sub_BG").GetComponent(); - subBG.sprite = subBGs[FadeType]; + _kldRemainingCharges--; // 消耗一次 + MelonLogger.Msg($"[SetFade] 触发 KLD 成功,剩余次数: {_kldRemainingCharges}"); + + if (___fadeObject != null) + { + foreach (var monitor in ___fadeObject) + DriveKLDAnimation(monitor, _activeKldConfig); + } } + else if (___fadeObject != null) + { + // 普通重定向模式下的 SubBG 替换 + foreach (var monitor in ___fadeObject) + ReplaceSubBG(monitor); + } + + isResourcePatchEnabled = false; // 关闭当次拦截锁 + + // 次数耗尽清理配置 + if (_kldRemainingCharges <= 0) _activeKldConfig = null; } - [HarmonyPrefix] - [HarmonyPatch(typeof(Resources), "Load", new[] { typeof(string), typeof(Type) })] - public static bool ResourcesLoadPrefix(ref string path, Type systemTypeInstance, ref UnityEngine.Object __result) + private static void ReplaceSubBG(GameObject monitor) + { + if (FadeType < 0 || FadeType >= subBGs.Length) return; + try { + var subBG = monitor.transform.Find("Canvas/Sub/Sub_ChangeScreen(Clone)/Sub_BG")?.GetComponent(); + if (subBG != null) subBG.sprite = subBGs[FadeType]; + } catch { } + } + + private static void DriveKLDAnimation(GameObject monitor, CommonFadeEntry cfg) { - if (isResourcePatchEnabled) + try { - if (path.StartsWith("Process/ChangeScreen/Prefabs/ChangeScreen_0") && - path != $"Process/ChangeScreen/Prefabs/ChangeScreen_0{FadeType + 1}") // 避免无限递归 + var main = monitor.transform.Find("Canvas/Main/UI_KLD_ChangeScreen(Clone)"); + var sub = monitor.transform.Find("Canvas/Sub/UI_KLD_Sub_ChangeScreen(Clone)"); + + // 根据你的要求修正动画映射 + string animName = cfg.FadeType switch { + 1 => "Out", + 2 => "Out_02", + 3 => "Out_03", + _ => "Out" + }; + + if (main != null) { - __result = Resources.Load($"Process/ChangeScreen/Prefabs/ChangeScreen_0{FadeType + 1}", systemTypeInstance); - return false; + var ctrl = main.GetComponent(); + if (ctrl != null) { + ctrl.SetBackGroundType(cfg.isBlack != 0 ? KaleidxScopeFadeController.BackGroundType.Black : KaleidxScopeFadeController.BackGroundType.Normal); + ctrl.SetSpriteType((KaleidxScopeFadeController.SpriteType)cfg.Type); + if (Enum.TryParse(animName, out var state)) + ctrl.PlayAnimation(state); + } } - } - return true; + if (sub != null) + { + var sCtrl = sub.GetComponent(); + if (sCtrl != null) { + sCtrl.SetBackGroundType(cfg.isBlack != 0 ? KaleidxScopeSubFadeController.BackGroundType.Black : KaleidxScopeSubFadeController.BackGroundType.Normal); + sCtrl.SetSpriteType((KaleidxScopeSubFadeController.SpriteType)cfg.Type); + sCtrl.PlayAnimation(KaleidxScopeSubFadeController.AnimState.In); + } + } + } catch { } + } + + private static void LoadJsonManual() + { + try { + string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, JSONDir, JSONFileName); + if (!File.Exists(path)) return; + string content = File.ReadAllText(path); + cachedEntries.Clear(); + var matches = Regex.Matches(content, @"\{[^{}]+\}"); + foreach (Match m in matches) { + string raw = m.Value; + var e = new CommonFadeEntry { + ID = ExtractInt(raw, "ID"), + isBlack = ExtractInt(raw, "isBlack"), + Type = ExtractInt(raw, "Type"), + FadeType = ExtractInt(raw, "FadeType") + }; + if (e.ID > 0) cachedEntries.Add(e); + } + MelonLogger.Msg($"[SetFade] 共载入 {cachedEntries.Count} 条 KLD 特殊配置。"); + } catch (Exception e) { MelonLogger.Error($"[SetFade] JSON加载出错: {e.Message}"); } + } + + private static int ExtractInt(string text, string key) { + var m = Regex.Match(text, $"\"{key}\"\\s*:\\s*\"?(\\d+)\"?"); + return (m.Success && int.TryParse(m.Groups[1].Value, out int res)) ? res : 0; } } From ebda3dab52f275d84da1abc14066a92a6b8a40d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=97=E9=9B=B2=E3=80=80=E3=81=82=E3=81=A5=E3=81=8D?= Date: Mon, 5 Jan 2026 20:09:59 +0800 Subject: [PATCH 10/16] Update KLD transition config entry description --- AquaMai.Mods/Fancy/SetFade.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AquaMai.Mods/Fancy/SetFade.cs b/AquaMai.Mods/Fancy/SetFade.cs index e5df8f21..b50a86a7 100644 --- a/AquaMai.Mods/Fancy/SetFade.cs +++ b/AquaMai.Mods/Fancy/SetFade.cs @@ -22,7 +22,7 @@ public class SetFade [ConfigEntry(name: "转场类型", zh: "0:Normal, 1:Plus, 2:Festa(仅限1.60+)")] public static readonly int FadeType = 0; - [ConfigEntry(name: "[仅限1.55+]启用特殊KLD转场", zh: "仅在配置过的歌曲启用KLD转场。1.50及以下版本无效。1.50我不想适配了如果有人想适配可以dd我)@力大砖飞")] + [ConfigEntry(name: "[仅限1.55+]启用特殊KLD转场,需要下载额外JSON文件", zh: "仅在配置过的歌曲启用KLD转场。1.50及以下版本无效。1.50我不想适配了如果有人想适配可以dd我)@力大砖飞")] public static readonly bool isKLDEnabled = true; private static readonly string JSONDir = "LocalAssets"; From 0ca1e8ffd03771132cfd387c8c825ae37ff6aeec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=97=E9=9B=B2=E3=80=80=E3=81=82=E3=81=A5=E3=81=8D?= Date: Mon, 5 Jan 2026 23:02:29 +0800 Subject: [PATCH 11/16] =?UTF-8?q?[F]=20=E4=BF=AE=E5=A4=8D=E9=83=A8?= =?UTF-8?q?=E5=88=86=E6=9B=B2=E7=9B=AE=E6=97=A0fade=20out=E5=8A=A8?= =?UTF-8?q?=E7=94=BB=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AquaMai.Mods/Fancy/SetFade.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AquaMai.Mods/Fancy/SetFade.cs b/AquaMai.Mods/Fancy/SetFade.cs index b50a86a7..8c3f53ab 100644 --- a/AquaMai.Mods/Fancy/SetFade.cs +++ b/AquaMai.Mods/Fancy/SetFade.cs @@ -176,10 +176,10 @@ private static void DriveKLDAnimation(GameObject monitor, CommonFadeEntry cfg) // 根据你的要求修正动画映射 string animName = cfg.FadeType switch { - 1 => "Out", + 1 => "In", 2 => "Out_02", 3 => "Out_03", - _ => "Out" + _ => "In" }; if (main != null) From b32ca44abaab3ab5834227538bdad12a9b25c179 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=97=E9=9B=B2=E3=80=80=E3=81=82=E3=81=A5=E3=81=8D?= Date: Tue, 6 Jan 2026 12:35:44 +0800 Subject: [PATCH 12/16] Update AquaMai.Mods/Fancy/SetFade.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 凌莞~(=^▽^=) --- AquaMai.Mods/Fancy/SetFade.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AquaMai.Mods/Fancy/SetFade.cs b/AquaMai.Mods/Fancy/SetFade.cs index 8c3f53ab..c82764e8 100644 --- a/AquaMai.Mods/Fancy/SetFade.cs +++ b/AquaMai.Mods/Fancy/SetFade.cs @@ -22,7 +22,7 @@ public class SetFade [ConfigEntry(name: "转场类型", zh: "0:Normal, 1:Plus, 2:Festa(仅限1.60+)")] public static readonly int FadeType = 0; - [ConfigEntry(name: "[仅限1.55+]启用特殊KLD转场,需要下载额外JSON文件", zh: "仅在配置过的歌曲启用KLD转场。1.50及以下版本无效。1.50我不想适配了如果有人想适配可以dd我)@力大砖飞")] + [ConfigEntry(name: "特殊 KLD 转场", zh: "仅在配置过的歌曲启用 KLD 转场。1.50 及以下版本无效,需要使用额外 JSON 文件 配置")] public static readonly bool isKLDEnabled = true; private static readonly string JSONDir = "LocalAssets"; From 58b9fe8dae66693129048573027aaa7a7fb00fef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=97=E9=9B=B2=E3=80=80=E3=81=82=E3=81=A5=E3=81=8D?= Date: Tue, 6 Jan 2026 12:37:04 +0800 Subject: [PATCH 13/16] Update AquaMai.Mods/Fancy/SetFade.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 凌莞~(=^▽^=) --- AquaMai.Mods/Fancy/SetFade.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AquaMai.Mods/Fancy/SetFade.cs b/AquaMai.Mods/Fancy/SetFade.cs index c82764e8..8d88a909 100644 --- a/AquaMai.Mods/Fancy/SetFade.cs +++ b/AquaMai.Mods/Fancy/SetFade.cs @@ -25,8 +25,8 @@ public class SetFade [ConfigEntry(name: "特殊 KLD 转场", zh: "仅在配置过的歌曲启用 KLD 转场。1.50 及以下版本无效,需要使用额外 JSON 文件 配置")] public static readonly bool isKLDEnabled = true; - private static readonly string JSONDir = "LocalAssets"; - private static readonly string JSONFileName = "CommonFadeList.json"; + [ConfigEntry("特殊转场配置文件")] + private static readonly string JSONFileName = @"LocalAssets\CommonFadeList.json"; private static bool isResourcePatchEnabled = false; private static bool _isInitialized = false; From 1985afbe736ac8be02792ba978479f564bede3e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=97=E9=9B=B2=E3=80=80=E3=81=82=E3=81=A5=E3=81=8D?= Date: Tue, 6 Jan 2026 15:42:08 +0800 Subject: [PATCH 14/16] =?UTF-8?q?[F]=20=E4=BF=AE=E6=94=B9JSON=E8=AF=BB?= =?UTF-8?q?=E5=8F=96=E9=80=BB=E8=BE=91=EF=BC=8C=E4=BD=BF=E7=94=A8Melonload?= =?UTF-8?q?er.TinyJSON=E3=80=82=E6=B7=BB=E5=8A=A0=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E4=B8=8D=E4=BF=AE=E6=94=B9=E8=BD=AC=E5=9C=BA=E7=9A=84=E9=80=89?= =?UTF-8?q?=E9=A1=B95=EF=BC=88=E5=85=B6=E5=AE=9E=E6=89=80=E6=9C=89?= =?UTF-8?q?=E6=97=A0=E6=95=88=E6=95=B0=E5=80=BC=E9=83=BD=E5=8F=AF=E4=BB=A5?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AquaMai.Mods/Fancy/SetFade.cs | 158 +++++++++++++++++++--------------- 1 file changed, 87 insertions(+), 71 deletions(-) diff --git a/AquaMai.Mods/Fancy/SetFade.cs b/AquaMai.Mods/Fancy/SetFade.cs index 8d88a909..e362508c 100644 --- a/AquaMai.Mods/Fancy/SetFade.cs +++ b/AquaMai.Mods/Fancy/SetFade.cs @@ -1,16 +1,16 @@ using HarmonyLib; using AquaMai.Config.Attributes; using AquaMai.Core.Helpers; -using Process; +using Process; using UnityEngine; using UnityEngine.UI; using System; using System.IO; using Manager; using MelonLoader; +using MelonLoader.TinyJSON; using System.Collections.Generic; -using System.Text.RegularExpressions; -using Monitor; +using Monitor; namespace AquaMai.Mods.Fancy; @@ -19,25 +19,32 @@ namespace AquaMai.Mods.Fancy; zh: "修改转场动画为其他变种")] public class SetFade { - [ConfigEntry(name: "转场类型", zh: "0:Normal, 1:Plus, 2:Festa(仅限1.60+)")] - public static readonly int FadeType = 0; + [ConfigEntry(name: "转场类型", zh: "0:Normal, 1:Plus, 2:Festa(仅限1.60+),5:禁用")] + public static readonly int FadeType = 5; - [ConfigEntry(name: "特殊 KLD 转场", zh: "仅在配置过的歌曲启用 KLD 转场。1.50 及以下版本无效,需要使用额外 JSON 文件 配置")] + [ConfigEntry(name: "[仅限1.55+]启用特殊KLD转场", zh: "仅在配置过的歌曲启用KLD转场。")] public static readonly bool isKLDEnabled = true; - [ConfigEntry("特殊转场配置文件")] - private static readonly string JSONFileName = @"LocalAssets\CommonFadeList.json"; + private static readonly string JSONDir = "LocalAssets"; + private static readonly string JSONFileName = "CommonFadeList.json"; private static bool isResourcePatchEnabled = false; private static bool _isInitialized = false; private static Sprite[] subBGs = new Sprite[3]; private static List cachedEntries = new List(); - - // 计数锁定逻辑变量 - private static int _kldRemainingCharges = 0; + + private static int _kldRemainingCharges = 0; private static CommonFadeEntry _activeKldConfig = null; - public class CommonFadeEntry { public int ID; public int isBlack; public int Type; public int FadeType; } + // --- TinyJson 数据模型 --- + // 注意:字段名必须与 JSON 中的 Key 完全一致,且必须为 public + public class CommonFadeEntry + { + public int ID; + public int isBlack; + public int Type; + public int FadeType; + } [HarmonyPrepare] public static bool Prepare() @@ -46,30 +53,69 @@ public static bool Prepare() subBGs[0] = Resources.Load("Process/ChangeScreen/Sprites/Sub_01"); subBGs[1] = Resources.Load("Process/ChangeScreen/Sprites/Sub_02"); subBGs[2] = (GameInfo.GameVersion >= 26000) ? Resources.Load("Process/ChangeScreen/Sprites/Sub_03") : subBGs[0]; + LoadJsonManual(); + _isInitialized = true; return true; } - // --- 1. 实时监听选曲:充能点 --- + // --- 1. 核心解析逻辑:使用 TinyJson --- +private static void LoadJsonManual() + { + try + { + string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, JSONDir, JSONFileName); + if (!File.Exists(path)) + { + MelonLogger.Warning($"[SetFade] 配置文件未找到: {path}"); + return; + } + + string jsonContent = File.ReadAllText(path); + cachedEntries.Clear(); + + // --- 修复 Line 80 --- + // 错误写法: var data = Process.TinyJson.FromJson>(jsonContent); + // 正确写法 (使用 MelonLoader.TinyJSON): + + var variant = JSON.Load(jsonContent); // 1. 先载入为 Variant 对象 + if (variant != null) + { + var data = variant.Make>(); // 2. 再转换为具体的 List + + if (data != null) + { + cachedEntries = data; + MelonLogger.Msg($"[SetFade] 成功通过 MelonLoader.TinyJSON 载入 {cachedEntries.Count} 条配置。"); + } + } + } + catch (Exception e) + { + MelonLogger.Error($"[SetFade] JSON 解析失败! 请检查格式。错误: {e.Message}"); + } + } + + // --- 2. 选曲监听与充能 --- [HarmonyPostfix] [HarmonyPatch(typeof(MusicSelectMonitor), "UpdateRivalScore")] [HarmonyPatch(typeof(MusicSelectMonitor), "SetRivalScore")] public static void OnMusicSelectionChanged(MusicSelectProcess ____musicSelect) { if (!isKLDEnabled || ____musicSelect == null) return; - - try { + try + { var musicData = ____musicSelect.GetMusic(0)?.MusicData; if (musicData != null) { var matched = cachedEntries.Find(e => e.ID == musicData.name.id); if (matched != null) { - if (_activeKldConfig != matched) + if (_activeKldConfig != matched) { _activeKldConfig = matched; - _kldRemainingCharges = 3; // 锁定 3 次机会 + _kldRemainingCharges = 3; MelonLogger.Msg($"[SetFade] 目标锁定:ID {matched.ID},KLD 已充能 (3次)"); } } @@ -79,17 +125,17 @@ public static void OnMusicSelectionChanged(MusicSelectProcess ____musicSelect) _kldRemainingCharges = 0; } } - } catch { } + } + catch { } } - // --- 2. 资源拦截触发 --- + // --- 3. 资源拦截与重定向 --- [HarmonyPrefix] [HarmonyPatch(typeof(FadeProcess), "OnStart")] [HarmonyPatch(typeof(AdvertiseProcess), "InitFade")] [HarmonyPatch(typeof(NextTrackProcess), "OnStart")] public static void StartFadePrefix() { - // 只有在有充能次数且配置存在时才开启拦截 isResourcePatchEnabled = (_kldRemainingCharges > 0 && _activeKldConfig != null); } @@ -97,7 +143,6 @@ public static void StartFadePrefix() [HarmonyPatch(typeof(Resources), "Load", new[] { typeof(string), typeof(global::System.Type) })] public static bool ResourcesLoadPrefix(ref string path, global::System.Type systemTypeInstance, ref UnityEngine.Object __result) { - // 如果 KLD 拦截未激活,则执行普通重定向(力大砖飞) if (!isResourcePatchEnabled) { if (FadeType >= 0 && FadeType <= 2) @@ -113,7 +158,6 @@ public static bool ResourcesLoadPrefix(ref string path, global::System.Type syst return true; } - // KLD 资源拦截 if (path.StartsWith("Process/ChangeScreen/Prefabs/ChangeScreen_0")) { __result = Resources.Load("Process/Kaleidxscope/Prefab/UI_KLD_ChangeScreen", systemTypeInstance); @@ -127,7 +171,7 @@ public static bool ResourcesLoadPrefix(ref string path, global::System.Type syst return true; } - // --- 3. 后置处理:消耗次数与动画播放 --- + // --- 4. 动画驱动 --- [HarmonyPostfix] [HarmonyPatch(typeof(FadeProcess), "OnStart")] [HarmonyPatch(typeof(AdvertiseProcess), "InitFade")] @@ -136,35 +180,31 @@ public static void GlobalPostfix(GameObject[] ___fadeObject) { if (isResourcePatchEnabled && _activeKldConfig != null) { - _kldRemainingCharges--; // 消耗一次 - MelonLogger.Msg($"[SetFade] 触发 KLD 成功,剩余次数: {_kldRemainingCharges}"); - + _kldRemainingCharges--; if (___fadeObject != null) { - foreach (var monitor in ___fadeObject) + foreach (var monitor in ___fadeObject) DriveKLDAnimation(monitor, _activeKldConfig); } } else if (___fadeObject != null) { - // 普通重定向模式下的 SubBG 替换 - foreach (var monitor in ___fadeObject) + foreach (var monitor in ___fadeObject) ReplaceSubBG(monitor); } - - isResourcePatchEnabled = false; // 关闭当次拦截锁 - - // 次数耗尽清理配置 + isResourcePatchEnabled = false; if (_kldRemainingCharges <= 0) _activeKldConfig = null; } private static void ReplaceSubBG(GameObject monitor) { if (FadeType < 0 || FadeType >= subBGs.Length) return; - try { + try + { var subBG = monitor.transform.Find("Canvas/Sub/Sub_ChangeScreen(Clone)/Sub_BG")?.GetComponent(); if (subBG != null) subBG.sprite = subBGs[FadeType]; - } catch { } + } + catch { } } private static void DriveKLDAnimation(GameObject monitor, CommonFadeEntry cfg) @@ -174,18 +214,19 @@ private static void DriveKLDAnimation(GameObject monitor, CommonFadeEntry cfg) var main = monitor.transform.Find("Canvas/Main/UI_KLD_ChangeScreen(Clone)"); var sub = monitor.transform.Find("Canvas/Sub/UI_KLD_Sub_ChangeScreen(Clone)"); - // 根据你的要求修正动画映射 - string animName = cfg.FadeType switch { - 1 => "In", - 2 => "Out_02", - 3 => "Out_03", - _ => "In" + string animName = cfg.FadeType switch + { + 1 => "In", + 2 => "Out_02", + 3 => "Out_03", + _ => "In" }; if (main != null) { var ctrl = main.GetComponent(); - if (ctrl != null) { + if (ctrl != null) + { ctrl.SetBackGroundType(cfg.isBlack != 0 ? KaleidxScopeFadeController.BackGroundType.Black : KaleidxScopeFadeController.BackGroundType.Normal); ctrl.SetSpriteType((KaleidxScopeFadeController.SpriteType)cfg.Type); if (Enum.TryParse(animName, out var state)) @@ -195,39 +236,14 @@ private static void DriveKLDAnimation(GameObject monitor, CommonFadeEntry cfg) if (sub != null) { var sCtrl = sub.GetComponent(); - if (sCtrl != null) { + if (sCtrl != null) + { sCtrl.SetBackGroundType(cfg.isBlack != 0 ? KaleidxScopeSubFadeController.BackGroundType.Black : KaleidxScopeSubFadeController.BackGroundType.Normal); sCtrl.SetSpriteType((KaleidxScopeSubFadeController.SpriteType)cfg.Type); sCtrl.PlayAnimation(KaleidxScopeSubFadeController.AnimState.In); } } - } catch { } - } - - private static void LoadJsonManual() - { - try { - string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, JSONDir, JSONFileName); - if (!File.Exists(path)) return; - string content = File.ReadAllText(path); - cachedEntries.Clear(); - var matches = Regex.Matches(content, @"\{[^{}]+\}"); - foreach (Match m in matches) { - string raw = m.Value; - var e = new CommonFadeEntry { - ID = ExtractInt(raw, "ID"), - isBlack = ExtractInt(raw, "isBlack"), - Type = ExtractInt(raw, "Type"), - FadeType = ExtractInt(raw, "FadeType") - }; - if (e.ID > 0) cachedEntries.Add(e); - } - MelonLogger.Msg($"[SetFade] 共载入 {cachedEntries.Count} 条 KLD 特殊配置。"); - } catch (Exception e) { MelonLogger.Error($"[SetFade] JSON加载出错: {e.Message}"); } - } - - private static int ExtractInt(string text, string key) { - var m = Regex.Match(text, $"\"{key}\"\\s*:\\s*\"?(\\d+)\"?"); - return (m.Success && int.TryParse(m.Groups[1].Value, out int res)) ? res : 0; + } + catch { } } } From 055469f6dd58a67379cbbe129141946f501f962e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=97=E9=9B=B2=E3=80=80=E3=81=82=E3=81=A5=E3=81=8D?= Date: Thu, 8 Jan 2026 11:30:24 +0800 Subject: [PATCH 15/16] =?UTF-8?q?[+,F]=20=E8=BF=BD=E5=8A=A0SetTrackStart?= =?UTF-8?q?=E6=A8=A1=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AquaMai.Mods/Fancy/SetFade.cs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/AquaMai.Mods/Fancy/SetFade.cs b/AquaMai.Mods/Fancy/SetFade.cs index e362508c..27c91af3 100644 --- a/AquaMai.Mods/Fancy/SetFade.cs +++ b/AquaMai.Mods/Fancy/SetFade.cs @@ -1,7 +1,7 @@ using HarmonyLib; using AquaMai.Config.Attributes; using AquaMai.Core.Helpers; -using Process; +using Process; using UnityEngine; using UnityEngine.UI; using System; @@ -11,6 +11,7 @@ using MelonLoader.TinyJSON; using System.Collections.Generic; using Monitor; +using Mono.Posix; namespace AquaMai.Mods.Fancy; @@ -24,7 +25,6 @@ public class SetFade [ConfigEntry(name: "[仅限1.55+]启用特殊KLD转场", zh: "仅在配置过的歌曲启用KLD转场。")] public static readonly bool isKLDEnabled = true; - private static readonly string JSONDir = "LocalAssets"; private static readonly string JSONFileName = "CommonFadeList.json"; @@ -34,7 +34,7 @@ public class SetFade private static List cachedEntries = new List(); private static int _kldRemainingCharges = 0; - private static CommonFadeEntry _activeKldConfig = null; + internal static CommonFadeEntry _activeKldConfig = null; // --- TinyJson 数据模型 --- // 注意:字段名必须与 JSON 中的 Key 完全一致,且必须为 public @@ -53,15 +53,15 @@ public static bool Prepare() subBGs[0] = Resources.Load("Process/ChangeScreen/Sprites/Sub_01"); subBGs[1] = Resources.Load("Process/ChangeScreen/Sprites/Sub_02"); subBGs[2] = (GameInfo.GameVersion >= 26000) ? Resources.Load("Process/ChangeScreen/Sprites/Sub_03") : subBGs[0]; - + LoadJsonManual(); - + _isInitialized = true; return true; } // --- 1. 核心解析逻辑:使用 TinyJson --- -private static void LoadJsonManual() + private static void LoadJsonManual() { try { @@ -78,12 +78,12 @@ private static void LoadJsonManual() // --- 修复 Line 80 --- // 错误写法: var data = Process.TinyJson.FromJson>(jsonContent); // 正确写法 (使用 MelonLoader.TinyJSON): - + var variant = JSON.Load(jsonContent); // 1. 先载入为 Variant 对象 if (variant != null) { var data = variant.Make>(); // 2. 再转换为具体的 List - + if (data != null) { cachedEntries = data; @@ -228,7 +228,8 @@ private static void DriveKLDAnimation(GameObject monitor, CommonFadeEntry cfg) if (ctrl != null) { ctrl.SetBackGroundType(cfg.isBlack != 0 ? KaleidxScopeFadeController.BackGroundType.Black : KaleidxScopeFadeController.BackGroundType.Normal); - ctrl.SetSpriteType((KaleidxScopeFadeController.SpriteType)cfg.Type); + if (cfg.Type == 10) ctrl.SetSpriteType((KaleidxScopeFadeController.SpriteType)7); + else ctrl.SetSpriteType((KaleidxScopeFadeController.SpriteType)cfg.Type); if (Enum.TryParse(animName, out var state)) ctrl.PlayAnimation(state); } @@ -239,7 +240,8 @@ private static void DriveKLDAnimation(GameObject monitor, CommonFadeEntry cfg) if (sCtrl != null) { sCtrl.SetBackGroundType(cfg.isBlack != 0 ? KaleidxScopeSubFadeController.BackGroundType.Black : KaleidxScopeSubFadeController.BackGroundType.Normal); - sCtrl.SetSpriteType((KaleidxScopeSubFadeController.SpriteType)cfg.Type); + if (cfg.Type == 10) sCtrl.SetSpriteType((KaleidxScopeSubFadeController.SpriteType)7); + else sCtrl.SetSpriteType((KaleidxScopeSubFadeController.SpriteType)cfg.Type); sCtrl.PlayAnimation(KaleidxScopeSubFadeController.AnimState.In); } } From 1f43eac42548c4504d175a09703f7a99772cdfab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=97=E9=9B=B2=E3=80=80=E3=81=82=E3=81=A5=E3=81=8D?= Date: Thu, 8 Jan 2026 11:30:49 +0800 Subject: [PATCH 16/16] =?UTF-8?q?[+,F]=20=E8=BF=BD=E5=8A=A0SetTrackStart?= =?UTF-8?q?=E6=A8=A1=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AquaMai.Mods/Fancy/SetTrackStart.cs | 145 ++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 AquaMai.Mods/Fancy/SetTrackStart.cs diff --git a/AquaMai.Mods/Fancy/SetTrackStart.cs b/AquaMai.Mods/Fancy/SetTrackStart.cs new file mode 100644 index 00000000..da9ca636 --- /dev/null +++ b/AquaMai.Mods/Fancy/SetTrackStart.cs @@ -0,0 +1,145 @@ +using HarmonyLib; +using AquaMai.Config.Attributes; +using Process; +using Manager; +using MAI2.Util; +using Monitor; +using System.Reflection; +using UnityEngine; +using Mai2.Mai2Cue; +using MelonLoader; + +namespace AquaMai.Mods.Fancy; + +[ConfigSection(name: "开场动画PLUS(需配合转场动画使用)", + en: "Set Track Start Animation", + zh: "同步修改开场动画为其他变种")] +public class SetTrackStart +{ + private static SetFade.CommonFadeEntry GetActiveConfig() => + typeof(SetFade).GetField("_activeKldConfig", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as SetFade.CommonFadeEntry; + + private static bool _isModActive = false; + private static float _timer = 0f; + private const float MAX_ANIM_TIME = 4.5f; + + // --- 修改:仅禁用 Active,不销毁对象,防止动画错位 --- + private static void DisableSpecificUI(TrackStartMonitor monitor) + { + if (monitor == null) return; + + // 向上找到当前屏幕对应的根节点 TrackStartProcess(Clone) + Transform root = monitor.transform; + while (root.parent != null && !root.name.Contains("TrackStartProcess")) + root = root.parent; + + // 目标 1: LifeGuage + Transform lifeGuage = root.Find("Canvas/Main/Null_UI_Kaleid_TS/UI_Kaleid_TS/UI/LifeGuage"); + if (lifeGuage != null) + { + lifeGuage.gameObject.SetActive(false); + MelonLogger.Msg("[V52] 已禁用 LifeGuage (SetActive: false)"); + } + + // 目标 2: TrackStart_head + Transform head = root.Find("Canvas/Main/Null_UI_Kaleid_TS/UI_Kaleid_TS/UI/TrackStart_head"); + if (head != null) + { + head.gameObject.SetActive(false); + MelonLogger.Msg("[V52] 已禁用 TrackStart_head (SetActive: false)"); + } + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(TrackStartMonitor), "SetTrackStart", new[] { typeof(TrackStartMonitor.TrackStartType) })] + public static bool SetTrackStartPrefix(TrackStartMonitor __instance, TrackStartMonitor.TrackStartType type) + { + if (GetActiveConfig() == null) return true; + + if (type == TrackStartMonitor.TrackStartType.Normal || type == TrackStartMonitor.TrackStartType.Versus) + { + var ctrlField = typeof(TrackStartMonitor).GetField("kaleidxScopeTrackStartController", BindingFlags.NonPublic | BindingFlags.Instance); + var ctrl = ctrlField?.GetValue(__instance) as KaleidxScopeTrackStartController; + + if (ctrl != null) + { + _isModActive = true; + _timer = 0f; + + // 初始隔离原版 UI + string[] targetNames = { "_normalObject", "_versusObject", "_objNormal", "_objVersus", "_backGround" }; + foreach (var name in targetNames) + { + var f = typeof(TrackStartMonitor).GetField(name, BindingFlags.NonPublic | BindingFlags.Instance); + (f?.GetValue(__instance) as GameObject)?.SetActive(false); + } + + (typeof(TrackStartMonitor).GetField("_kaleidxScopeBackground", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(__instance) as GameObject)?.SetActive(true); + ctrl.gameObject.SetActive(true); + + int gateId = 1; + var config = GetActiveConfig(); + var gField = config.GetType().GetField("Type", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + if (gField != null) gateId = System.Convert.ToInt32(gField.GetValue(config)); + + ctrl.PlayAnimation((KaleidxScopeTrackStartController.AnimState)(gateId % 11)); + ctrl.SetTrackNum(GameManager.MusicTrackNumber); + + // 内部数值依然设为0 + ctrl.SetLife(0); + + // --- 修改:调用禁用函数而非删除函数 --- + DisableSpecificUI(__instance); + + SoundManager.PlaySE(Cue.SE_TRACK_START_KALEID, __instance.MonitorIndex); + return false; + } + } + return true; + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(TrackStartMonitor), "IsEnd")] + public static bool IsEndPrefix(TrackStartMonitor __instance, ref bool __result) + { + if (!_isModActive) return true; + _timer += Time.deltaTime; + + var ctrlField = typeof(TrackStartMonitor).GetField("kaleidxScopeTrackStartController", BindingFlags.NonPublic | BindingFlags.Instance); + var ctrl = ctrlField?.GetValue(__instance) as KaleidxScopeTrackStartController; + + if (ctrl != null) + { + if (ctrl.PlayEnded() || _timer >= MAX_ANIM_TIME) + { + __result = true; + } + else + { + __result = false; + } + return false; + } + return true; + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(TrackStartProcess), "OnUpdate")] + public static bool ProcessUpdatePrefix(TrackStartProcess __instance) + { + if (!_isModActive) return true; + + var stateField = typeof(TrackStartProcess).GetField("_state", BindingFlags.NonPublic | BindingFlags.Instance); + var currentState = (TrackStartProcess.TrackStartSequence)stateField.GetValue(__instance); + + if (currentState == TrackStartProcess.TrackStartSequence.DispEnd) + { + // 这里保持逻辑,动画结束后会自然销毁或释放进程 + } + return true; + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(TrackStartProcess), "OnRelease")] + public static void OnReleasePostfix() => _isModActive = false; +}