From 02f9c208040488691ea1284e3d994dfd2fd88f95 Mon Sep 17 00:00:00 2001
From: Atony <75191600+AtonyGit@users.noreply.github.com>
Date: Sat, 7 Mar 2026 15:23:26 -0600
Subject: [PATCH] Add wip string option
---
Directory.Build.props | 4 +-
MiraAPI/GameOptions/ModdedOptionsManager.cs | 1 +
.../OptionTypes/ModdedStringOption.cs | 183 ++++++++++++++++++
3 files changed, 186 insertions(+), 2 deletions(-)
create mode 100644 MiraAPI/GameOptions/OptionTypes/ModdedStringOption.cs
diff --git a/Directory.Build.props b/Directory.Build.props
index 4b87d91..8fad4d6 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -8,8 +8,8 @@
true
- 0.3.8
- dev
+ 0.3.9
+ wip
All Of Us, XtraCube, Angxl
All Of Us
diff --git a/MiraAPI/GameOptions/ModdedOptionsManager.cs b/MiraAPI/GameOptions/ModdedOptionsManager.cs
index 3b85a73..8045e3f 100644
--- a/MiraAPI/GameOptions/ModdedOptionsManager.cs
+++ b/MiraAPI/GameOptions/ModdedOptionsManager.cs
@@ -24,6 +24,7 @@ public static class ModdedOptionsManager
private static readonly Dictionary TypeToGroup = [];
internal static readonly Dictionary CreatedPlayerOptions = [];
+ internal static readonly Dictionary CreatedStringOptions = [];
internal static readonly Dictionary ModdedOptions = [];
internal static readonly List Groups = [];
diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedStringOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedStringOption.cs
new file mode 100644
index 0000000..70206db
--- /dev/null
+++ b/MiraAPI/GameOptions/OptionTypes/ModdedStringOption.cs
@@ -0,0 +1,183 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using HarmonyLib;
+using Il2CppInterop.Runtime.InteropTypes.Arrays;
+using MiraAPI.Networking;
+using Reactor.Localization.Utilities;
+using UnityEngine;
+using Object = UnityEngine.Object;
+
+namespace MiraAPI.GameOptions.OptionTypes;
+
+///
+/// An option for selecting an enum value.
+///
+public class ModdedStringOption : ModdedOption
+{
+ ///
+ /// Gets or sets the string values of the option.
+ ///
+ public string[] Values { get; set; }
+
+ ///
+ /// Gets or sets the String Name values of the option.
+ ///
+ public StringNames[] StringNameValues { get; set; }
+
+ ///
+ /// Gets the string's index
+ ///
+ /// The option's string value.
+ /// The option's value in the index.
+ public int GetIndex(string value, int location)
+ {
+ if (Values.Contains(value))
+ {
+ return Values.ToList().IndexOf(value);
+ }
+ Info($"Cannot find a valid index for {value} in {location}. Returning 0 instead. Values: {string.Join(", ", Values)}");
+ return 0;
+ }
+
+ ///
+ /// Adds a value to the option.
+ ///
+ /// The option's string value.
+ /// The additional value's index on the list.
+ public void RegisterExtraValue(string value, int index = -1)
+ {
+ if (!Values.Contains(value))
+ {
+ if (index <= -1)
+ {
+ Values.AddItem(value);
+ StringNameValues.AddItem(CustomStringName.CreateAndRegister(value));
+ }
+ else
+ {
+ var newVals = Values.ToList();
+ newVals.Insert(index, value);
+ Values = newVals.ToArray();
+ var newStrVals = StringNameValues.ToList();
+ newStrVals.Insert(index, CustomStringName.CreateAndRegister(value));
+ StringNameValues = newStrVals.ToArray();
+ }
+ }
+
+ if (Data != null)
+ {
+ var data = (StringGameSetting)Data;
+ data.Values = StringNameValues;
+
+ data.Index = GetIndex(Value, 1);
+ if (OptionBehaviour != null)
+ {
+ var strOpt = OptionBehaviour.TryCast();
+ if (strOpt != null)
+ {
+ strOpt.Values = StringNameValues;
+ strOpt.Value = GetIndex(Value, 2);
+ Warning("StringOption is valid");
+ }
+ else
+ {
+ Error("StringOption is null.");
+ }
+ Warning("OptionBehaviour is valid");
+ }
+ else
+ {
+ Error("OptionBehaviour is null.");
+ }
+ Warning("Data is valid");
+ }
+ else
+ {
+ Error("Data is null.");
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The title of the option.
+ /// The default value as a string.
+ /// An option list of string values to use.
+ /// Whether to include this option in the preset or not.
+ public ModdedStringOption(string title, string defaultValue, string[] values, bool includeInPreset=true) : base(title, defaultValue, includeInPreset)
+ {
+ Values = values;
+ Data = ScriptableObject.CreateInstance();
+ var data = (StringGameSetting)Data;
+
+ data.Title = StringName;
+ data.Type = global::OptionTypes.String;
+ StringNameValues = values.Select(CustomStringName.CreateAndRegister).ToArray();
+ data.Values = StringNameValues;
+
+ data.Index = GetIndex(Value, 3);
+ }
+
+ ///
+ public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, PlayerOption playerOpt, Transform container)
+ {
+ var stringOption = Object.Instantiate(stringOpt, container);
+ stringOption.name =
+ $"{ParentMod!.OptionsTitleText}.StringOption.{TranslationController.Instance.GetString(StringName)}";
+
+ stringOption.SetUpFromData(Data, 20);
+ stringOption.OnValueChanged = (Il2CppSystem.Action)ValueChanged;
+
+ // SetUpFromData method doesnt work correctly so we must set the values manually
+ stringOption.Title = StringName;
+ stringOption.Values = (Data as StringGameSetting)?.Values ?? new Il2CppStructArray(0);
+ stringOption.Value = GetIndex(Value, 4);
+
+ OptionBehaviour = stringOption;
+ ModdedOptionsManager.CreatedStringOptions.TryAdd(stringOption, this);
+
+ return stringOption;
+ }
+
+ ///
+ public override float GetFloatData()
+ {
+ return Convert.ToSingle(Value, NumberFormatInfo.InvariantInfo);
+ }
+
+ ///
+ public override NetData GetNetData()
+ {
+ return new NetData(Id, Encoding.Unicode.GetBytes(Convert.ToString(Value, NumberFormatInfo.InvariantInfo)!));
+ }
+
+ ///
+ public override void HandleNetData(byte[] data)
+ {
+ SetValue(Encoding.Unicode.GetString(data));
+ }
+
+ ///
+ public override string GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour)
+ {
+ return Values[optionBehaviour.GetInt()];
+ }
+
+ ///
+ protected override void OnValueChanged(string newValue)
+ {
+ HudManager.Instance.Notifier.AddSettingsChangeMessage(StringName, Data.GetValueString(GetIndex(newValue, 5)), false);
+ if (!OptionBehaviour)
+ {
+ return;
+ }
+
+ if (OptionBehaviour is StringOption opt)
+ {
+ opt.Value = GetIndex(newValue, 5);
+ }
+ }
+}