diff --git a/MiraAPI/GameOptions/Attributes/Delegate/ModdedOptionMemberAttribute.cs b/MiraAPI/GameOptions/Attributes/Delegate/ModdedOptionMemberAttribute.cs index 1db3700..43742ba 100644 --- a/MiraAPI/GameOptions/Attributes/Delegate/ModdedOptionMemberAttribute.cs +++ b/MiraAPI/GameOptions/Attributes/Delegate/ModdedOptionMemberAttribute.cs @@ -38,7 +38,7 @@ public abstract class ModdedOptionMemberAttribute(string? memberName, Type? hold var flags = BindingFlags.Static | BindingFlags.Public; type = holderType ?? groupType; - MemberName ??= $"{property.Name}{defaultSuffix}"; + MemberName ??= property.Name + defaultSuffix; if (type.IsAssignableTo(typeof(AbstractOptionGroup))) { @@ -59,13 +59,24 @@ public abstract class ModdedOptionMemberAttribute(string? memberName, Type? hold var member = type.GetMember(MemberName, flags).FirstOrDefault(); - if (instanceGetter != null) + if (instanceGetter == null) + return member; + + bool isStatic; + + if (member is FieldInfo field) + { + isStatic = field.IsStatic; + } + else { var method = (member as PropertyInfo)?.GetGetMethod(true) ?? member as MethodInfo; - if (method is { IsStatic: true }) - instanceGetter = null; + isStatic = method is { IsStatic: true }; } + if (isStatic) + instanceGetter = null; + return member; } @@ -186,8 +197,8 @@ public abstract class ModdedOptionMemberAttribute(string? memberName, Type? hold baseParamTypes, delegateType, suffix, - forwardParameters: baseParamTypes.Length > 0, - postProcessCall: postProcessCall); + baseParamTypes.Length > 0, + postProcessCall); return _ => sharedDelegate; } @@ -202,11 +213,16 @@ public abstract class ModdedOptionMemberAttribute(string? memberName, Type? hold for (byte i = 0; i < baseParamTypes.Length; i++) { - if (i == 0) il.Emit(OpCodes.Ldarg_0); - else if (i == 1) il.Emit(OpCodes.Ldarg_1); - else if (i == 2) il.Emit(OpCodes.Ldarg_2); - else if (i == 3) il.Emit(OpCodes.Ldarg_3); - else il.Emit(OpCodes.Ldarg_S, i); + if (i == 0) + il.Emit(OpCodes.Ldarg_0); + else if (i == 1) + il.Emit(OpCodes.Ldarg_1); + else if (i == 2) + il.Emit(OpCodes.Ldarg_2); + else if (i == 3) + il.Emit(OpCodes.Ldarg_3); + else + il.Emit(OpCodes.Ldarg_S, i); } if (hasIndexParameter) diff --git a/MiraAPI/GameOptions/Attributes/Delegate/ModdedOptionVisibilityAttribute.cs b/MiraAPI/GameOptions/Attributes/Delegate/ModdedOptionVisibilityAttribute.cs index 60e494d..1077e58 100644 --- a/MiraAPI/GameOptions/Attributes/Delegate/ModdedOptionVisibilityAttribute.cs +++ b/MiraAPI/GameOptions/Attributes/Delegate/ModdedOptionVisibilityAttribute.cs @@ -71,6 +71,32 @@ public sealed class ModdedOptionVisibilityAttribute(string? memberName = null, T targetMethod = vMethod; } + else if (member is FieldInfo field) + { + if (field.FieldType != typeof(bool)) + { + Error($"Field {MemberName} is not a bool."); + return null; + } + + var dynamicMethod = new DynamicMethod($"GetVisibility_{type.Name}_{field.Name}", typeof(bool), Type.EmptyTypes, type, true); + + var il = dynamicMethod.GetILGenerator(); + + if (instanceGetter != null) + { + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Call, instanceGetter); + il.Emit(OpCodes.Ldfld, field); + } + else + { + il.Emit(OpCodes.Ldsfld, field); + } + + il.Emit(OpCodes.Ret); + return dynamicMethod.CreateDelegate>(); + } else { Error($"Member {MemberName} of {type.FullName} is not valid for Visible function."); @@ -118,6 +144,7 @@ public sealed class ModdedOptionVisibilityAttribute(string? memberName = null, T } var indexParams = vProperty.GetIndexParameters(); + if (indexParams.Length > 1) { Error($"Indexer {MemberName} has too many parameters."); @@ -178,6 +205,43 @@ public sealed class ModdedOptionVisibilityAttribute(string? memberName = null, T targetMethod = vMethod; } + else if (member is FieldInfo field) + { + if (field.FieldType.IsAssignableTo(typeof(IList))) + { + postProcess = il => + { + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Callvirt, ListBoolIndexerGetter); + }; + } + else if (field.FieldType != typeof(bool)) + { + Error($"Field {MemberName} is not a bool or a list of bools."); + return null; + } + + var dynamicMethod = new DynamicMethod($"GetListVisibility_{type.Name}_{field.Name}", typeof(bool), [typeof(int)], type, true); + var il = dynamicMethod.GetILGenerator(); + + if (instanceGetter != null) + { + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Call, instanceGetter); + il.Emit(OpCodes.Ldfld, field); + } + else + { + il.Emit(OpCodes.Ldsfld, field); + } + + postProcess?.Invoke(il); + + il.Emit(OpCodes.Ret); + + var func = dynamicMethod.CreateDelegate>(); + return index => () => func(index); + } else { Error($"Member {MemberName} of {type.FullName} is not valid for Visibility function."); diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs index 764df9c..5d0b792 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs @@ -52,6 +52,10 @@ public class ModdedEnumOption : ModdedOption stringOption.SetUpFromData(Data, 20); stringOption.OnValueChanged = (Il2CppSystem.Action)ValueChanged; + // Changing the button text to make them more distinct from number options + stringOption.MinusBtn.ChangeButtonText("<"); + stringOption.PlusBtn.ChangeButtonText(">"); + // SetUpFromData method doesn't work correctly so we must set the values manually stringOption.Title = StringName; stringOption.Values = (Data as StringGameSetting)?.Values ?? new Il2CppStructArray(0);