diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedOptionList.cs b/MiraAPI/GameOptions/OptionTypes/ModdedOptionList.cs
index be14e14..4c61485 100644
--- a/MiraAPI/GameOptions/OptionTypes/ModdedOptionList.cs
+++ b/MiraAPI/GameOptions/OptionTypes/ModdedOptionList.cs
@@ -34,8 +34,10 @@ public abstract class ModdedOptionList : IModdedOptionList
///
public IReadOnlyList StringNames { get; }
+ protected readonly BaseGameSetting[] _data;
+
///
- public IReadOnlyList Data { get; protected set; } = [];
+ public IReadOnlyList Data => _data;
///
public IMiraPlugin? ParentMod
@@ -118,3 +120,146 @@ public abstract class ModdedOptionList : IModdedOptionList
PlayerOption playerOpt,
Transform container);
}
+
+///
+/// Represents a modded option list.
+///
+/// The value type.
+public abstract class ModdedOptionList : ModdedOptionList
+{
+ protected override void OnParentModChange()
+ {
+ var configFile = _parentMod!.GetConfigFile();
+ for (int i = 0; i < Count; i++)
+ {
+ var entry = configFile.Bind(ConfigDefinitions[i], DefaultValue(i));
+ Values[i] = entry.Value;
+ }
+ }
+
+ ///
+ /// Gets the list of values of the options.
+ ///
+ public T[] Values { get; }
+
+ ///
+ /// Gets the list of default value of the options.
+ ///
+ public Func DefaultValue { get; }
+
+ ///
+ /// Gets or sets the event that is invoked when the value of an option changes.
+ ///
+ public Action? ChangedEvent { get; set; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The options' title.
+ /// The option list's length.
+ /// The default values.
+ /// Whether to include the options in the preset.
+ /// Whether the first option's title index is 0, else 1.
+ protected ModdedOptionList(string title, int count, Func defaultValues, bool includeInPreset = true, bool zeroIndexTitle = false)
+ : base(title, count, includeInPreset, zeroIndexTitle)
+ {
+ DefaultValue = defaultValues;
+ Values = Enumerable.Range(0, Count).Select(defaultValues).ToArray();
+ }
+
+ internal void ValueChanged(int idx, OptionBehaviour optionBehaviour)
+ {
+ SetValue(idx, GetValueFromOptionBehaviour(idx, optionBehaviour));
+ }
+
+ ///
+ /// Sets the value of the option.
+ ///
+ /// The option's index.
+ /// The new value.
+ /// Whether to send the value to other players.
+ public void SetValue(int idx, T newValue, bool sendRpc = true)
+ {
+ var oldVal = Values[idx];
+ Values[idx] = newValue;
+
+ if (Values[idx]?.Equals(oldVal) == false)
+ {
+ ChangedEvent?.Invoke(idx, Values[idx]);
+ }
+
+ if (sendRpc && AmongUsClient.Instance.AmHost)
+ {
+ if (ParentMod?.GetConfigFile().TryGetEntry(ConfigDefinitions[idx], out var entry) == true)
+ {
+ entry.Value = Values[idx];
+ }
+
+ Rpc.Instance.Send(PlayerControl.LocalPlayer, [GetNetData(idx)], true); // This might not work
+ }
+
+ OnValueChanged(idx, newValue);
+ }
+
+ ///
+ public override void SaveToPreset(ConfigFile presetConfig, bool saveDefault = false)
+ {
+ if (ConfigDefinitions.Any(d => d is null))
+ {
+ Error($"Attempted to save {Title} to preset, but some ConfigDefinitions are null.");
+ return;
+ }
+ Bind(presetConfig);
+ for (int i = 0; i < Count; i++)
+ {
+ presetConfig[ConfigDefinitions[i]].BoxedValue = saveDefault ? DefaultValue(i) : Values[i];
+ }
+ }
+
+ ///
+ public override void Bind(ConfigFile config)
+ {
+ for (int i = 0; i < Count; i++)
+ {
+ config.Bind(ConfigDefinitions[i], DefaultValue(i));
+ }
+ }
+
+ ///
+ public override void LoadFromPreset(ConfigFile presetConfig)
+ {
+ for (int i = 0; i < Count; i++)
+ {
+ if (presetConfig.TryGetEntry(ConfigDefinitions[i], out ConfigEntry entry))
+ {
+ SetValue(i, entry.Value, false);
+ }
+ else
+ {
+ Error($"Attempted to load {Title} from preset, but ConfigDefinition {i} is not found in preset.");
+ }
+ }
+ }
+
+ ///
+ /// Handles the value changed event.
+ ///
+ /// The option's index.
+ /// The new value.
+ protected abstract void OnValueChanged(int idx, T newValue);
+
+ ///
+ /// Gets the value from the option behaviour.
+ ///
+ /// The option's index.
+ /// The OptionBehaviour.
+ /// The value.
+ public abstract T GetValueFromOptionBehaviour(int idx, OptionBehaviour optionBehaviour);
+
+ ///
+ /// Indexes the option for its value of type .
+ ///
+ /// The option's index.
+ /// value of type .
+ public T this[int idx] => Values[idx];
+}