mirror of
https://github.com/MewoLab/AquaDX.git
synced 2026-02-10 06:27:26 +08:00
[RF] Move some patches to visual
This commit is contained in:
24
AquaMai/Visual/BreakSlideJudgeBlink.cs
Normal file
24
AquaMai/Visual/BreakSlideJudgeBlink.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using HarmonyLib;
|
||||
using Monitor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AquaMai.Visual;
|
||||
|
||||
public class BreakSlideJudgeBlink
|
||||
{
|
||||
/*
|
||||
* 这个 Patch 让 BreakSlide 的 Critical 判定也可以像 BreakTap 一样闪烁
|
||||
* 推荐与自定义皮肤一起使用 (否则视觉效果可能并不好)
|
||||
*/
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(SlideJudge), "UpdateBreakEffectAdd")]
|
||||
private static void FixBreakSlideJudgeBlink(
|
||||
SpriteRenderer ___SpriteRenderAdd, SpriteRenderer ___SpriteRender,
|
||||
SlideJudge.SlideJudgeType ____judgeType, SlideJudge.SlideAngle ____angle
|
||||
)
|
||||
{
|
||||
if (!___SpriteRenderAdd.gameObject.activeSelf) return;
|
||||
float num = ___SpriteRenderAdd.color.r;
|
||||
___SpriteRenderAdd.color = new Color(num, num, num, 1f);
|
||||
}
|
||||
}
|
||||
87
AquaMai/Visual/Config.cs
Normal file
87
AquaMai/Visual/Config.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using AquaMai.Attributes;
|
||||
|
||||
namespace AquaMai.Visual;
|
||||
|
||||
public class Config
|
||||
{
|
||||
[ConfigComment(
|
||||
en: """
|
||||
Provide the ability to use custom skins (advanced feature)
|
||||
Load skin textures from LocalAssets\Skins
|
||||
""",
|
||||
zh: """
|
||||
提供自定义皮肤的能力(高级功能)
|
||||
从 LocalAssets\Skins 中加载皮肤贴图
|
||||
""")]
|
||||
public bool CustomSkins { get; set; }
|
||||
|
||||
[ConfigComment(
|
||||
en: """
|
||||
More detailed judgment display
|
||||
Requires CustomSkins to be enabled and the resource file to be downloaded
|
||||
https://github.com/hykilpikonna/AquaDX/releases/download/nightly/JudgeDisplay4B.7z
|
||||
""",
|
||||
zh: """
|
||||
更精细的判定表示
|
||||
需开启 CustomSkins 并下载资源文件
|
||||
https://github.com/hykilpikonna/AquaDX/releases/download/nightly/JudgeDisplay4B.7z
|
||||
""")]
|
||||
public bool JudgeDisplay4B { get; set; }
|
||||
|
||||
[ConfigComment(
|
||||
en: """
|
||||
Custom track start difficulty image (not really custom difficulty)
|
||||
Requires CustomSkins to be enabled
|
||||
Will load four image resources through custom skins: musicBase, musicTab, musicLvBase, musicLvText
|
||||
""",
|
||||
zh: """
|
||||
自定义在歌曲开始界面上显示的难度贴图 (并不是真的自定义难度)
|
||||
需要启用自定义皮肤功能
|
||||
会通过自定义皮肤加载四个图片资源: musicBase, musicTab, musicLvBase, musicLvText
|
||||
""")]
|
||||
public bool CustomTrackStartDiff { get; set; }
|
||||
|
||||
[ConfigComment(
|
||||
en: """
|
||||
Delayed the animation of the song start screen
|
||||
For recording chart confirmation
|
||||
""",
|
||||
zh: """
|
||||
推迟了歌曲开始界面的动画
|
||||
录制谱面确认用
|
||||
""")]
|
||||
public bool TrackStartProcessTweak { get; set; }
|
||||
|
||||
[ConfigComment(
|
||||
en: """
|
||||
Disable the TRACK X text, DX/Standard display box, and the derakkuma at the bottom of the screen in the song start screen
|
||||
For recording chart confirmation
|
||||
""",
|
||||
zh: """
|
||||
在歌曲开始界面, 把 TRACK X 字样, DX/标准谱面的显示框, 以及画面下方的滴蜡熊隐藏掉
|
||||
录制谱面确认用
|
||||
""")]
|
||||
public bool DisableTrackStartTabs { get; set; }
|
||||
|
||||
[ConfigComment(
|
||||
en: """
|
||||
Make the judgment display of Wifi Slide different in up and down (originally all Wifi judgment displays are towards the center), just like in majdata
|
||||
The reason for this bug is that SEGA forgot to assign EndButtonId to Wifi
|
||||
""",
|
||||
zh: """
|
||||
这个 Patch 让 Wifi Slide 的判定显示有上下的区别 (原本所有 Wifi 的判定显示都是朝向圆心的), 就像 majdata 里那样
|
||||
这个 bug 产生的原因是 SBGA 忘记给 Wifi 的 EndButtonId 赋值了
|
||||
""")]
|
||||
public bool FanJudgeFlip { get; set; }
|
||||
|
||||
[ConfigComment(
|
||||
en: """
|
||||
This Patch makes the Critical judgment of BreakSlide also flicker like BreakTap
|
||||
Recommended to use with custom skins (otherwise the visual effect may not be good)
|
||||
""",
|
||||
zh: """
|
||||
这个 Patch 让 BreakSlide 的 Critical 判定也可以像 BreakTap 一样闪烁
|
||||
推荐与自定义皮肤一起使用 (否则视觉效果可能并不好)
|
||||
""")]
|
||||
public bool BreakSlideJudgeBlink { get; set; }
|
||||
}
|
||||
59
AquaMai/Visual/CustomLogo.cs
Normal file
59
AquaMai/Visual/CustomLogo.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using HarmonyLib;
|
||||
using Monitor;
|
||||
using Process;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace AquaMai.Visual;
|
||||
|
||||
public class CustomLogo
|
||||
{
|
||||
private static List<Sprite> segaLogo = new();
|
||||
private static List<Sprite> allNetLogo = new();
|
||||
|
||||
public static void DoCustomPatch(HarmonyLib.Harmony h)
|
||||
{
|
||||
EnumSprite(segaLogo, Path.Combine(Environment.CurrentDirectory, "LocalAssets", "SegaLogo"));
|
||||
EnumSprite(allNetLogo, Path.Combine(Environment.CurrentDirectory, "LocalAssets", "AllNetLogo"));
|
||||
}
|
||||
|
||||
private static void EnumSprite(List<Sprite> collection, string path)
|
||||
{
|
||||
if (!Directory.Exists(path)) return;
|
||||
foreach (var file in Directory.EnumerateFiles(path, "*.png"))
|
||||
{
|
||||
var data = File.ReadAllBytes(file);
|
||||
var texture2D = new Texture2D(1, 1, TextureFormat.RGBA32, false);
|
||||
if (texture2D.LoadImage(data))
|
||||
{
|
||||
collection.Add(Sprite.Create(texture2D, new Rect(0f, 0f, texture2D.width, texture2D.height), new Vector2(0.5f, 0.5f)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(AdvertiseProcess), "OnStart")]
|
||||
[HarmonyPostfix]
|
||||
private static void AdvProcessPostFix(AdvertiseMonitor[] ____monitors)
|
||||
{
|
||||
if (segaLogo.Count > 0)
|
||||
{
|
||||
var logo = segaLogo[UnityEngine.Random.Range(0, segaLogo.Count)];
|
||||
foreach (var monitor in ____monitors)
|
||||
{
|
||||
monitor.transform.Find("Canvas/Main/SegaAllNet_LOGO/NUL_ADT_SegaAllNet_LOGO/SegaLogo").GetComponent<Image>().sprite = logo;
|
||||
}
|
||||
}
|
||||
|
||||
if (allNetLogo.Count > 0)
|
||||
{
|
||||
var logo = allNetLogo[UnityEngine.Random.Range(0, allNetLogo.Count)];
|
||||
foreach (var monitor in ____monitors)
|
||||
{
|
||||
monitor.transform.Find("Canvas/Main/SegaAllNet_LOGO/NUL_ADT_SegaAllNet_LOGO/AllNetLogo").GetComponent<Image>().sprite = logo;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
388
AquaMai/Visual/CustomSkins.cs
Normal file
388
AquaMai/Visual/CustomSkins.cs
Normal file
@@ -0,0 +1,388 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using HarmonyLib;
|
||||
using MelonLoader;
|
||||
using Monitor;
|
||||
using Monitor.Game;
|
||||
using Process;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AquaMai.Visual;
|
||||
|
||||
public class CustomSkins
|
||||
{
|
||||
private static readonly List<string> ImageExts = [".png", ".jpg", ".jpeg"];
|
||||
private static readonly List<string> SlideFanFields = ["_normalSlideFan", "_eachSlideFan", "_breakSlideFan", "_breakSlideFanEff"];
|
||||
private static readonly List<string> CustomTrackStartFields = ["_musicBase", "_musicTab", "_musicLvBase", "_musicLvText"];
|
||||
|
||||
private static Sprite customOutline;
|
||||
private static Sprite[,] customSlideFan = new Sprite[4, 11];
|
||||
|
||||
public static readonly Sprite[,] CustomJudge = new Sprite[2, ((int)NoteJudge.ETiming.End + 1)];
|
||||
public static readonly Sprite[,,,] CustomJudgeSlide = new Sprite[2, 3, 2, ((int)NoteJudge.ETiming.End + 1)];
|
||||
public static readonly Texture2D[] CustomTrackStart = new Texture2D[4];
|
||||
|
||||
private static bool LoadIntoGameNoteImageContainer(string fieldName, int? idx1, int? idx2, Texture2D texture)
|
||||
{
|
||||
// 先确定确实有这个 Field, 如果没有的话可以直接跳过这个文件
|
||||
var fieldTraverse = Traverse.Create(typeof(GameNoteImageContainer)).Field(fieldName);
|
||||
if (!fieldTraverse.FieldExists())
|
||||
{
|
||||
MelonLogger.Msg($"[CustomNoteSkin] Cannot found field {fieldName}");
|
||||
return false;
|
||||
}
|
||||
|
||||
var fieldType = fieldTraverse.GetValueType();
|
||||
if (!idx1.HasValue)
|
||||
{
|
||||
// 目标 Field 应当是单个 Sprite
|
||||
if (fieldType != typeof(Sprite))
|
||||
{
|
||||
MelonLogger.Msg($"[CustomNoteSkin] Field {fieldName} is a {fieldType.Name}, not a Sprite");
|
||||
return false;
|
||||
}
|
||||
|
||||
var target = fieldTraverse.GetValue<Sprite>();
|
||||
var pivot = new Vector2(target.pivot.x / target.rect.width, target.pivot.y / target.rect.height);
|
||||
var custom = Sprite.Create(
|
||||
texture, new Rect(0, 0, texture.width, texture.height), pivot, 1f,
|
||||
0, SpriteMeshType.Tight, target.border
|
||||
);
|
||||
fieldTraverse.SetValue(custom);
|
||||
}
|
||||
else if (!idx2.HasValue)
|
||||
{
|
||||
// 目标 Field 是一维数组
|
||||
if (fieldType != typeof(Sprite[]))
|
||||
{
|
||||
MelonLogger.Msg($"[CustomNoteSkin] Field {fieldName} is a {fieldType.Name}, not a Sprite[]");
|
||||
return false;
|
||||
}
|
||||
|
||||
var targetArray = fieldTraverse.GetValue<Sprite[]>();
|
||||
var target = targetArray[idx1.Value];
|
||||
var pivot = new Vector2(target.pivot.x / target.rect.width, target.pivot.y / target.rect.height);
|
||||
var custom = Sprite.Create(
|
||||
texture, new Rect(0, 0, texture.width, texture.height), pivot, 1f,
|
||||
0, SpriteMeshType.Tight, target.border
|
||||
);
|
||||
targetArray[idx1.Value] = custom;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 目标 Field 是二维数组
|
||||
if (fieldType != typeof(Sprite[,]))
|
||||
{
|
||||
MelonLogger.Msg($"[CustomNoteSkin] Field {fieldName} is a {fieldType.Name}, not a Sprite[,]");
|
||||
return false;
|
||||
}
|
||||
|
||||
var targetArray = fieldTraverse.GetValue<Sprite[,]>();
|
||||
var target = targetArray[idx1.Value, idx2.Value];
|
||||
var pivot = new Vector2(target.pivot.x / target.rect.width, target.pivot.y / target.rect.height);
|
||||
var custom = Sprite.Create(
|
||||
texture, new Rect(0, 0, texture.width, texture.height), pivot, 1f,
|
||||
0, SpriteMeshType.Tight, target.border
|
||||
);
|
||||
targetArray[idx1.Value, idx2.Value] = custom;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(GameNotePrefabContainer), "Initialize")]
|
||||
private static void LoadNoteSkin()
|
||||
{
|
||||
if (!Directory.Exists(Path.Combine(Environment.CurrentDirectory, "LocalAssets", "Skins"))) return;
|
||||
|
||||
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);
|
||||
texture.LoadImage(File.ReadAllBytes(laFile));
|
||||
|
||||
var name = Path.GetFileNameWithoutExtension(laFile);
|
||||
var args = name.Split('_');
|
||||
// 文件名的格式是 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? idx3 = (args.Length < 4) ? null : (int.TryParse(args[3], out temp) ? temp : null);
|
||||
|
||||
Traverse traverse;
|
||||
|
||||
if (CustomTrackStartFields.Contains(fieldName))
|
||||
{
|
||||
var i = CustomTrackStartFields.IndexOf(fieldName);
|
||||
CustomTrackStart[i] = texture;
|
||||
MelonLogger.Msg($"[CustomNoteSkin] Successfully loaded {name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fieldName == "_outline")
|
||||
{
|
||||
customOutline = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f), 1f);
|
||||
MelonLogger.Msg($"[CustomNoteSkin] Successfully loaded {name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fieldName == "_judgeNormal" || fieldName == "_judgeBreak")
|
||||
{
|
||||
if (!idx1.HasValue)
|
||||
{
|
||||
MelonLogger.Msg($"[CustomNoteSkin] Field {fieldName} needs a index");
|
||||
continue;
|
||||
}
|
||||
|
||||
var i = (fieldName == "_judgeBreak") ? 1 : 0;
|
||||
CustomJudge[i, idx1.Value] = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f), 1f);
|
||||
MelonLogger.Msg($"[CustomNoteSkin] Successfully loaded {name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fieldName == "_judgeSlideNormal" || fieldName == "_judgeSlideBreak")
|
||||
{
|
||||
if (!idx1.HasValue || !idx2.HasValue || !idx3.HasValue)
|
||||
{
|
||||
MelonLogger.Msg($"[CustomNoteSkin] Field {fieldName} needs 3 indices");
|
||||
continue;
|
||||
}
|
||||
|
||||
var i = (fieldName == "_judgeSlideBreak") ? 1 : 0;
|
||||
Vector2 pivot;
|
||||
switch (idx1.Value)
|
||||
{
|
||||
case 0 when idx2.Value == 0:
|
||||
pivot = new Vector2(0f, 0.5f);
|
||||
break;
|
||||
case 0 when idx2.Value == 1:
|
||||
pivot = new Vector2(1f, 0.5f);
|
||||
break;
|
||||
case 1 when idx2.Value == 0:
|
||||
pivot = new Vector2(0f, 0.3f);
|
||||
break;
|
||||
case 1 when idx2.Value == 1:
|
||||
pivot = new Vector2(1f, 0.3f);
|
||||
break;
|
||||
case 2 when idx2.Value == 0:
|
||||
pivot = new Vector2(0.5f, 0.8f);
|
||||
break;
|
||||
case 2 when idx2.Value == 1:
|
||||
pivot = new Vector2(0.5f, 0.2f);
|
||||
break;
|
||||
default:
|
||||
pivot = new Vector2(0.5f, 0.5f);
|
||||
break;
|
||||
}
|
||||
CustomJudgeSlide[i, idx1.Value, idx2.Value, idx3.Value] = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), pivot, 1f);
|
||||
MelonLogger.Msg($"[CustomNoteSkin] Successfully loaded {name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (SlideFanFields.Contains(fieldName))
|
||||
{
|
||||
if (!idx1.HasValue)
|
||||
{
|
||||
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}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fieldName == "_touchJust")
|
||||
{
|
||||
traverse = Traverse.Create(GameNotePrefabContainer.TouchTapB);
|
||||
var noticeObject = traverse.Field<GameObject>("NoticeObject").Value;
|
||||
var target = noticeObject.GetComponent<SpriteRenderer>();
|
||||
var pivot = new Vector2(
|
||||
target.sprite.pivot.x / target.sprite.rect.width,
|
||||
target.sprite.pivot.y / target.sprite.rect.height
|
||||
);
|
||||
var custom = Sprite.Create(
|
||||
texture, new Rect(0, 0, texture.width, texture.height), pivot, 1f,
|
||||
0, SpriteMeshType.Tight, target.sprite.border
|
||||
);
|
||||
target.sprite = custom;
|
||||
|
||||
traverse = Traverse.Create(GameNotePrefabContainer.TouchTapC);
|
||||
noticeObject = traverse.Field<GameObject>("NoticeObject").Value;
|
||||
noticeObject.GetComponent<SpriteRenderer>().sprite = custom;
|
||||
MelonLogger.Msg($"[CustomNoteSkin] Successfully loaded {name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fieldName == "_touchHold")
|
||||
{
|
||||
if (!idx1.HasValue)
|
||||
{
|
||||
MelonLogger.Msg($"[CustomNoteSkin] Field {fieldName} needs a index");
|
||||
continue;
|
||||
}
|
||||
|
||||
traverse = Traverse.Create(GameNotePrefabContainer.TouchHoldC);
|
||||
var target = traverse.Field<SpriteRenderer[]>("ColorsObject").Value;
|
||||
var renderer = target[idx1.Value];
|
||||
var pivot = new Vector2(
|
||||
renderer.sprite.pivot.x / renderer.sprite.rect.width,
|
||||
renderer.sprite.pivot.y / renderer.sprite.rect.height
|
||||
);
|
||||
var custom = Sprite.Create(
|
||||
texture, new Rect(0, 0, texture.width, texture.height), pivot, 1f,
|
||||
0, SpriteMeshType.Tight, renderer.sprite.border
|
||||
);
|
||||
renderer.sprite = custom;
|
||||
MelonLogger.Msg($"[CustomNoteSkin] Successfully loaded {name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fieldName == "_normalTouchBorder")
|
||||
{
|
||||
if (!idx1.HasValue)
|
||||
{
|
||||
MelonLogger.Msg($"[CustomNoteSkin] Field {fieldName} needs a index");
|
||||
continue;
|
||||
}
|
||||
|
||||
traverse = Traverse.Create(GameNotePrefabContainer.TouchReserve);
|
||||
var target = traverse.Field<Sprite[]>("_reserveSingleSprite").Value;
|
||||
var targetSprite = target[idx1.Value - 2];
|
||||
var pivot = new Vector2(
|
||||
targetSprite.pivot.x / targetSprite.rect.width,
|
||||
targetSprite.pivot.y / targetSprite.rect.height
|
||||
);
|
||||
target[idx1.Value - 2] = Sprite.Create(
|
||||
texture, new Rect(0, 0, texture.width, texture.height), pivot, 1f,
|
||||
0, SpriteMeshType.Tight, targetSprite.border
|
||||
);
|
||||
MelonLogger.Msg($"[CustomNoteSkin] Successfully loaded {name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fieldName == "_eachTouchBorder")
|
||||
{
|
||||
if (!idx1.HasValue)
|
||||
{
|
||||
MelonLogger.Msg($"[CustomNoteSkin] Field {fieldName} needs a index");
|
||||
continue;
|
||||
}
|
||||
|
||||
traverse = Traverse.Create(GameNotePrefabContainer.TouchReserve);
|
||||
var target = traverse.Field<Sprite[]>("_reserveEachSprite").Value;
|
||||
var targetSprite = target[idx1.Value - 2];
|
||||
var pivot = new Vector2(
|
||||
targetSprite.pivot.x / targetSprite.rect.width,
|
||||
targetSprite.pivot.y / targetSprite.rect.height
|
||||
);
|
||||
target[idx1.Value - 2] = Sprite.Create(
|
||||
texture, new Rect(0, 0, texture.width, texture.height), pivot, 1f,
|
||||
0, SpriteMeshType.Tight, targetSprite.border
|
||||
);
|
||||
MelonLogger.Msg($"[CustomNoteSkin] Successfully loaded {name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (LoadIntoGameNoteImageContainer(fieldName, idx1, idx2, texture))
|
||||
{
|
||||
MelonLogger.Msg($"[CustomNoteSkin] Successfully loaded {name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(GameCtrl), "Initialize")]
|
||||
private static void ChangeOutlineTexture(GameObject ____guideEndPointObj)
|
||||
{
|
||||
if (____guideEndPointObj != null && customOutline != null)
|
||||
{
|
||||
____guideEndPointObj.GetComponent<SpriteRenderer>().sprite = customOutline;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(SlideFan), "Initialize")]
|
||||
private static void ChangeFanTexture(
|
||||
SpriteRenderer[] ____spriteLines, SpriteRenderer[] ____effectSprites, bool ___BreakFlag, bool ___EachFlag
|
||||
)
|
||||
{
|
||||
Vector3 position;
|
||||
Sprite sprite;
|
||||
if (___BreakFlag)
|
||||
{
|
||||
for (var i = 0; i < 11; i++)
|
||||
{
|
||||
sprite = customSlideFan[2, i];
|
||||
if (sprite != null)
|
||||
{
|
||||
____spriteLines[2 * i].sprite = sprite;
|
||||
position = ____spriteLines[2 * i].transform.localPosition;
|
||||
____spriteLines[2 * i].transform.localPosition = new Vector3(0, position.y, position.z);
|
||||
____spriteLines[2 * i].color = Color.white;
|
||||
|
||||
____spriteLines[2 * i + 1].sprite = sprite;
|
||||
position = ____spriteLines[2 * i + 1].transform.localPosition;
|
||||
____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)
|
||||
{
|
||||
____effectSprites[2 * i].sprite = sprite;
|
||||
position = ____effectSprites[2 * i].transform.localPosition;
|
||||
____effectSprites[2 * i].transform.localPosition = new Vector3(0, position.y, position.z);
|
||||
____effectSprites[2 * i].color = Color.white;
|
||||
|
||||
____effectSprites[2 * i + 1].sprite = sprite;
|
||||
position = ____effectSprites[2 * i + 1].transform.localPosition;
|
||||
____effectSprites[2 * i + 1].transform.localPosition = new Vector3(0, position.y, position.z);
|
||||
____effectSprites[2 * i + 1].color = Color.white;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (___EachFlag)
|
||||
{
|
||||
for (var i = 0; i < 11; i++)
|
||||
{
|
||||
sprite = customSlideFan[1, i];
|
||||
if (sprite != null)
|
||||
{
|
||||
____spriteLines[2 * i].sprite = sprite;
|
||||
position = ____spriteLines[2 * i].transform.localPosition;
|
||||
____spriteLines[2 * i].transform.localPosition = new Vector3(0, position.y, position.z);
|
||||
____spriteLines[2 * i].color = Color.white;
|
||||
|
||||
____spriteLines[2 * i + 1].sprite = sprite;
|
||||
position = ____spriteLines[2 * i + 1].transform.localPosition;
|
||||
____spriteLines[2 * i + 1].transform.localPosition = new Vector3(0, position.y, position.z);
|
||||
____spriteLines[2 * i + 1].color = Color.white;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < 11; i++)
|
||||
{
|
||||
sprite = customSlideFan[0, i];
|
||||
if (sprite != null)
|
||||
{
|
||||
____spriteLines[2 * i].sprite = sprite;
|
||||
position = ____spriteLines[2 * i].transform.localPosition;
|
||||
____spriteLines[2 * i].transform.localPosition = new Vector3(0, position.y, position.z);
|
||||
____spriteLines[2 * i].color = Color.white;
|
||||
|
||||
____spriteLines[2 * i + 1].sprite = sprite;
|
||||
position = ____spriteLines[2 * i + 1].transform.localPosition;
|
||||
____spriteLines[2 * i + 1].transform.localPosition = new Vector3(0, position.y, position.z);
|
||||
____spriteLines[2 * i + 1].color = Color.white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
66
AquaMai/Visual/CustomTrackStartDiff.cs
Normal file
66
AquaMai/Visual/CustomTrackStartDiff.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using System.Collections.Generic;
|
||||
using HarmonyLib;
|
||||
using Monitor;
|
||||
using UI;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace AquaMai.Visual;
|
||||
|
||||
public class CustomTrackStartDiff
|
||||
{
|
||||
// 自定义在歌曲开始界面上显示的难度 (并不是真的自定义难度)
|
||||
// 需要启用自定义皮肤功能
|
||||
// 会加载四个图片资源: musicBase, musicTab, musicLvBase, musicLvText
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(TrackStartMonitor), "SetTrackStart")]
|
||||
private static void DisableTabs(
|
||||
MultipleImage ____musicBaseImage,
|
||||
MultipleImage ____musicTabImage,
|
||||
SpriteCounter ____difficultySingle,
|
||||
SpriteCounter ____difficultyDouble,
|
||||
Image ____levelTextImage,
|
||||
List<ResultMonitor.SpriteSheet> ____musicLevelSpriteSheets,
|
||||
TimelineRoot ____musicDetail
|
||||
)
|
||||
{
|
||||
var texture = CustomSkins.CustomTrackStart[0];
|
||||
if (texture != null)
|
||||
{
|
||||
____musicBaseImage.MultiSprites[6] = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f), 100f);
|
||||
____musicBaseImage.ChangeSprite(6);
|
||||
}
|
||||
|
||||
texture = CustomSkins.CustomTrackStart[1];
|
||||
if (texture != null)
|
||||
{
|
||||
____musicTabImage.MultiSprites[6] = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f), 100f);
|
||||
____musicTabImage.ChangeSprite(6);
|
||||
}
|
||||
|
||||
texture = CustomSkins.CustomTrackStart[2];
|
||||
if (texture != null)
|
||||
{
|
||||
var lvBase = Traverse.Create(____musicDetail).Field<MultipleImage>("_lv_Base").Value;
|
||||
lvBase.MultiSprites[6] = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f), 100f);
|
||||
lvBase.ChangeSprite(6);
|
||||
}
|
||||
|
||||
texture = CustomSkins.CustomTrackStart[3];
|
||||
if (texture != null)
|
||||
{
|
||||
var original = ____musicLevelSpriteSheets[0].Sheet;
|
||||
var sheet = new Sprite[original.Length];
|
||||
for (var i = 0; i < original.Length; i++)
|
||||
{
|
||||
var sprite = original[i];
|
||||
sheet[i] = Sprite.Create(texture, sprite.textureRect, new Vector2(0.5f, 0.5f), 100f);
|
||||
}
|
||||
|
||||
____difficultySingle.SetSpriteSheet(sheet);
|
||||
____difficultyDouble.SetSpriteSheet(sheet);
|
||||
____levelTextImage.sprite = sheet[14];
|
||||
}
|
||||
}
|
||||
}
|
||||
28
AquaMai/Visual/DisableTrackStartTabs.cs
Normal file
28
AquaMai/Visual/DisableTrackStartTabs.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using HarmonyLib;
|
||||
using Monitor;
|
||||
using UI;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AquaMai.Visual;
|
||||
|
||||
public class DisableTrackStartTabs
|
||||
{
|
||||
// 在歌曲开始界面, 把 TRACK X 字样, DX/标准谱面的显示框, 以及画面下方的滴蜡熊隐藏掉, 让他看起来不那么 sinmai, 更像是 majdata
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(TrackStartMonitor), "SetTrackStart")]
|
||||
private static void DisableTabs(
|
||||
SpriteCounter ____trackNumber, SpriteCounter ____bossTrackNumber, SpriteCounter ____utageTrackNumber,
|
||||
MultipleImage ____musicTabImage, GameObject[] ____musicTabObj, GameObject ____derakkumaRoot
|
||||
)
|
||||
{
|
||||
____trackNumber.transform.parent.gameObject.SetActive(false);
|
||||
____bossTrackNumber.transform.parent.gameObject.SetActive(false);
|
||||
____utageTrackNumber.transform.parent.gameObject.SetActive(false);
|
||||
____musicTabImage.gameObject.SetActive(false);
|
||||
____musicTabObj[0].gameObject.SetActive(false);
|
||||
____musicTabObj[1].gameObject.SetActive(false);
|
||||
____musicTabObj[2].gameObject.SetActive(false);
|
||||
____derakkumaRoot.SetActive(false);
|
||||
}
|
||||
}
|
||||
32
AquaMai/Visual/FanJudgeFlip.cs
Normal file
32
AquaMai/Visual/FanJudgeFlip.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using HarmonyLib;
|
||||
using Monitor;
|
||||
|
||||
namespace AquaMai.Visual;
|
||||
|
||||
public class FanJudgeFlip
|
||||
{
|
||||
/*
|
||||
* 这个 Patch 让 Wifi Slide 的判定显示有上下的区别 (原本所有 Wifi 的判定显示都是朝向圆心的), 就像 majdata 里那样
|
||||
* 这个 bug 产生的原因是 SBGA 忘记给 Wifi 的 EndButtonId 赋值了
|
||||
* 不过需要注意的是, 考虑到圆弧形 Slide 的判定显示就是永远朝向圆心的, 我个人会觉得这个 Patch 关掉更好看一点
|
||||
*/
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(SlideFan), "Initialize")]
|
||||
private static void FixFanJudgeFilp(
|
||||
int[] ___GoalButtonId, SlideJudge ___JudgeObj
|
||||
)
|
||||
{
|
||||
if (null != ___JudgeObj)
|
||||
{
|
||||
if (2 <= ___GoalButtonId[1] && ___GoalButtonId[1] <= 5)
|
||||
{
|
||||
___JudgeObj.Flip(false);
|
||||
___JudgeObj.transform.Rotate(0.0f, 0.0f, 180f);
|
||||
}
|
||||
else
|
||||
{
|
||||
___JudgeObj.Flip(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
42
AquaMai/Visual/FixCircleSlideJudge.cs
Normal file
42
AquaMai/Visual/FixCircleSlideJudge.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using HarmonyLib;
|
||||
using Manager;
|
||||
using Monitor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AquaMai.Visual;
|
||||
|
||||
public class FixCircleSlideJudge
|
||||
{
|
||||
/*
|
||||
* 这个 Patch 让圆弧形的 Slide 的判定显示与判定线精确对齐 (原本会有一点歪), 就像 majdata 里那样
|
||||
* 我觉得这个 Patch 算是无副作用的, 可以默认开启
|
||||
*/
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(SlideRoot), "Initialize")]
|
||||
private static void FixJudgePosition(
|
||||
SlideRoot __instance, SlideType ___EndSlideType, SlideJudge ___JudgeObj
|
||||
)
|
||||
{
|
||||
if (null != ___JudgeObj)
|
||||
{
|
||||
float z = ___JudgeObj.transform.localPosition.z;
|
||||
if (___EndSlideType == SlideType.Slide_Circle_L)
|
||||
{
|
||||
float angle = -45.0f - 45.0f * __instance.EndButtonId;
|
||||
double angleRad = Math.PI / 180.0 * (angle + 90 + 22.5 + 2.6415);
|
||||
___JudgeObj.transform.localPosition = new Vector3(480f * (float)Math.Cos(angleRad), 480f * (float)Math.Sin(angleRad), z);
|
||||
___JudgeObj.transform.localRotation = Quaternion.Euler(0.0f, 0.0f, angle);
|
||||
}
|
||||
else if (___EndSlideType == SlideType.Slide_Circle_R)
|
||||
{
|
||||
float angle = -45.0f * __instance.EndButtonId;
|
||||
double angleRad = Math.PI / 180.0 * (angle + 90 - 22.5 - 2.6415);
|
||||
___JudgeObj.transform.localPosition = new Vector3(480f * (float)Math.Cos(angleRad), 480f * (float)Math.Sin(angleRad), z);
|
||||
___JudgeObj.transform.localRotation = Quaternion.Euler(0.0f, 0.0f, angle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
75
AquaMai/Visual/FixLevelDisplay.cs
Normal file
75
AquaMai/Visual/FixLevelDisplay.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using AquaMai.Attributes;
|
||||
using HarmonyLib;
|
||||
using MAI2.Util;
|
||||
using Manager;
|
||||
using Monitor;
|
||||
using Monitor.MusicSelect.ChainList;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AquaMai.Fix;
|
||||
|
||||
[GameVersion(24000)]
|
||||
public class FixLevelDisplay
|
||||
{
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(MusicChainCardObejct), "SetLevel")]
|
||||
private static void FixLevelShiftMusicChainCardObejct(MusicLevelID levelID, SpriteCounter ____digitLevel, SpriteCounter ____doubleDigitLevel, bool utage, GameObject ____difficultyUtageQuesionMarkSingleDigit, GameObject ____difficultyUtageQuesionMarkDoubleDigit)
|
||||
{
|
||||
switch (levelID)
|
||||
{
|
||||
case > MusicLevelID.Level9P:
|
||||
____digitLevel.gameObject.SetActive(value: false);
|
||||
____doubleDigitLevel.gameObject.SetActive(value: true);
|
||||
____doubleDigitLevel.ChangeText(levelID.GetLevelNum().PadRight(3));
|
||||
break;
|
||||
case >= MusicLevelID.None:
|
||||
____digitLevel.gameObject.SetActive(value: true);
|
||||
____doubleDigitLevel.gameObject.SetActive(value: false);
|
||||
____digitLevel.ChangeText(levelID.GetLevelNum().PadRight(2));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!utage) return;
|
||||
switch (levelID)
|
||||
{
|
||||
case > MusicLevelID.Level9P:
|
||||
____difficultyUtageQuesionMarkSingleDigit.SetActive(value: false);
|
||||
____difficultyUtageQuesionMarkDoubleDigit.SetActive(value: true);
|
||||
break;
|
||||
case >= MusicLevelID.None:
|
||||
____difficultyUtageQuesionMarkSingleDigit.SetActive(value: true);
|
||||
____difficultyUtageQuesionMarkDoubleDigit.SetActive(value: false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(SingleResultCardController), "SetLevel")]
|
||||
private static void FixLevelShiftSingleResultCardController(MusicLevelID levelID, bool isUtage, ref SpriteCounter ____difficultySingle, ref SpriteCounter ____difficultyDouble, GameObject ____utageQuestionMarkSingleDigit, GameObject ____utageQuestionMarkDoubleDigit)
|
||||
{
|
||||
FixLevelShiftMusicChainCardObejct(levelID, ____difficultySingle, ____difficultyDouble, isUtage, ____utageQuestionMarkSingleDigit, ____utageQuestionMarkDoubleDigit);
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(TotalResultPlayer), "SetLevel")]
|
||||
private static void FixLevelShiftTotalResultPlayer(MusicLevelID levelID, bool isUtage, ref SpriteCounter ____difficultySingle, ref SpriteCounter ____difficultyDouble, GameObject ____utageQuestionMarkSingleDigit, GameObject ____utageQuestionMarkDoubleDigit)
|
||||
{
|
||||
FixLevelShiftMusicChainCardObejct(levelID, ____difficultySingle, ____difficultyDouble, isUtage, ____utageQuestionMarkSingleDigit, ____utageQuestionMarkDoubleDigit);
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(ResultMonitor), "SetLevel")]
|
||||
private static void FixLevelShiftTotalResultPlayer(MusicLevelID levelID, ref SpriteCounter ____difficultySingle, ref SpriteCounter ____difficultyDouble)
|
||||
{
|
||||
FixLevelShiftMusicChainCardObejct(levelID, ____difficultySingle, ____difficultyDouble, false, null, null);
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(TrackStartMonitor), "SetTrackStart")]
|
||||
private static void FixLevelShiftTrackStartMonitor(int ___monitorIndex, ref SpriteCounter ____difficultySingle, ref SpriteCounter ____difficultyDouble, GameObject ____utageQuestionSingleDigit, GameObject ____utageQuestionDoubleDigit)
|
||||
{
|
||||
var music = Singleton<DataManager>.Instance.GetMusic(GameManager.SelectMusicID[___monitorIndex]);
|
||||
var levelID = (MusicLevelID)music.notesData[GameManager.SelectDifficultyID[___monitorIndex]].musicLevelID;
|
||||
FixLevelShiftMusicChainCardObejct(levelID, ____difficultySingle, ____difficultyDouble, music.name.id >= 100000, ____utageQuestionSingleDigit, ____utageQuestionDoubleDigit);
|
||||
}
|
||||
}
|
||||
102
AquaMai/Visual/FixSlideAutoPlay.cs
Normal file
102
AquaMai/Visual/FixSlideAutoPlay.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using System.Collections.Generic;
|
||||
using HarmonyLib;
|
||||
using Manager;
|
||||
using Monitor;
|
||||
|
||||
namespace AquaMai.Visual;
|
||||
|
||||
public class FixSlideAutoPlay
|
||||
{
|
||||
/* 这个 Patch 用于修复以下 bug:
|
||||
* SlideFan 在 AutoPlay 时, 只有第一个箭头会消失
|
||||
* 原 method 逻辑如下:
|
||||
*
|
||||
* if (this.IsNoteCheckTimeStartIgnoreJudgeWait())
|
||||
* {
|
||||
* // do something ...
|
||||
* if (!GameManager.IsAutoPlay())
|
||||
* {
|
||||
* // do something ...
|
||||
* for (int index = 0; index < this._arrowPrefubs.Length && (double) index < (double) num2 * 11.0; ++index)
|
||||
* {
|
||||
* // do something about displaying arrows ...
|
||||
* }
|
||||
* }
|
||||
* else
|
||||
* {
|
||||
* float num4 = (currentMsec - this.StarLaunchMsec) / (this.StarArriveMsec - this.StarLaunchMsec - this.lastWaitTime);
|
||||
* for (int index = 0; index < this._arrowPrefubs.Length && (double) index < (double) num4 * 1.0; ++index)
|
||||
* {
|
||||
* // do something about displaying arrows ...
|
||||
* }
|
||||
* if ((double) num4 > 1.0)
|
||||
* num1 = 3;
|
||||
* }
|
||||
* // do something ...
|
||||
* }
|
||||
*
|
||||
* 导致这个 bug 的原因是 else 分支的 for 循环终止条件写错了, 应该是 11.0 (因为有 11 个箭头), SBGA 写成了 1.0
|
||||
* 这个 method 中一共只有 5 处 ldc.r4 的 IL Code, 依次为 10.0, 11.0, 1.0, 1.0, 0.0
|
||||
* 修复 bug 需要把第三处的 1.0 更改为 11.0, 这里使用 Transpiler 解决
|
||||
*/
|
||||
[HarmonyTranspiler]
|
||||
[HarmonyPatch(typeof(SlideFan), "NoteCheck")]
|
||||
private static IEnumerable<CodeInstruction> FixFanAutoPlayArrow(IEnumerable<CodeInstruction> instructions)
|
||||
{
|
||||
List<CodeInstruction> instList = new List<CodeInstruction>(instructions);
|
||||
bool found = false;
|
||||
for (int i = 0; i < instList.Count; i++)
|
||||
{
|
||||
CodeInstruction inst = instList[i];
|
||||
if (inst.LoadsConstant(11.0))
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (found && inst.LoadsConstant(1.0))
|
||||
{
|
||||
inst.operand = 11.0f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return instList;
|
||||
}
|
||||
|
||||
/* 这个 Patch 让 Slide 在 AutoPlay 的时候, 每个区仍然会分按下和松开两段进行推进 (加上 this._hitIn 的变化)
|
||||
* 原 method 逻辑如下:
|
||||
*
|
||||
* if (!GameManager.IsAutoPlay())
|
||||
* {
|
||||
* // do somethings ...
|
||||
* }
|
||||
* else
|
||||
* {
|
||||
* float num1 = (currentMsec - this.StarLaunchMsec) / (this.StarArriveMsec - this.StarLaunchMsec - this.lastWaitTime);
|
||||
* this._hitIndex = (int) ((double) this._hitAreaList.Count * (double) num1);
|
||||
* if (this._hitIndex >= this._hitAreaList.Count)
|
||||
* this._hitIndex = this._hitAreaList.Count - 1;
|
||||
* if (this._hitIndex < 0)
|
||||
* this._hitIndex = 0;
|
||||
* int num2 = (int) ((double) this._dispLaneNum * this.GetDeleteArrowDistance());
|
||||
* // do somethings ...
|
||||
* }
|
||||
*
|
||||
* 现在要在 this.GetDeleteArrowDistance() 之前插入
|
||||
* this._hitIn = ((float)this._hitAreaList.Count * num1 > (float)this._hitIndex + 0.5f);
|
||||
* 这段代码, 可以采用 Prefix, GetDeleteArrowDistance() 只在两个地方调用过, 另一处就在上面的 if 分支中 (即非 AutoPlay 情况)
|
||||
*/
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(SlideRoot), "GetDeleteArrowDistance")]
|
||||
private static void FixSlideAutoPlayArrow(
|
||||
SlideRoot __instance, ref bool ____hitIn, int ____hitIndex, List<SlideManager.HitArea> ____hitAreaList,
|
||||
float ___StarLaunchMsec, float ___StarArriveMsec, float ___lastWaitTime
|
||||
)
|
||||
{
|
||||
if (GameManager.IsAutoPlay())
|
||||
{
|
||||
float prop = (NotesManager.GetCurrentMsec() - ___StarLaunchMsec) / (___StarArriveMsec - ___StarLaunchMsec - ___lastWaitTime);
|
||||
____hitIn = ____hitAreaList.Count * prop > ____hitIndex + 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
74
AquaMai/Visual/JudgeDisplay4B.cs
Normal file
74
AquaMai/Visual/JudgeDisplay4B.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using HarmonyLib;
|
||||
using Monitor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AquaMai.Visual;
|
||||
|
||||
public class JudgeDisplay4B
|
||||
{
|
||||
// 精确到子判定的自定义判定显示, 需要启用自定义皮肤功能 (理论上不启用自定义皮肤不会崩游戏, 只不过此时这个功能显然不会生效)
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(SlideJudge), "Initialize")]
|
||||
private static void SlideJudgeDisplay4B(
|
||||
SpriteRenderer ___SpriteRenderAdd, SpriteRenderer ___SpriteRender,
|
||||
SlideJudge.SlideJudgeType ____judgeType, SlideJudge.SlideAngle ____angle,
|
||||
NoteJudge.ETiming judge, float msec, bool isBreak
|
||||
)
|
||||
{
|
||||
var i = isBreak ? 1 : 0;
|
||||
Sprite sprite = CustomSkins.CustomJudgeSlide[i, (int)____judgeType, (int)____angle, (int)judge];
|
||||
if (sprite != null) {
|
||||
___SpriteRender.sprite = sprite;
|
||||
}
|
||||
|
||||
if (isBreak && judge == NoteJudge.ETiming.Critical)
|
||||
{
|
||||
sprite = CustomSkins.CustomJudgeSlide[i, (int)____judgeType, (int)____angle, (int) NoteJudge.ETiming.End];
|
||||
if (sprite != null)
|
||||
{
|
||||
___SpriteRenderAdd.sprite = sprite;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(JudgeGrade), "Initialize")]
|
||||
private static void JudgeGradeDisplay4B(
|
||||
SpriteRenderer ___SpriteRender,
|
||||
NoteJudge.ETiming judge, float msec, NoteJudge.EJudgeType type
|
||||
)
|
||||
{
|
||||
var i = (type == NoteJudge.EJudgeType.Break) ? 1 : 0;
|
||||
Sprite sprite = CustomSkins.CustomJudge[i, (int)judge];
|
||||
if (sprite != null) {
|
||||
___SpriteRender.sprite = sprite;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(JudgeGrade), "InitializeBreak")]
|
||||
private static void JudgeGradeBreakDisplay4B(
|
||||
SpriteRenderer ___SpriteRenderAdd,
|
||||
NoteJudge.ETiming judge, float msec, NoteJudge.EJudgeType type
|
||||
)
|
||||
{
|
||||
if (judge == NoteJudge.ETiming.Critical)
|
||||
{
|
||||
var sprite = CustomSkins.CustomJudge[1, (int) NoteJudge.ETiming.End];
|
||||
if (sprite != null)
|
||||
{
|
||||
___SpriteRenderAdd.sprite = sprite;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(JudgeGrade), "InitializeBreak")]
|
||||
private static void InitializeBreakFix(ref NoteJudge.EJudgeType type)
|
||||
{
|
||||
type = NoteJudge.EJudgeType.Break;
|
||||
}
|
||||
|
||||
}
|
||||
67
AquaMai/Visual/TrackStartProcessTweak.cs
Normal file
67
AquaMai/Visual/TrackStartProcessTweak.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using HarmonyLib;
|
||||
using Process;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AquaMai.Visual;
|
||||
|
||||
public class TrackStartProcessTweak
|
||||
{
|
||||
// 总之这个 Patch 没啥用, 是我个人用 sinmai 录谱面确认时用得到, 顺手也写进来了
|
||||
// 具体而言就是推迟了歌曲开始界面的动画便于后期剪辑
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(TrackStartProcess), "OnUpdate")]
|
||||
private static bool DelayAnimation(
|
||||
TrackStartProcess.TrackStartSequence ____state,
|
||||
ref float ____timeCounter,
|
||||
ProcessDataContainer ___container
|
||||
)
|
||||
{
|
||||
if (____state == TrackStartProcess.TrackStartSequence.Wait)
|
||||
{
|
||||
// 将开始动画(就是“噔噔, 噔 噔噔”)推迟
|
||||
float temp = ____timeCounter + Time.deltaTime;
|
||||
if (____timeCounter < 1.0f && temp >= 1.0f)
|
||||
{
|
||||
// 这是用来让转场动画继续播放的, 原本就是这个时候 notify 的同时开始播放开始动画
|
||||
// 现在把开始动画往后延
|
||||
___container.processManager.NotificationFadeIn();
|
||||
}
|
||||
____timeCounter = temp;
|
||||
if (____timeCounter >= 3.0f)
|
||||
{
|
||||
return true;
|
||||
// 原 method 的逻辑是这样
|
||||
// case TrackStartProcess.TrackStartSequence.Wait:
|
||||
// this._timeCounter += Time.deltaTime;
|
||||
// if ((double) this._timeCounter >= 1.0)
|
||||
// {
|
||||
// this._timeCounter = 0.0f;
|
||||
// this._state = TrackStartProcess.TrackStartSequence.Disp;
|
||||
// /* 一些开始播放开始动画的代码 */
|
||||
// this.container.processManager.NotificationFadeIn();
|
||||
// break;
|
||||
// }
|
||||
// break;
|
||||
// 所以只要在 prefix 里面等到 timeCounter 达到我们想要的值以后再执行原 method 就好
|
||||
// 这里有个细节: NotificationFadeIn() 会被执行两遍, 这其实不好, 是个潜在 bug
|
||||
// 不过由于此处把开始动画往后推了 2s, 转场动画已经结束把 Process 释放掉了, 所以第二遍会找不到 Process 就没效果
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else if (____state == TrackStartProcess.TrackStartSequence.DispEnd)
|
||||
{
|
||||
// 将开始动画结束以后的转场动画推迟
|
||||
____timeCounter += Time.deltaTime; // timeCounter 会在先前由原本的 method 归零
|
||||
if (____timeCounter >= 1.0f)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user