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]; +}