diff --git a/AquaMai/AquaMai.toml b/AquaMai/AquaMai.toml index 5bafe809..736c1c8d 100644 --- a/AquaMai/AquaMai.toml +++ b/AquaMai/AquaMai.toml @@ -53,6 +53,12 @@ HideSelfMadeCharts=true # Place font.ttf in the LocalAssets directory to replace the game's global font # Cannot be used together with FontFix CustomFont=false +# Provide the ability to use custom note skins (advanced feature) +CustomNoteSkin=false +# Delayed the animation of the song start screen +# Hide "TRACK X" text and DX/Standard chart display box +# For recording chart confirmation +TrackStartProcessTweak=false [Fix] # Allow login with higher data version @@ -70,6 +76,9 @@ FrameRateLock=false # Use Microsoft YaHei Bold to display characters not in the font library # Cannot be used together with CustomFont FontFix=true +# Make the Critical judgment of BreakSlide flash like BreakTap +# Align the judgment display of arc-shaped Slide with the judgment line accurately (it was slightly off before) +SlideJudgeTweak=true [Utils] # Log user ID on login diff --git a/AquaMai/AquaMai.zh.toml b/AquaMai/AquaMai.zh.toml index bbbdb668..77977f97 100644 --- a/AquaMai/AquaMai.zh.toml +++ b/AquaMai/AquaMai.zh.toml @@ -62,6 +62,12 @@ HideSelfMadeCharts=true # 在 LocalAssets 目录下放置 font.ttf 可以替换游戏的全局字体 # 不可以和 FontFix 一起使用 CustomFont=false +# 提供自定义音符皮肤的能力(高级功能) +CustomNoteSkin=false +# 推迟了歌曲开始界面的动画 +# 隐藏“TRACK X”字样和 DX/标准谱面的显示框 +# 录制谱面确认用 +TrackStartProcessTweak=false # =================================== # 修复一些潜在的问题 @@ -86,6 +92,9 @@ FrameRateLock=false # 在显示字库里没有的字时使用微软雅黑 Bold 显示 # 不可以和 CustomFont 一起使用 FontFix=true +# 让 BreakSlide 的 Critical 判定也可以像 BreakTap 一样闪烁 +# 让圆弧形的 Slide 的判定显示与判定线精确对齐 (原本会有一点歪) +SlideJudgeTweak=true [Utils] # 登录时将 UserID 输出到日志 diff --git a/AquaMai/UX/CustomNoteSkin.cs b/AquaMai/UX/CustomNoteSkin.cs index 603077d5..a9d299ee 100644 --- a/AquaMai/UX/CustomNoteSkin.cs +++ b/AquaMai/UX/CustomNoteSkin.cs @@ -12,7 +12,7 @@ namespace AquaMai.UX; public class CustomNoteSkin { - private static readonly List ImageExts = [".jpg", ".png", ".jpeg"]; + private static readonly List ImageExts = [".png", ".jpg", ".jpeg"]; private static readonly List SlideFanFields = ["_normalSlideFan", "_eachSlideFan", "_breakSlideFan", "_breakSlideFanEff"]; private static Sprite customOutline; @@ -37,6 +37,7 @@ public class CustomNoteSkin MelonLogger.Msg($"[CustomNoteSkin] Field {fieldName} is a {fieldType.Name}, not a Sprite"); return false; } + var target = fieldTraverse.GetValue(); var pivot = new Vector2(target.pivot.x / target.rect.width, target.pivot.y / target.rect.height); var custom = Sprite.Create( @@ -53,6 +54,7 @@ public class CustomNoteSkin MelonLogger.Msg($"[CustomNoteSkin] Field {fieldName} is a {fieldType.Name}, not a Sprite[]"); return false; } + var targetArray = fieldTraverse.GetValue(); var target = targetArray[idx1.Value]; var pivot = new Vector2(target.pivot.x / target.rect.width, target.pivot.y / target.rect.height); @@ -70,6 +72,7 @@ public class CustomNoteSkin MelonLogger.Msg($"[CustomNoteSkin] Field {fieldName} is a {fieldType.Name}, not a Sprite[,]"); return false; } + var targetArray = fieldTraverse.GetValue(); var target = targetArray[idx1.Value, idx2.Value]; var pivot = new Vector2(target.pivot.x / target.rect.width, target.pivot.y / target.rect.height); @@ -87,9 +90,9 @@ public class CustomNoteSkin [HarmonyPatch(typeof(GameNotePrefabContainer), "Initialize")] private static void LoadNoteSkin() { - if (!Directory.Exists(Path.Combine(Environment.CurrentDirectory, "Skins"))) return; + if (!Directory.Exists(Path.Combine(Environment.CurrentDirectory, "LocalAssets", "Skins"))) return; - foreach (var laFile in Directory.EnumerateFiles(Path.Combine(Environment.CurrentDirectory, "Skins"))) + foreach (var laFile in Directory.EnumerateFiles(Path.Combine(Environment.CurrentDirectory, "LocalAssets", "Skins"))) { if (!ImageExts.Contains(Path.GetExtension(laFile).ToLowerInvariant())) continue; var texture = new Texture2D(1, 1, TextureFormat.RGBA32, false); @@ -100,8 +103,8 @@ public class CustomNoteSkin // 文件名的格式是 XXXXXXXX_A_B 表示 GameNoteImageContainer._XXXXXXXX[A, B] // 视具体情况, A, B 可能不存在 var fieldName = '_' + args[0]; - int? idx1 = (args.Length < 2)? null : (int.TryParse(args[1], out var temp) ? temp : null); - int? idx2 = (args.Length < 3)? null : (int.TryParse(args[2], out temp) ? temp : null); + int? idx1 = (args.Length < 2) ? null : (int.TryParse(args[1], out var temp) ? temp : null); + int? idx2 = (args.Length < 3) ? null : (int.TryParse(args[2], out temp) ? temp : null); Traverse traverse; @@ -119,6 +122,7 @@ public class CustomNoteSkin MelonLogger.Msg($"[CustomNoteSkin] Field {fieldName} needs a index"); continue; } + var i = SlideFanFields.IndexOf(fieldName); customSlideFan[i, idx1.Value] = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(1f, 0.5f), 1f); MelonLogger.Msg($"[CustomNoteSkin] Successfully loaded {name}"); @@ -154,6 +158,7 @@ public class CustomNoteSkin MelonLogger.Msg($"[CustomNoteSkin] Field {fieldName} needs a index"); continue; } + traverse = Traverse.Create(GameNotePrefabContainer.TouchHoldC); var target = traverse.Field("ColorsObject").Value; var renderer = target[idx1.Value]; @@ -177,6 +182,7 @@ public class CustomNoteSkin MelonLogger.Msg($"[CustomNoteSkin] Field {fieldName} needs a index"); continue; } + traverse = Traverse.Create(GameNotePrefabContainer.TouchReserve); var target = traverse.Field("_reserveSingleSprite").Value; var targetSprite = target[idx1.Value - 2]; @@ -199,6 +205,7 @@ public class CustomNoteSkin MelonLogger.Msg($"[CustomNoteSkin] Field {fieldName} needs a index"); continue; } + traverse = Traverse.Create(GameNotePrefabContainer.TouchReserve); var target = traverse.Field("_reserveEachSprite").Value; var targetSprite = target[idx1.Value - 2]; @@ -256,6 +263,7 @@ public class CustomNoteSkin ____spriteLines[2 * i + 1].transform.localPosition = new Vector3(0, position.y, position.z); ____spriteLines[2 * i + 1].color = Color.white; } + sprite = customSlideFan[3, i]; if (sprite != null) {