[+] TouchHold judgment support in non-C areas#45
Conversation
审核者指南介绍了通过添加新的配置选项、更新迁移和排序顺序,以及实现一个基于 Harmony 的 mod 来修补 TouchHoldC.NoteCheck,从而支持正确判断默认 C 区之外的 Touch Hold 音符,该 mod 基于传感器区域路由触摸输入通过自定义逻辑。 JudgeTouchHoldInNormalArea 及相关更改的类图classDiagram
class JudgeTouchHoldInNormalArea {
<<ConfigSection>>
static TouchHoldC _currentInstance
+static IEnumerable<CodeInstruction> NoteCheckTranspiler(IEnumerable<CodeInstruction> instructions)
+static void SetCurrentInstance(TouchHoldC instance)
+static int GetTouchAreaValue(TouchHoldC instance)
+static bool CustomTouchDown(int monitorId)
+static bool CustomTouchPush(int monitorId)
-static InputManager.TouchPanelArea GetTouchPanelAreaFromButtonId(InputManager.ButtonSetting buttonId, int touchAreaValue)
}
class TouchHoldC {
+NoteCheck()
+ButtonId
...
}
class InputManager {
+static bool InGameTouchPanelArea_C_Down(int monitorId)
+static bool InGameTouchPanelArea_C_Push(int monitorId)
+static bool InGameTouchPanelArea_B_Down(int monitorId, ButtonSetting buttonId)
+static bool InGameTouchPanelArea_B_Push(int monitorId, ButtonSetting buttonId)
+static bool InGameTouchPanelArea_E_Down(int monitorId, ButtonSetting buttonId)
+static bool InGameTouchPanelArea_E_Push(int monitorId, ButtonSetting buttonId)
+static bool InGameTouchPanelAreaDown(int monitorId, ButtonSetting buttonId)
+static bool InGameTouchPanelAreaPush(int monitorId, ButtonSetting buttonId)
+static bool InGameTouchPanelArea_D_Down(int monitorId, ButtonSetting buttonId)
+static bool InGameTouchPanelArea_D_Push(int monitorId, ButtonSetting buttonId)
+static bool IsUsedThisFrame(int monitorId, TouchPanelArea area)
enum ButtonSetting
enum TouchPanelArea
}
class GameManager {
+static bool IsAutoPlay()
}
JudgeTouchHoldInNormalArea -- TouchHoldC : patches
JudgeTouchHoldInNormalArea ..> InputManager : uses
JudgeTouchHoldInNormalArea ..> GameManager : uses
文件级别更改
提示和命令与 Sourcery 互动
自定义您的体验访问您的 仪表板 以:
获得帮助Original review guide in EnglishReviewer's GuideIntroduces support for correct judging of Touch Hold notes outside the default C zone by adding a new configuration option, updating migration and sort order, and implementing a Harmony-based mod that patches TouchHoldC.NoteCheck to route touch inputs through custom logic based on the sensor area. Class diagram for JudgeTouchHoldInNormalArea and related changesclassDiagram
class JudgeTouchHoldInNormalArea {
<<ConfigSection>>
static TouchHoldC _currentInstance
+static IEnumerable<CodeInstruction> NoteCheckTranspiler(IEnumerable<CodeInstruction> instructions)
+static void SetCurrentInstance(TouchHoldC instance)
+static int GetTouchAreaValue(TouchHoldC instance)
+static bool CustomTouchDown(int monitorId)
+static bool CustomTouchPush(int monitorId)
-static InputManager.TouchPanelArea GetTouchPanelAreaFromButtonId(InputManager.ButtonSetting buttonId, int touchAreaValue)
}
class TouchHoldC {
+NoteCheck()
+ButtonId
...
}
class InputManager {
+static bool InGameTouchPanelArea_C_Down(int monitorId)
+static bool InGameTouchPanelArea_C_Push(int monitorId)
+static bool InGameTouchPanelArea_B_Down(int monitorId, ButtonSetting buttonId)
+static bool InGameTouchPanelArea_B_Push(int monitorId, ButtonSetting buttonId)
+static bool InGameTouchPanelArea_E_Down(int monitorId, ButtonSetting buttonId)
+static bool InGameTouchPanelArea_E_Push(int monitorId, ButtonSetting buttonId)
+static bool InGameTouchPanelAreaDown(int monitorId, ButtonSetting buttonId)
+static bool InGameTouchPanelAreaPush(int monitorId, ButtonSetting buttonId)
+static bool InGameTouchPanelArea_D_Down(int monitorId, ButtonSetting buttonId)
+static bool InGameTouchPanelArea_D_Push(int monitorId, ButtonSetting buttonId)
+static bool IsUsedThisFrame(int monitorId, TouchPanelArea area)
enum ButtonSetting
enum TouchPanelArea
}
class GameManager {
+static bool IsAutoPlay()
}
JudgeTouchHoldInNormalArea -- TouchHoldC : patches
JudgeTouchHoldInNormalArea ..> InputManager : uses
JudgeTouchHoldInNormalArea ..> GameManager : uses
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
嘿 @ERR0RPR0MPT - 我已经查看了你的更改 - 这里有一些反馈:
- CustomTouchDown 和 CustomTouchPush 共享几乎相同的 switch‐case 逻辑——考虑提取一个助手来减少重复。
- GetTouchAreaValue 扫描每次调用中的所有字段和属性——缓存已解析的 FieldInfo/PropertyInfo 以避免重复的反射开销。
- 你的转译器盲目地在开头插入——定位一个特定的 IL 模式或偏移量,以使补丁对未来的 TouchHoldC 更改更健壮。
AI 代理的提示
请解决此代码审查中的评论:
## 总体评论
- CustomTouchDown 和 CustomTouchPush 共享几乎相同的 switch‐case 逻辑——考虑提取一个助手来减少重复。
- GetTouchAreaValue 扫描每次调用中的所有字段和属性——缓存已解析的 FieldInfo/PropertyInfo 以避免重复的反射开销。
- 你的转译器盲目地在开头插入——定位一个特定的 IL 模式或偏移量,以使补丁对未来的 TouchHoldC 更改更健壮。
## 单独评论
### 评论 1
<location> `AquaMai.Mods/Fancy/GamePlay/JudgeTouchHoldInNormalArea.cs:19` </location>
<code_context>
+)]
+public class JudgeTouchHoldInNormalArea
+{
+ private static TouchHoldC _currentInstance;
+
+ [HarmonyPatch(typeof(TouchHoldC), "NoteCheck")]
</code_context>
<issue_to_address>
静态字段 _currentInstance 可能会在多线程或多实例场景中导致问题。
这里的静态字段可能会导致竞争条件或并发或多个 NoteCheck 调用中的不正确行为。如果需要并发,请考虑显式传递实例或使用线程本地存储。
</issue_to_address>
### 评论 2
<location> `AquaMai.Mods/Fancy/GamePlay/JudgeTouchHoldInNormalArea.cs:52` </location>
<code_context>
+
+ // 替换方法调用
+ int replacedCount = 0;
+ for (int i = 0; i < codes.Count; i++)
+ {
+ if (codes[i].opcode == OpCodes.Call && codes[i].operand is MethodInfo method)
+ {
+ if (method == cDownMethod)
+ {
+ codes[i] = new CodeInstruction(OpCodes.Call, customDownMethod);
+ replacedCount++;
+ MelonLogger.Msg($"Replaced C_Down method call at index {i}");
+ }
+ else if (method == cPushMethod)
+ {
+ codes[i] = new CodeInstruction(OpCodes.Call, customPushMethod);
+ replacedCount++;
+ MelonLogger.Msg($"Replaced C_Push method call at index {i}");
+ }
+ }
</code_context>
<issue_to_address>
就地替换指令可能无法保留原始指令元数据。
替换 CodeInstruction 对象时,请确保您还从原始指令传输任何元数据(例如,标签、异常块)以保持正确的控制流和异常处理。
</issue_to_address>
### 评论 3
<location> `AquaMai.Mods/Fancy/GamePlay/JudgeTouchHoldInNormalArea.cs:258` </location>
<code_context>
+ }
+
+ // 根据ButtonId和TouchArea计算正确的TouchPanelArea枚举值
+ private static InputManager.TouchPanelArea GetTouchPanelAreaFromButtonId(InputManager.ButtonSetting buttonId,
+ int touchAreaValue)
+ {
</code_context>
<issue_to_address>
假定枚举值是连续且有序的。
如果枚举值不是严格连续且有序的,则此方法可能会产生无效结果。请验证此假设或使用显式映射。
</issue_to_address>
### 评论 4
<location> `AquaMai.Mods/Fancy/GamePlay/JudgeTouchHoldInNormalArea.cs:184` </location>
<code_context>
+ }
+ }
+
+ public static bool CustomTouchDown(int monitorId)
+ {
+ try
</code_context>
<issue_to_address>
考虑使用字典和统一的反射方法将重复的 switch 语句和反射逻辑重构为数据驱动的助手。
以下是一些小的重构,它们会将您的样板开关和繁重的反射折叠为数据驱动的助手,而无需触及任何公共行为。
1. 将您的“Down”/“Push”处理程序缓存在字典中,而不是重复两次 switch:
```csharp
// 在类级别
private static readonly Dictionary<int, Func<int, InputManager.ButtonSetting, bool>> _downHandlers =
new Dictionary<int, Func<int, InputManager.ButtonSetting, bool>>
{
[0] = InputManager.InGameTouchPanelArea_B_Down,
[1] = (m, b) => InputManager.InGameTouchPanelArea_C_Down(m),
[2] = InputManager.InGameTouchPanelArea_E_Down,
[3] = InputManager.InGameTouchPanelAreaDown,
[4] = InputManager.InGameTouchPanelArea_D_Down
};
private static readonly Dictionary<int, Func<int, InputManager.ButtonSetting, bool>> _pushHandlers =
new Dictionary<int, Func<int, InputManager.ButtonSetting, bool>>
{
[0] = InputManager.InGameTouchPanelArea_B_Push,
[1] = (m, b) => InputManager.InGameTouchPanelArea_C_Push(m),
[2] = InputManager.InGameTouchPanelArea_E_Push,
[3] = InputManager.InGameTouchPanelAreaPush,
[4] = InputManager.InGameTouchPanelArea_D_Push
};
// 然后在 CustomTouchDown 中:
var handler = _downHandlers.TryGetValue(area, out var h)
? h
: (m,b) => InputManager.InGameTouchPanelArea_C_Down(m);
bool touchDown = handler(monitorId, buttonId);
// 并在 CustomTouchPush 中:
var push = (_pushHandlers.TryGetValue(area, out var p) ? p : (m,b) => InputManager.InGameTouchPanelArea_C_Push(m))
.Invoke(monitorId, buttonId);
```
2. 将您的许多基于 switch 的面板区域映射折叠为一个小的基本值数组:
```csharp
// 类级别
private static readonly InputManager.TouchPanelArea[] _baseAreas =
{
InputManager.TouchPanelArea.B1,
InputManager.TouchPanelArea.C1,
InputManager.TouchPanelArea.E1,
InputManager.TouchPanelArea.A1,
InputManager.TouchPanelArea.D1
};
// 将 GetTouchPanelAreaFromButtonId 替换为:
private static InputManager.TouchPanelArea GetTouchPanelAreaFromButtonId(
InputManager.ButtonSetting btn, int area)
{
var baseArea = (area >= 0 && area < _baseAreas.Length)
? _baseAreas[area]
: InputManager.TouchPanelArea.C1;
return baseArea + (int)btn;
}
```
3. 将您的字段/属性搜索减少为一个助手,该助手尝试名称,然后回退到任何枚举类型的成员:
```csharp
private static readonly string[] _candidateNames = { "TouchArea", "touchArea", "_touchArea", "m_touchArea" };
private static int GetTouchAreaValue(TouchHoldC inst)
{
// 1) 尝试候选名称
foreach (var name in _candidateNames)
{
var f = typeof(TouchHoldC).GetField(name, ALL_FLAGS);
if (f != null && TryEnumValue(f.GetValue(inst), out var v)) return v;
var p = typeof(TouchHoldC).GetProperty(name, ALL_FLAGS);
if (p != null && TryEnumValue(p.GetValue(inst), out v)) return v;
}
// 2) 回退:任何枚举类型的字段/属性
foreach (var mi in typeof(TouchHoldC).GetMembers(ALL_FLAGS))
{
object val = mi switch
{
FieldInfo f => f.GetValue(inst),
PropertyInfo p => TrySafe(() => p.GetValue(inst)),
_ => null
};
if (TryEnumValue(val, out var v)) return v;
}
MelonLogger.Error("Could not find TouchArea");
return 1;
}
private static bool TryEnumValue(object o, out int val)
{
if (o != null && o.GetType().IsEnum)
{
val = (int)o;
return true;
}
val = default;
return false;
}
private static object TrySafe(Func<object> f)
{
try { return f(); }
catch { return null; }
}
const BindingFlags ALL_FLAGS = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
```
这三个更改删除了您的重复 switch 块,将面板区域逻辑折叠为数据,并将反射统一为一个简洁的助手——所有这些都同时保留了现有行为。
</issue_to_address>帮助我更有用!请点击每个评论上的 👍 或 👎,我将使用反馈来改进您的评论。
Original comment in English
Hey @ERR0RPR0MPT - I've reviewed your changes - here's some feedback:
- CustomTouchDown and CustomTouchPush share nearly identical switch‐case logic—consider extracting a helper to reduce duplication.
- GetTouchAreaValue scans all fields and properties on every call—cache the resolved FieldInfo/PropertyInfo to avoid repeated reflection overhead.
- Your transpiler blindly inserts at the beginning—target a specific IL pattern or offset instead to make the patch more robust against future TouchHoldC changes.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- CustomTouchDown and CustomTouchPush share nearly identical switch‐case logic—consider extracting a helper to reduce duplication.
- GetTouchAreaValue scans all fields and properties on every call—cache the resolved FieldInfo/PropertyInfo to avoid repeated reflection overhead.
- Your transpiler blindly inserts at the beginning—target a specific IL pattern or offset instead to make the patch more robust against future TouchHoldC changes.
## Individual Comments
### Comment 1
<location> `AquaMai.Mods/Fancy/GamePlay/JudgeTouchHoldInNormalArea.cs:19` </location>
<code_context>
+)]
+public class JudgeTouchHoldInNormalArea
+{
+ private static TouchHoldC _currentInstance;
+
+ [HarmonyPatch(typeof(TouchHoldC), "NoteCheck")]
</code_context>
<issue_to_address>
Static field _currentInstance may cause issues in multi-threaded or multi-instance scenarios.
A static field here can cause race conditions or incorrect behavior with concurrent or multiple NoteCheck calls. Consider passing the instance explicitly or using thread-local storage if concurrency is needed.
</issue_to_address>
### Comment 2
<location> `AquaMai.Mods/Fancy/GamePlay/JudgeTouchHoldInNormalArea.cs:52` </location>
<code_context>
+
+ // 替换方法调用
+ int replacedCount = 0;
+ for (int i = 0; i < codes.Count; i++)
+ {
+ if (codes[i].opcode == OpCodes.Call && codes[i].operand is MethodInfo method)
+ {
+ if (method == cDownMethod)
+ {
+ codes[i] = new CodeInstruction(OpCodes.Call, customDownMethod);
+ replacedCount++;
+ MelonLogger.Msg($"Replaced C_Down method call at index {i}");
+ }
+ else if (method == cPushMethod)
+ {
+ codes[i] = new CodeInstruction(OpCodes.Call, customPushMethod);
+ replacedCount++;
+ MelonLogger.Msg($"Replaced C_Push method call at index {i}");
+ }
+ }
</code_context>
<issue_to_address>
Replacing instructions in-place may not preserve original instruction metadata.
When replacing CodeInstruction objects, ensure you also transfer any metadata (e.g., labels, exception blocks) from the original instruction to maintain correct control flow and exception handling.
</issue_to_address>
### Comment 3
<location> `AquaMai.Mods/Fancy/GamePlay/JudgeTouchHoldInNormalArea.cs:258` </location>
<code_context>
+ }
+
+ // 根据ButtonId和TouchArea计算正确的TouchPanelArea枚举值
+ private static InputManager.TouchPanelArea GetTouchPanelAreaFromButtonId(InputManager.ButtonSetting buttonId,
+ int touchAreaValue)
+ {
</code_context>
<issue_to_address>
Assumes enum values are contiguous and ordered.
If enum values are not strictly contiguous and ordered, this approach may yield invalid results. Please validate this assumption or use an explicit mapping.
</issue_to_address>
### Comment 4
<location> `AquaMai.Mods/Fancy/GamePlay/JudgeTouchHoldInNormalArea.cs:184` </location>
<code_context>
+ }
+ }
+
+ public static bool CustomTouchDown(int monitorId)
+ {
+ try
</code_context>
<issue_to_address>
Consider refactoring repeated switch statements and reflection logic into data-driven helpers using dictionaries and unified reflection methods.
Here are a few small refactorings that will collapse your boiler-plate switches and heavy reflection into data-driven helpers without touching any public behavior.
1. Cache your “Down”/“Push” handlers in dictionaries instead of repeating the switch twice:
```csharp
// at class level
private static readonly Dictionary<int, Func<int, InputManager.ButtonSetting, bool>> _downHandlers =
new Dictionary<int, Func<int, InputManager.ButtonSetting, bool>>
{
[0] = InputManager.InGameTouchPanelArea_B_Down,
[1] = (m, b) => InputManager.InGameTouchPanelArea_C_Down(m),
[2] = InputManager.InGameTouchPanelArea_E_Down,
[3] = InputManager.InGameTouchPanelAreaDown,
[4] = InputManager.InGameTouchPanelArea_D_Down
};
private static readonly Dictionary<int, Func<int, InputManager.ButtonSetting, bool>> _pushHandlers =
new Dictionary<int, Func<int, InputManager.ButtonSetting, bool>>
{
[0] = InputManager.InGameTouchPanelArea_B_Push,
[1] = (m, b) => InputManager.InGameTouchPanelArea_C_Push(m),
[2] = InputManager.InGameTouchPanelArea_E_Push,
[3] = InputManager.InGameTouchPanelAreaPush,
[4] = InputManager.InGameTouchPanelArea_D_Push
};
// then in CustomTouchDown:
var handler = _downHandlers.TryGetValue(area, out var h)
? h
: (m,b) => InputManager.InGameTouchPanelArea_C_Down(m);
bool touchDown = handler(monitorId, buttonId);
// and in CustomTouchPush:
var push = (_pushHandlers.TryGetValue(area, out var p) ? p : (m,b) => InputManager.InGameTouchPanelArea_C_Push(m))
.Invoke(monitorId, buttonId);
```
2. Collapse your many switch‐based panel‐area mapping into a small base‐value array:
```csharp
// class‐level
private static readonly InputManager.TouchPanelArea[] _baseAreas =
{
InputManager.TouchPanelArea.B1,
InputManager.TouchPanelArea.C1,
InputManager.TouchPanelArea.E1,
InputManager.TouchPanelArea.A1,
InputManager.TouchPanelArea.D1
};
// replace GetTouchPanelAreaFromButtonId with:
private static InputManager.TouchPanelArea GetTouchPanelAreaFromButtonId(
InputManager.ButtonSetting btn, int area)
{
var baseArea = (area >= 0 && area < _baseAreas.Length)
? _baseAreas[area]
: InputManager.TouchPanelArea.C1;
return baseArea + (int)btn;
}
```
3. Reduce your field/property hunts into one helper that tries names, then falls back to any enum‐typed member:
```csharp
private static readonly string[] _candidateNames = { "TouchArea", "touchArea", "_touchArea", "m_touchArea" };
private static int GetTouchAreaValue(TouchHoldC inst)
{
// 1) try candidate names
foreach (var name in _candidateNames)
{
var f = typeof(TouchHoldC).GetField(name, ALL_FLAGS);
if (f != null && TryEnumValue(f.GetValue(inst), out var v)) return v;
var p = typeof(TouchHoldC).GetProperty(name, ALL_FLAGS);
if (p != null && TryEnumValue(p.GetValue(inst), out v)) return v;
}
// 2) fallback: any enum‐typed field/property
foreach (var mi in typeof(TouchHoldC).GetMembers(ALL_FLAGS))
{
object val = mi switch
{
FieldInfo f => f.GetValue(inst),
PropertyInfo p => TrySafe(() => p.GetValue(inst)),
_ => null
};
if (TryEnumValue(val, out var v)) return v;
}
MelonLogger.Error("Could not find TouchArea");
return 1;
}
private static bool TryEnumValue(object o, out int val)
{
if (o != null && o.GetType().IsEnum)
{
val = (int)o;
return true;
}
val = default;
return false;
}
private static object TrySafe(Func<object> f)
{
try { return f(); }
catch { return null; }
}
const BindingFlags ALL_FLAGS = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
```
These three changes remove your duplicate switch blocks, collapse panel‐area logic into data, and unify reflection into a single concise helper—all while preserving existing behavior.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| } | ||
| } | ||
|
|
||
| public static bool CustomTouchDown(int monitorId) |
There was a problem hiding this comment.
issue (complexity): 考虑使用字典和统一的反射方法将重复的 switch 语句和反射逻辑重构为数据驱动的助手。
以下是一些小的重构,它们会将您的样板开关和繁重的反射折叠为数据驱动的助手,而无需触及任何公共行为。
- 将您的“Down”/“Push”处理程序缓存在字典中,而不是重复两次 switch:
// 在类级别
private static readonly Dictionary<int, Func<int, InputManager.ButtonSetting, bool>> _downHandlers =
new Dictionary<int, Func<int, InputManager.ButtonSetting, bool>>
{
[0] = InputManager.InGameTouchPanelArea_B_Down,
[1] = (m, b) => InputManager.InGameTouchPanelArea_C_Down(m),
[2] = InputManager.InGameTouchPanelArea_E_Down,
[3] = InputManager.InGameTouchPanelAreaDown,
[4] = InputManager.InGameTouchPanelArea_D_Down
};
private static readonly Dictionary<int, Func<int, InputManager.ButtonSetting, bool>> _pushHandlers =
new Dictionary<int, Func<int, InputManager.ButtonSetting, bool>>
{
[0] = InputManager.InGameTouchPanelArea_B_Push,
[1] = (m, b) => InputManager.InGameTouchPanelArea_C_Push(m),
[2] = InputManager.InGameTouchPanelArea_E_Push,
[3] = InputManager.InGameTouchPanelAreaPush,
[4] = InputManager.InGameTouchPanelArea_D_Push
};
// 然后在 CustomTouchDown 中:
var handler = _downHandlers.TryGetValue(area, out var h)
? h
: (m,b) => InputManager.InGameTouchPanelArea_C_Down(m);
bool touchDown = handler(monitorId, buttonId);
// 并在 CustomTouchPush 中:
var push = (_pushHandlers.TryGetValue(area, out var p) ? p : (m,b) => InputManager.InGameTouchPanelArea_C_Push(m))
.Invoke(monitorId, buttonId);- 将您的许多基于 switch 的面板区域映射折叠为一个小的基本值数组:
// 类级别
private static readonly InputManager.TouchPanelArea[] _baseAreas =
{
InputManager.TouchPanelArea.B1,
InputManager.TouchPanelArea.C1,
InputManager.TouchPanelArea.E1,
InputManager.TouchPanelArea.A1,
InputManager.TouchPanelArea.D1
};
// 将 GetTouchPanelAreaFromButtonId 替换为:
private static InputManager.TouchPanelArea GetTouchPanelAreaFromButtonId(
InputManager.ButtonSetting btn, int area)
{
var baseArea = (area >= 0 && area < _baseAreas.Length)
? _baseAreas[area]
: InputManager.TouchPanelArea.C1;
return baseArea + (int)btn;
}- 将您的字段/属性搜索减少为一个助手,该助手尝试名称,然后回退到任何枚举类型的成员:
private static readonly string[] _candidateNames = { "TouchArea", "touchArea", "_touchArea", "m_touchArea" };
private static int GetTouchAreaValue(TouchHoldC inst)
{
// 1) 尝试候选名称
foreach (var name in _candidateNames)
{
var f = typeof(TouchHoldC).GetField(name, ALL_FLAGS);
if (f != null && TryEnumValue(f.GetValue(inst), out var v)) return v;
var p = typeof(TouchHoldC).GetProperty(name, ALL_FLAGS);
if (p != null && TryEnumValue(p.GetValue(inst), out v)) return v;
}
// 2) 回退:任何枚举类型的字段/属性
foreach (var mi in typeof(TouchHoldC).GetMembers(ALL_FLAGS))
{
object val = mi switch
{
FieldInfo f => f.GetValue(inst),
PropertyInfo p => TrySafe(() => p.GetValue(inst)),
_ => null
};
if (TryEnumValue(val, out var v)) return v;
}
MelonLogger.Error("Could not find TouchArea");
return 1;
}
private static bool TryEnumValue(object o, out int val)
{
if (o != null && o.GetType().IsEnum)
{
val = (int)o;
return true;
}
val = default;
return false;
}
private static object TrySafe(Func<object> f)
{
try { return f(); }
catch { return null; }
}
const BindingFlags ALL_FLAGS = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;这三个更改删除了您的重复 switch 块,将面板区域逻辑折叠为数据,并将反射统一为一个简洁的助手——所有这些都同时保留了现有行为。
Original comment in English
issue (complexity): Consider refactoring repeated switch statements and reflection logic into data-driven helpers using dictionaries and unified reflection methods.
Here are a few small refactorings that will collapse your boiler-plate switches and heavy reflection into data-driven helpers without touching any public behavior.
- Cache your “Down”/“Push” handlers in dictionaries instead of repeating the switch twice:
// at class level
private static readonly Dictionary<int, Func<int, InputManager.ButtonSetting, bool>> _downHandlers =
new Dictionary<int, Func<int, InputManager.ButtonSetting, bool>>
{
[0] = InputManager.InGameTouchPanelArea_B_Down,
[1] = (m, b) => InputManager.InGameTouchPanelArea_C_Down(m),
[2] = InputManager.InGameTouchPanelArea_E_Down,
[3] = InputManager.InGameTouchPanelAreaDown,
[4] = InputManager.InGameTouchPanelArea_D_Down
};
private static readonly Dictionary<int, Func<int, InputManager.ButtonSetting, bool>> _pushHandlers =
new Dictionary<int, Func<int, InputManager.ButtonSetting, bool>>
{
[0] = InputManager.InGameTouchPanelArea_B_Push,
[1] = (m, b) => InputManager.InGameTouchPanelArea_C_Push(m),
[2] = InputManager.InGameTouchPanelArea_E_Push,
[3] = InputManager.InGameTouchPanelAreaPush,
[4] = InputManager.InGameTouchPanelArea_D_Push
};
// then in CustomTouchDown:
var handler = _downHandlers.TryGetValue(area, out var h)
? h
: (m,b) => InputManager.InGameTouchPanelArea_C_Down(m);
bool touchDown = handler(monitorId, buttonId);
// and in CustomTouchPush:
var push = (_pushHandlers.TryGetValue(area, out var p) ? p : (m,b) => InputManager.InGameTouchPanelArea_C_Push(m))
.Invoke(monitorId, buttonId);- Collapse your many switch‐based panel‐area mapping into a small base‐value array:
// class‐level
private static readonly InputManager.TouchPanelArea[] _baseAreas =
{
InputManager.TouchPanelArea.B1,
InputManager.TouchPanelArea.C1,
InputManager.TouchPanelArea.E1,
InputManager.TouchPanelArea.A1,
InputManager.TouchPanelArea.D1
};
// replace GetTouchPanelAreaFromButtonId with:
private static InputManager.TouchPanelArea GetTouchPanelAreaFromButtonId(
InputManager.ButtonSetting btn, int area)
{
var baseArea = (area >= 0 && area < _baseAreas.Length)
? _baseAreas[area]
: InputManager.TouchPanelArea.C1;
return baseArea + (int)btn;
}- Reduce your field/property hunts into one helper that tries names, then falls back to any enum‐typed member:
private static readonly string[] _candidateNames = { "TouchArea", "touchArea", "_touchArea", "m_touchArea" };
private static int GetTouchAreaValue(TouchHoldC inst)
{
// 1) try candidate names
foreach (var name in _candidateNames)
{
var f = typeof(TouchHoldC).GetField(name, ALL_FLAGS);
if (f != null && TryEnumValue(f.GetValue(inst), out var v)) return v;
var p = typeof(TouchHoldC).GetProperty(name, ALL_FLAGS);
if (p != null && TryEnumValue(p.GetValue(inst), out v)) return v;
}
// 2) fallback: any enum‐typed field/property
foreach (var mi in typeof(TouchHoldC).GetMembers(ALL_FLAGS))
{
object val = mi switch
{
FieldInfo f => f.GetValue(inst),
PropertyInfo p => TrySafe(() => p.GetValue(inst)),
_ => null
};
if (TryEnumValue(val, out var v)) return v;
}
MelonLogger.Error("Could not find TouchArea");
return 1;
}
private static bool TryEnumValue(object o, out int val)
{
if (o != null && o.GetType().IsEnum)
{
val = (int)o;
return true;
}
val = default;
return false;
}
private static object TrySafe(Func<object> f)
{
try { return f(); }
catch { return null; }
}
const BindingFlags ALL_FLAGS = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;These three changes remove your duplicate switch blocks, collapse panel‐area logic into data, and unify reflection into a single concise helper—all while preserving existing behavior.
|
|
||
| // MaimaiDX2077 (WTF is the name?) | ||
| MapBooleanTrueToSectionEnable(src, dst, "MaimaiDX2077.CustomNoteTypePatch", "Fancy.GamePlay.CustomNoteTypes"); | ||
| MapBooleanTrueToSectionEnable(src, dst, "MaimaiDX2077.JudgeTouchHoldInNormalArea", "Fancy.GamePlay.JudgeTouchHoldInNormalArea"); |
| using MelonLoader; | ||
| using Monitor; | ||
|
|
||
| namespace AquaMai.Mods.Fancy.GamePlay; |
There was a problem hiding this comment.
如果原先游戏无法正常判定,这个 patch 应该放在 fix 命名空间中
| try | ||
| { | ||
| // 尝试多种可能的字段名 | ||
| var possibleFieldNames = new[] { "TouchArea", "touchArea", "_touchArea", "m_touchArea" }; |
好的,这是将拉取请求摘要翻译成中文的结果:
Sourcery 总结
通过修补游戏的输入检查,增加对非 C 区域 Touch Hold 输入的正确判断的支持,并将该功能集成到配置迁移和排序中。
新特性:
增强功能:
Original summary in English
Summary by Sourcery
Add support for correctly judging Touch Hold inputs in non-C areas by patching the game's input checks and integrate the feature into configuration migration and sorting
New Features:
Enhancements: