-
Notifications
You must be signed in to change notification settings - Fork 73
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Customize note skin (just like MajdataView) & several patches
- Loading branch information
Showing
8 changed files
with
667 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,314 @@ | ||
using System; | ||
using System.IO; | ||
using System.Collections.Generic; | ||
using System.Reflection; | ||
using HarmonyLib; | ||
using MelonLoader; | ||
using Monitor; | ||
using Monitor.Game; | ||
using Process; | ||
using UnityEngine; | ||
|
||
namespace AquaMai.CustomSkin; | ||
|
||
public class CustomNoteSkin | ||
{ | ||
private static readonly List<string> ImageExts = [".jpg", ".png", ".jpeg"]; | ||
private static readonly List<string> SlideFanFields = ["_normalSlideFan", "_eachSlideFan", "_breakSlideFan", "_breakSlideFanEff"]; | ||
|
||
private static Sprite customOutline; | ||
private static Sprite[,] customSlideFan = new Sprite[4, 11]; | ||
|
||
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, "Skins"))) return; | ||
|
||
foreach (var laFile in Directory.EnumerateFiles(Path.Combine(Environment.CurrentDirectory, "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); | ||
|
||
Traverse traverse; | ||
|
||
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 (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; | ||
} | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.