/// <summary> /// Adds a <see cref="ModChoiceOption"/> to the <see cref="ModOptions"/> menu. /// </summary> /// <param name="id"></param> /// <param name="label"></param> /// <param name="memberInfoMetadata">The metadata of the corresponding member.</param> /// <param name="choiceAttribute">The defined or generated <see cref="ChoiceAttribute"/> of the member.</param> private void BuildModChoiceOption(string id, string label, MemberInfoMetadata <T> memberInfoMetadata, ChoiceAttribute choiceAttribute) { if (memberInfoMetadata.ValueType.IsEnum && (choiceAttribute.Options == null || !choiceAttribute.Options.Any())) { // Enum-based choice where the values are parsed from the enum type string[] options = Enum.GetNames(memberInfoMetadata.ValueType); string value = memberInfoMetadata.GetValue(ConfigFileMetadata.Config).ToString(); AddChoiceOption(id, label, options, value); } else if (memberInfoMetadata.ValueType.IsEnum) { // Enum-based choice where the values are defined as custom strings string[] options = choiceAttribute.Options; string name = memberInfoMetadata.GetValue(ConfigFileMetadata.Config).ToString(); int index = Math.Max(Array.IndexOf(Enum.GetNames(memberInfoMetadata.ValueType), name), 0); AddChoiceOption(id, label, options, index); } else if (memberInfoMetadata.ValueType == typeof(string)) { // string-based choice value string[] options = choiceAttribute.Options; string value = memberInfoMetadata.GetValue <string>(ConfigFileMetadata.Config); AddChoiceOption(id, label, options, value); } else if (memberInfoMetadata.ValueType == typeof(int)) { // index-based choice value string[] options = choiceAttribute.Options; int index = memberInfoMetadata.GetValue <int>(ConfigFileMetadata.Config); AddChoiceOption(id, label, options, index); } }
/// <summary> /// Adds a <see cref="ModSliderOption"/> to the <see cref="ModOptions"/> menu. /// </summary> /// <param name="id"></param> /// <param name="label"></param> /// <param name="memberInfoMetadata">The metadata of the corresponding member.</param> /// <param name="sliderAttribute">The defined or generated <see cref="SliderAttribute"/> of the member.</param> private void BuildModSliderOption(string id, string label, MemberInfoMetadata <T> memberInfoMetadata, SliderAttribute sliderAttribute) { float value = Convert.ToSingle(memberInfoMetadata.GetValue(ConfigFileMetadata.Config)); float step = sliderAttribute.Step; if (memberInfoMetadata.ValueType == typeof(int)) { step = Mathf.CeilToInt(step); } AddSliderOption(id, label, sliderAttribute.Min, sliderAttribute.Max, Convert.ToSingle(value), sliderAttribute.DefaultValue, sliderAttribute.Format, step); }
/// <summary> /// Gets the metadata of every <typeparamref name="TAttribute"/> defined for a member. /// </summary> /// <typeparam name="TAttribute"> /// The type of <see cref="ModOptionEventAttribute"/> attribute defined on the member to gather metadata for. /// </typeparam> /// <param name="memberInfo">The member to gather attribute metadata for.</param> /// <returns></returns> private IEnumerable <MemberInfoMetadata <T> > GetEventMetadata <TAttribute>(MemberInfo memberInfo) where TAttribute : ModOptionEventAttribute { var metadatas = new List <MemberInfoMetadata <T> >(); foreach (TAttribute attribute in memberInfo.GetCustomAttributes <TAttribute>(true)) { var methodMetadata = new MemberInfoMetadata <T> { MemberType = MemberType.Method, Name = attribute.MethodName }; methodMetadata.ParseMethodParameterTypes(); metadatas.Add(methodMetadata); } return(metadatas); }
/// <summary> /// Invoke the relevant method specified by a <see cref="ModOptionEventAttribute"/> /// and passes relevant parameters. /// </summary> /// <typeparam name="TSource">The type of the event args.</typeparam> /// <param name="memberInfoMetadata">The metadata for the method.</param> /// <param name="sender">The sender of the event.</param> /// <param name="e">The event args from the event.</param> private void InvokeEvent <TSource>(MemberInfoMetadata <T> memberInfoMetadata, object sender, TSource e) where TSource : IModOptionEventArgs { if (!memberInfoMetadata.MethodValid) { // Method not found, log error and skip. Logger.Error($"[OptionsMenuBuilder] Could not find the specified method: {typeof(T)}.{memberInfoMetadata.Name}"); return; } if (memberInfoMetadata.MethodParameterTypes is Type[] parameterTypes) { var parameters = new object[parameterTypes.Length]; var senderFound = false; var eventArgsFound = false; var modOptionEventFound = false; for (var i = 0; i < parameterTypes.Length; i++) { if (!senderFound && parameterTypes[i] == typeof(object)) { senderFound = true; parameters[i] = sender; } else if (!eventArgsFound && parameterTypes[i] == typeof(TSource)) { eventArgsFound = true; parameters[i] = e; } else if (!modOptionEventFound && parameterTypes[i] == typeof(IModOptionEventArgs)) { modOptionEventFound = true; parameters[i] = e; } if (senderFound && eventArgsFound && modOptionEventFound) { break; } } memberInfoMetadata.InvokeMethod(Config, parameters); } }
/// <summary> /// Sets the value in the <see cref="Config"/>, optionally saving the <see cref="Config"/> to disk if the /// <see cref="MenuAttribute.SaveEvents.ChangeValue"/> flag is set, before passing off to /// <see cref="InvokeOnChangeEvents{TSource}(ModOptionAttributeMetadata{T}, object, TSource)"/> /// to invoke any methods specified with an <see cref="OnChangeAttribute"/>. /// </summary> /// <param name="sender">The sender of the original slider changed event.</param> /// <param name="e">The <see cref="SliderChangedEventArgs"/> for the slider changed event.</param> public void HandleSliderChanged(object sender, SliderChangedEventArgs e) { if (TryGetMetadata(e.Id, out ModOptionAttributeMetadata <T> modOptionMetadata)) { // Set the value in the Config MemberInfoMetadata <T> memberInfoMetadata = modOptionMetadata.MemberInfoMetadata; object value = Convert.ChangeType(e.Value, memberInfoMetadata.ValueType); memberInfoMetadata.SetValue(Config, value); // Optionally save the Config to disk if (MenuAttribute.SaveOn.HasFlag(MenuAttribute.SaveEvents.ChangeValue)) { Config.Save(); } // Invoke any OnChange methods specified InvokeOnChangeEvents(modOptionMetadata, sender, e); } }
/// <summary> /// Sets the value in the <see cref="Config"/>, optionally saving the <see cref="Config"/> to disk if the /// <see cref="MenuAttribute.SaveEvents.ChangeValue"/> flag is set, before passing off to /// <see cref="InvokeOnChangeEvents{TSource}(ModOptionAttributeMetadata{T}, object, TSource)"/> /// to invoke any methods specified with an <see cref="OnChangeAttribute"/>. /// </summary> /// <param name="sender">The sender of the original choice changed event.</param> /// <param name="e">The <see cref="ChoiceChangedEventArgs"/> for the choice changed event.</param> public void HandleChoiceChanged(object sender, ChoiceChangedEventArgs e) { if (TryGetMetadata(e.Id, out ModOptionAttributeMetadata <T> modOptionMetadata)) { // Set the value in the Config MemberInfoMetadata <T> memberInfoMetadata = modOptionMetadata.MemberInfoMetadata; ChoiceAttribute choiceAttribute = modOptionMetadata.ModOptionAttribute as ChoiceAttribute; if (memberInfoMetadata.ValueType.IsEnum && (choiceAttribute.Options == null || !choiceAttribute.Options.Any())) { // Enum-based choice where the values are parsed from the enum type object value = Enum.Parse(memberInfoMetadata.ValueType, e.Value); memberInfoMetadata.SetValue(Config, value); } else if (memberInfoMetadata.ValueType.IsEnum) { // Enum-based choice where the values are defined as custom strings object value = Enum.Parse(memberInfoMetadata.ValueType, Enum.GetNames(memberInfoMetadata.ValueType)[e.Index]); memberInfoMetadata.SetValue(Config, value); } else if (memberInfoMetadata.ValueType == typeof(string)) { // string-based choice value string value = e.Value; memberInfoMetadata.SetValue(Config, value); } else if (memberInfoMetadata.ValueType == typeof(int)) { // index-based choice value int value = e.Index; memberInfoMetadata.SetValue(Config, value); } // Optionally save the Config to disk if (MenuAttribute.SaveOn.HasFlag(MenuAttribute.SaveEvents.ChangeValue)) { Config.Save(); } // Invoke any OnChange methods specified InvokeOnChangeEvents(modOptionMetadata, sender, e); } }
/// <summary> /// Invokes the relevant method(s) specified with the <see cref="OnChangeAttribute"/>(s) /// and passes parameters when a value is changed when loaded from disk. /// </summary> /// <param name="modOptionMetadata">The metadata for the mod option.</param> /// <param name="sender">The sender of the event.</param> private void InvokeOnChangeEvents(ModOptionAttributeMetadata <T> modOptionMetadata, object sender) { string id = modOptionMetadata.ModOptionAttribute.Id; MemberInfoMetadata <T> memberInfoMetadata = modOptionMetadata.MemberInfoMetadata; switch (modOptionMetadata.ModOptionAttribute) { case ChoiceAttribute choiceAttribute when memberInfoMetadata.ValueType.IsEnum && (choiceAttribute.Options == null || !choiceAttribute.Options.Any()): // Enum-based choice where the values are parsed from the enum type { string[] options = Enum.GetNames(memberInfoMetadata.ValueType); string value = memberInfoMetadata.GetValue(Config).ToString(); var eventArgs = new ChoiceChangedEventArgs(id, Array.IndexOf(options, value), value); InvokeOnChangeEvents(modOptionMetadata, sender, eventArgs); } break; case ChoiceAttribute _ when memberInfoMetadata.ValueType.IsEnum: // Enum-based choice where the values are defined as custom strings { string value = memberInfoMetadata.GetValue(Config).ToString(); int index = Math.Max(Array.IndexOf(Enum.GetValues(memberInfoMetadata.ValueType), value), 0); var eventArgs = new ChoiceChangedEventArgs(id, index, value); InvokeOnChangeEvents(modOptionMetadata, sender, eventArgs); } break; case ChoiceAttribute choiceAttribute when memberInfoMetadata.ValueType == typeof(string): // string-based choice value { string[] options = choiceAttribute.Options; string value = memberInfoMetadata.GetValue <string>(Config); var eventArgs = new ChoiceChangedEventArgs(id, Array.IndexOf(options, value), value); InvokeOnChangeEvents(modOptionMetadata, sender, eventArgs); } break; case ChoiceAttribute choiceAttribute when memberInfoMetadata.ValueType == typeof(int): // index-based choice value { string[] options = choiceAttribute.Options; int index = memberInfoMetadata.GetValue <int>(Config); var eventArgs = new ChoiceChangedEventArgs(id, index, options[index]); InvokeOnChangeEvents(modOptionMetadata, sender, eventArgs); } break; case KeybindAttribute _: { var eventArgs = new KeybindChangedEventArgs(id, memberInfoMetadata.GetValue <KeyCode>(Config)); InvokeOnChangeEvents(modOptionMetadata, sender, eventArgs); } break; case SliderAttribute _: { var eventArgs = new SliderChangedEventArgs(id, Convert.ToSingle(memberInfoMetadata.GetValue(Config))); InvokeOnChangeEvents(modOptionMetadata, sender, eventArgs); } break; case ToggleAttribute _: { var eventArgs = new ToggleChangedEventArgs(id, memberInfoMetadata.GetValue <bool>(Config)); InvokeOnChangeEvents(modOptionMetadata, sender, eventArgs); } break; } }
/// <summary> /// Adds a <see cref="ModToggleOption"/> to the <see cref="ModOptions"/> menu. /// </summary> /// <param name="id"></param> /// <param name="label"></param> /// <param name="memberInfoMetadata">The metadata of the corresponding member.</param> private void BuildModToggleOption(string id, string label, MemberInfoMetadata <T> memberInfoMetadata) { bool value = memberInfoMetadata.GetValue <bool>(ConfigFileMetadata.Config); AddToggleOption(id, label, value); }
/// <summary> /// Adds a <see cref="ModKeybindOption"/> to the <see cref="ModOptions"/> menu. /// </summary> /// <param name="id"></param> /// <param name="label"></param> /// <param name="memberInfoMetadata">The metadata of the corresponding member.</param> private void BuildModKeybindOption(string id, string label, MemberInfoMetadata <T> memberInfoMetadata) { KeyCode value = memberInfoMetadata.GetValue <KeyCode>(ConfigFileMetadata.Config); AddKeybindOption(id, label, GameInput.Device.Keyboard, value); }