options patches overhaul

This commit is contained in:
XtraCube committed 2026-08-04 00:01:21 -04:00
1 parent edee889ffe
commit 4ca2249fbd
14 files changed
+1165 -1136

No files matched your search

@@ -10,6 +10,8 @@ public class ExampleOptions2 : AbstractOptionGroup
public override uint GroupPriority => 0; // This group will be displayed first. The default value is uint.MaxValue.
public override MenuCategory ParentMenu => MenuCategory.CustomTwo;
public ModdedPlayerOption PlayerOption2 { get; } = new("YOU MUST CHOSE A PLAYER", false);
public ModdedPlayerOption PlayerOption { get; } = new("Eh whatever");
public ModdedToggleOption ToggleOpt1 { get; } = new("Toggle Option 1", false);
+6 -1
View File
@@ -69,7 +69,7 @@ public abstract class AbstractOptionGroup
internal CategoryHeaderMasked? Header { get; set; }
internal Coroutine? CreationCoroutine { get; set; }
internal bool Ready { get; set; }
}
/// <summary>
@@ -132,6 +132,11 @@ public abstract class AbstractRoleOptionGroup<T>() : AbstractOptionGroup<T> wher
/// </summary>
public enum MenuCategory
{
/// <summary>
/// Placeholder for indexing purposes. Options don't exist in the presset tab.
/// </summary>
Preset,
/// <summary>
/// Determines the option group is in the game settings tab.
/// </summary>
@@ -254,12 +254,6 @@ public static class TaskAdderPatches
.ToArray()
.Any(x => x.FolderName == child.FolderName);
[HarmonyPostfix]
[HarmonyPatch(nameof(TaskAdderGame.OnDisable))]
public static void ClosePatch()
{
Utilities.Extensions.ClearGarbageCollector();
}
// yes it might be crazy patching the entire method, but i tried so many other methods and only this works :cry:
// true -chip
[HarmonyPrefix]
-1
View File
@@ -150,7 +150,6 @@ public static class HudManagerPatches
{
t.gameObject.Destroy();
}
Utilities.Extensions.ClearGarbageCollector();
var gridArrange = BottomLeft.GetComponent<GridArrange>();
var aspectPosition = BottomLeft.GetComponent<AspectPosition>();
+161 -186
View File
@@ -21,6 +21,54 @@ namespace MiraAPI.Patches.Options;
[HarmonyPatch(typeof(GameOptionsMenu))]
internal static class GameOptionsMenuPatch
{
[HarmonyPrefix]
[HarmonyPatch(nameof(GameOptionsMenu.Initialize))]
// ReSharper disable once InconsistentNaming
public static bool InitPatch(GameOptionsMenu __instance)
{
__instance.Children ??= new Il2CppSystem.Collections.Generic.List<OptionBehaviour>();
__instance.Children.Clear();
if (MenuState.Instance.CurrentModIdx == 0)
{
__instance.MapPicker.gameObject.SetActive(true);
__instance.cachedData = GameOptionsManager.Instance.CurrentGameOptions;
var container = MenuState.Instance.CurrentContainer.transform;
if (container.childCount == 1)
{
__instance.MapPicker.Initialize(20);
var mapNameSetting = GameManager.Instance.GameSettingsList.MapNameSetting;
__instance.MapPicker.SetUpFromData(mapNameSetting, 20);
__instance.Children.Add(__instance.MapPicker);
__instance.CreateSettings();
foreach (var optionBehaviour in __instance.Children)
{
if (AmongUsClient.Instance && !AmongUsClient.Instance.AmHost)
{
optionBehaviour.SetAsPlayer();
}
optionBehaviour.OnValueChanged = new Action<OptionBehaviour>(__instance.ValueChanged);
}
}
else
{
foreach (var child in container.GetComponentsInChildren<OptionBehaviour>(true))
{
__instance.Children.Add(child);
}
}
}
else
{
__instance.MapPicker.gameObject.SetActive(false);
CustomCreateSettings(__instance);
}
return false;
}
[HarmonyPostfix]
[HarmonyPatch(nameof(GameOptionsMenu.OnEnable))]
public static void OpenPatch(GameOptionsMenu __instance)
@@ -35,47 +83,41 @@ internal static class GameOptionsMenuPatch
HudManager.Instance.PlayerCam.OverrideScreenShakeEnabled = true;
}
[HarmonyPostfix]
[HarmonyPatch(nameof(GameOptionsMenu.CloseMenu))]
public static void FullMenuClosePatch(GameOptionsMenu __instance)
{
Utilities.Extensions.ClearGarbageCollector();
}
[HarmonyPostfix]
[HarmonyPatch(nameof(GameOptionsMenu.Update))]
// ReSharper disable once InconsistentNaming
public static void UpdatePatch(GameOptionsMenu __instance)
{
if (GameSettingMenuPatches.SelectedModIdx == 0)
if (MenuState.Instance.CurrentModIdx == 0)
{
return;
}
var num = 2.1f;
if (__instance.name == "MODIFIERS TAB")
{
ModifiersUpdate(ref num);
}
else if (__instance.name == "CUSTOM TAB 1")
{
CustomMenuOneUpdate(ref num);
}
else if (__instance.name == "CUSTOM TAB 2")
{
CustomMenuTwoUpdate(ref num);
}
else
{
var filteredGroups =
GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
.Where(x => x.OptionableType == null &&
x.ParentMenu == MenuCategory.Game) ?? [];
foreach (var group in filteredGroups)
{
UpdateGroup(group, ref num);
}
switch (MenuState.Instance.CurrentMenu)
{
case MenuCategory.Modifiers:
ModifiersUpdate(ref num);
break;
case MenuCategory.CustomOne:
CustomMenuOneUpdate(ref num);
break;
case MenuCategory.CustomTwo:
CustomMenuTwoUpdate(ref num);
break;
default:
var filteredGroups =
MenuState.Instance.CurrentMod.InternalOptionGroups
.Where(x => x.OptionableType == null &&
x.ParentMenu == MenuCategory.Game) ?? [];
foreach (var group in filteredGroups)
{
UpdateGroup(group, ref num);
}
break;
}
__instance.scrollBar.SetYBoundsMax(-num - 1.65f);
@@ -83,7 +125,7 @@ internal static class GameOptionsMenuPatch
private static void UpdateGroup(AbstractOptionGroup? group, ref float num)
{
if (group is null || group.Options.Count == 0 || group.Header is null || group.CreationCoroutine is not null)
if (group is null || !group.Ready || group.Options.Count == 0 || group.Header is null)
{
return;
}
@@ -135,8 +177,8 @@ internal static class GameOptionsMenuPatch
private static void CustomMenuOneUpdate(ref float num)
{
var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
.Where(x => x.ParentMenu == MenuCategory.CustomOne) ?? [];
var groups = MenuState.Instance.CurrentMod.InternalOptionGroups
.Where(x => x.ParentMenu == MenuCategory.CustomOne);
foreach (var modGroup in groups)
{
@@ -144,20 +186,10 @@ internal static class GameOptionsMenuPatch
}
}
private static void CustomMenuOneCreate(GameOptionsMenu menu)
{
var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
.Where(x => x.ParentMenu == MenuCategory.CustomOne) ?? [];
foreach (var group in groups)
{
CreateGroup(menu, group);
}
}
private static void CustomMenuTwoUpdate(ref float num)
{
var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
.Where(x => x.ParentMenu == MenuCategory.CustomTwo) ?? [];
var groups = MenuState.Instance.CurrentMod.InternalOptionGroups
.Where(x => x.ParentMenu == MenuCategory.CustomTwo);
foreach (var modGroup in groups)
{
@@ -165,21 +197,11 @@ internal static class GameOptionsMenuPatch
}
}
private static void CustomMenuTwoCreate(GameOptionsMenu menu)
{
var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
.Where(x => x.ParentMenu == MenuCategory.CustomTwo) ?? [];
foreach (var group in groups)
{
CreateGroup(menu, group);
}
}
private static void ModifiersUpdate(ref float num)
{
var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
var groups = MenuState.Instance.CurrentMod.InternalOptionGroups
.Where(x => x.ParentMenu is MenuCategory.Modifiers ||
x.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true) ?? [];
x.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true);
foreach (var modGroup in groups)
{
@@ -187,132 +209,109 @@ internal static class GameOptionsMenuPatch
}
}
private static void ModifiersCreate(GameOptionsMenu menu)
private static Coroutine? _creationCoroutine;
public static void CustomCreateSettings(GameOptionsMenu menu)
{
var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
.Where(x => x.ParentMenu is MenuCategory.Modifiers ||
x.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true) ?? [];
foreach (var group in groups)
{
CreateGroup(menu, group);
}
}
menu.MapPicker.gameObject.SetActive(false);
var mod = MenuState.Instance.CurrentMod;
[HarmonyPrefix]
[HarmonyPatch(nameof(GameOptionsMenu.CreateSettings))]
// ReSharper disable once InconsistentNaming
public static bool SettingsPatch(GameOptionsMenu __instance)
{
if (GameSettingMenuPatches.SelectedModIdx == 0)
var filteredGroups = MenuState.Instance.CurrentMenu switch
{
return true;
MenuCategory.Game => mod.InternalOptionGroups.Where(x =>
x.OptionableType == null && x.ParentMenu == MenuCategory.Game),
MenuCategory.Modifiers => mod.InternalOptionGroups.Where(x =>
x.ParentMenu is MenuCategory.Modifiers ||
x.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true),
MenuCategory.CustomOne => mod.InternalOptionGroups.Where(x => x.ParentMenu == MenuCategory.CustomOne),
MenuCategory.CustomTwo => mod.InternalOptionGroups.Where(x => x.ParentMenu == MenuCategory.CustomTwo),
_ => [],
};
if (_creationCoroutine != null)
{
GameSettingMenu.Instance.StopCoroutine(_creationCoroutine);
}
__instance.MapPicker.gameObject.SetActive(false);
_creationCoroutine = GameSettingMenu.Instance.StartCoroutine(CreateGroups().WrapToIl2Cpp());
switch (__instance.name)
IEnumerator CreateGroups()
{
case "MODIFIERS TAB":
ModifiersCreate(__instance);
return false;
case "CUSTOM TAB 1":
CustomMenuOneCreate(__instance);
return false;
case "CUSTOM TAB 2":
CustomMenuTwoCreate(__instance);
return false;
}
var filteredGroups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
.Where(x => x.OptionableType == null && x.ParentMenu == MenuCategory.Game) ?? [];
foreach (var group in filteredGroups)
{
CreateGroup(__instance, group);
}
return false;
}
[HarmonyPrefix]
[HarmonyPatch(nameof(GameOptionsMenu.Initialize))]
// ReSharper disable once InconsistentNaming
public static bool InitPatch(GameOptionsMenu __instance)
{
if (__instance.Children != null && __instance.Children.Count != 0)
{
return false;
}
__instance.MapPicker.gameObject.SetActive(true);
__instance.MapPicker.Initialize(20);
var mapNameSetting = GameManager.Instance.GameSettingsList.MapNameSetting;
__instance.MapPicker.SetUpFromData(mapNameSetting, 20);
__instance.Children = new Il2CppSystem.Collections.Generic.List<OptionBehaviour>();
__instance.Children.Add(__instance.MapPicker);
__instance.CreateSettings();
__instance.cachedData = GameOptionsManager.Instance.CurrentGameOptions;
foreach (var optionBehaviour in __instance.Children)
{
if (AmongUsClient.Instance && !AmongUsClient.Instance.AmHost)
foreach (var group in filteredGroups)
{
optionBehaviour.SetAsPlayer();
yield return CoCreateGroup(menu, group);
}
if (optionBehaviour.IsCustom())
{
continue;
}
optionBehaviour.OnValueChanged = new Action<OptionBehaviour>(__instance.ValueChanged);
}
return false;
}
private static IEnumerator CoCreateGroup(GameOptionsMenu menu, AbstractOptionGroup group)
{
var categoryHeaderMasked = Object.Instantiate(
menu.categoryHeaderOrigin,
Vector3.zero,
Quaternion.identity,
menu.settingsContainer);
var container = MenuState.Instance.CurrentContainer.transform;
categoryHeaderMasked.SetHeader(CustomStringName.CreateAndRegister(group.GroupName), 20);
categoryHeaderMasked.Background.color = group.GroupColor;
categoryHeaderMasked.Divider.color = group.GroupColor;
categoryHeaderMasked.Title.color = group.GroupColor.Equals(MiraApiPlugin.DefaultHeaderColor)
? Color.white
: group.GroupColor.FindAlternateColor();
group.Ready = false;
if (group.Header == null || !group.Header)
{
var categoryHeaderMasked = Object.Instantiate(
menu.categoryHeaderOrigin,
Vector3.zero,
Quaternion.identity,
container);
categoryHeaderMasked.Background.sprite = MiraAssets.CategoryHeader.LoadAsset();
categoryHeaderMasked.Background.sprite.texture.filterMode = FilterMode.Bilinear;
categoryHeaderMasked.Background.sprite.texture.wrapMode = TextureWrapMode.Clamp;
categoryHeaderMasked.SetHeader(CustomStringName.CreateAndRegister(group.GroupName), 20);
categoryHeaderMasked.Background.color = group.GroupColor;
categoryHeaderMasked.Divider.color = group.GroupColor;
categoryHeaderMasked.Title.color = group.GroupColor.Equals(MiraApiPlugin.DefaultHeaderColor)
? Color.white
: group.GroupColor.FindAlternateColor();
categoryHeaderMasked.Background.transform.localPosition = new Vector3(0.5f, -0.1833f, 0);
categoryHeaderMasked.Background.size = new Vector2(
categoryHeaderMasked.Background.size.x + 1.5f,
categoryHeaderMasked.Background.size.y);
categoryHeaderMasked.Background.sprite = MiraAssets.CategoryHeader.LoadAsset();
categoryHeaderMasked.Background.sprite.texture.filterMode = FilterMode.Bilinear;
categoryHeaderMasked.Background.sprite.texture.wrapMode = TextureWrapMode.Clamp;
categoryHeaderMasked.gameObject.SetActive(false);
group.Header = categoryHeaderMasked;
categoryHeaderMasked.Background.transform.localPosition = new Vector3(0.5f, -0.1833f, 0);
categoryHeaderMasked.Background.size = new Vector2(
categoryHeaderMasked.Background.size.x + 1.5f,
categoryHeaderMasked.Background.size.y);
var newText = Object.Instantiate(categoryHeaderMasked.Title, categoryHeaderMasked.transform);
newText.text = "<size=70%>(Click to close)</size>";
newText.transform.localPosition = new Vector3(2.6249f, -0.165f, 0f);
newText.gameObject.GetComponent<TextTranslatorTMP>().Destroy();
categoryHeaderMasked.gameObject.SetActive(false);
var options = group.Options.Select(opt => opt.CreateOption(
var newText = Object.Instantiate(categoryHeaderMasked.Title, categoryHeaderMasked.transform);
newText.text = "<size=70%>(Click to close)</size>";
newText.transform.localPosition = new Vector3(2.6249f, -0.165f, 0f);
newText.gameObject.GetComponent<TextTranslatorTMP>().Destroy();
var boxCol = categoryHeaderMasked.gameObject.AddComponent<BoxCollider2D>();
boxCol.size = new Vector2(7, 0.7f);
boxCol.offset = new Vector2(1.5f, -0.3f);
var headerBtn = categoryHeaderMasked.gameObject.AddComponent<PassiveButton>();
headerBtn.ClickSound = menu.BackButton.GetComponent<PassiveButton>().ClickSound;
headerBtn.OnMouseOver = new UnityEvent();
headerBtn.OnMouseOut = new UnityEvent();
headerBtn.OnClick.AddListener(
(UnityAction)(() =>
{
group.AllOptionsHidden = !group.AllOptionsHidden;
newText.text = group.AllOptionsHidden
? "<size=70%>(Click to open)</size>"
: "<size=70%>(Click to close)</size>";
}));
headerBtn.SetButtonEnableState(true);
group.Header = categoryHeaderMasked;
}
var options = group.Options.Where(opt => opt.OptionBehaviour == null || !opt.OptionBehaviour
).Select(opt => opt.CreateOption(
menu.checkboxOrigin,
menu.numberOptionOrigin,
menu.stringOptionOrigin,
menu.playerOptionOrigin,
menu.settingsContainer));
container)
);
OptionPreset? defaultPreset = null;
if (GameSettingMenuPatches.SelectedMod != null && PresetManager.DefaultPresets.TryGetValue(
GameSettingMenuPatches.SelectedMod,
out var preset))
if (PresetManager.DefaultPresets.TryGetValue(MenuState.Instance.CurrentMod, out var preset))
{
defaultPreset = preset;
}
@@ -428,36 +427,12 @@ internal static class GameOptionsMenuPatch
newOpt.Initialize();
newOpt.gameObject.SetActive(false);
}).WrapToIl2Cpp();
var boxCol = categoryHeaderMasked.gameObject.AddComponent<BoxCollider2D>();
boxCol.size = new Vector2(7, 0.7f);
boxCol.offset = new Vector2(1.5f, -0.3f);
var headerBtn = categoryHeaderMasked.gameObject.AddComponent<PassiveButton>();
headerBtn.ClickSound = menu.BackButton.GetComponent<PassiveButton>().ClickSound;
headerBtn.OnMouseOver = new UnityEvent();
headerBtn.OnMouseOut = new UnityEvent();
headerBtn.OnClick.AddListener(
(UnityAction)(() =>
if (AmongUsClient.Instance && !AmongUsClient.Instance.AmHost)
{
group.AllOptionsHidden = !group.AllOptionsHidden;
newText.text = group.AllOptionsHidden
? "<size=70%>(Click to open)</size>"
: "<size=70%>(Click to close)</size>";
}));
headerBtn.SetButtonEnableState(true);
group.CreationCoroutine = null;
}
private static void CreateGroup(GameOptionsMenu menu, AbstractOptionGroup group)
{
if (group.CreationCoroutine != null)
{
GameSettingMenu.Instance.StopCoroutine(group.CreationCoroutine);
}
group.CreationCoroutine = GameSettingMenu.Instance.StartCoroutine(CoCreateGroup(menu, group).WrapToIl2Cpp());
newOpt.SetAsPlayer();
}
}).WrapToIl2Cpp();
group.Ready = true;
}
}
@@ -3,6 +3,7 @@ using System.IO;
using System.Linq;
using BepInEx.Configuration;
using HarmonyLib;
using MiraAPI.GameOptions;
using MiraAPI.PluginLoading;
using MiraAPI.Presets;
using MiraAPI.Roles;
@@ -23,8 +24,6 @@ internal static class GamePresetsTabPatches
private static GameObject _refreshButton = null!;
private static GameObject _folderButton = null!;
private static GameObject _newDivider = null!;
private static GameObject _presetHolder = null!;
private static GridArrange _arrange = null!;
private static readonly object Lock = new();
@@ -41,10 +40,10 @@ internal static class GamePresetsTabPatches
CreatePresetMenu(__instance);
__instance.StandardPresetButton.gameObject.SetActive(GameSettingMenuPatches.SelectedModIdx == 0);
__instance.SecondPresetButton.gameObject.SetActive(GameSettingMenuPatches.SelectedModIdx == 0);
__instance.StandardPresetButton.gameObject.SetActive(MenuState.Instance.CurrentModIdx == 0);
__instance.SecondPresetButton.gameObject.SetActive(MenuState.Instance.CurrentModIdx == 0);
if (GameSettingMenuPatches.SelectedModIdx != 0)
if (MenuState.Instance.CurrentModIdx != 0)
{
__instance.PresetDescriptionText.text = "Select or create an options preset for this mod";
}
@@ -56,26 +55,41 @@ internal static class GamePresetsTabPatches
// Show the save and refresh buttons
if (_saveButton)
{
_saveButton.SetActive(GameSettingMenuPatches.SelectedModIdx != 0);
_saveButton.SetActive(MenuState.Instance.CurrentModIdx != 0);
}
if (_refreshButton)
{
_refreshButton.SetActive(GameSettingMenuPatches.SelectedModIdx != 0);
_refreshButton.SetActive(MenuState.Instance.CurrentModIdx != 0);
}
if (_folderButton)
{
_folderButton.SetActive(GameSettingMenuPatches.SelectedModIdx != 0);
_folderButton.SetActive(MenuState.Instance.CurrentModIdx != 0);
}
if (_newDivider)
{
_newDivider.SetActive(GameSettingMenuPatches.SelectedModIdx != 0);
_newDivider.SetActive(MenuState.Instance.CurrentModIdx != 0);
}
if (MenuState.Instance.CurrentModIdx == 0)
{
return;
}
var prefab = GameSettingMenu.Instance.GameSettingsButton;
foreach (var preset in MiraPluginManager.Instance.RegisteredPlugins.SelectMany(x => x.InternalPresets))
var presetHolder = MenuState.Instance.CurrentContainer;
var arrange = presetHolder.GetComponent<GridArrange>();
presetHolder.SetActive(true);
foreach (var preset in MenuState.Instance.CurrentMod.InternalPresets)
{
var button = Object.Instantiate(prefab, _presetHolder.transform);
if (preset.PresetButton != null && preset.PresetButton)
{
preset.PresetButton.SetActive(true);
continue;
}
var button = Object.Instantiate(prefab, presetHolder.transform);
button.buttonText.text = preset.Name;
if (button.buttonText.TryGetComponent<TextTranslatorTMP>(out var tmp))
{
@@ -90,23 +104,12 @@ internal static class GamePresetsTabPatches
{
preset.LoadPreset();
}));
button.gameObject.SetActive(false);
button.gameObject.SetActive(true);
preset.PresetButton = button.gameObject;
}
foreach (var mod in MiraPluginManager.Instance.RegisteredPlugins)
{
foreach (var button in mod.InternalPresets.Select(x => x.PresetButton))
{
if (button != null)
{
button.SetActive(mod == GameSettingMenuPatches.SelectedMod);
}
}
}
_arrange.Start();
_arrange.ArrangeChilds();
arrange.Start();
arrange.ArrangeChilds();
}
}
@@ -116,8 +119,6 @@ internal static class GamePresetsTabPatches
// ReSharper disable once InconsistentNaming
public static void Postfix()
{
Error("OnDisable called");
if (_saveButton)
{
_saveButton.SetActive(false);
@@ -197,7 +198,7 @@ internal static class GamePresetsTabPatches
saveButton.OnClick.AddListener(
(UnityAction)(() =>
{
if (GameSettingMenuPatches.SelectedModIdx == 0)
if (MenuState.Instance.CurrentModIdx == 0)
{
return;
}
@@ -205,7 +206,7 @@ internal static class GamePresetsTabPatches
SavePresetPopup.CreatePopup(
name =>
{
if (GameSettingMenuPatches.SelectedMod == null)
if (MenuState.Instance.CurrentModIdx == 0)
{
return;
}
@@ -218,19 +219,19 @@ internal static class GamePresetsTabPatches
var presetFile = new ConfigFile(
Path.Combine(
PresetManager.PresetDirectory,
GameSettingMenuPatches.SelectedMod.PluginId,
MenuState.Instance.CurrentMod.PluginId,
$"{name}.cfg"),
false)
{
SaveOnConfigSet = false,
};
foreach (var option in GameSettingMenuPatches.SelectedMod.InternalOptions.Where(x => x.IncludeInPreset))
foreach (var option in MenuState.Instance.CurrentMod.InternalOptions.Where(x => x.IncludeInPreset))
{
option.SaveToPreset(presetFile);
}
foreach (var role in GameSettingMenuPatches.SelectedMod.InternalRoles.Values.OfType<ICustomRole>().Where(x=>!x.Configuration.HideSettings))
foreach (var role in MenuState.Instance.CurrentMod.InternalRoles.Values.OfType<ICustomRole>().Where(x=>!x.Configuration.HideSettings))
{
role.SaveToPreset(presetFile);
}
@@ -285,7 +286,7 @@ internal static class GamePresetsTabPatches
{
var directory = Path.Combine(
PresetManager.PresetDirectory,
GameSettingMenuPatches.SelectedMod?.PluginId ?? string.Empty);
MenuState.Instance.CurrentMod.PluginId);
Directory.CreateDirectory(directory);
Process.Start(
new ProcessStartInfo
@@ -301,31 +302,20 @@ internal static class GamePresetsTabPatches
_refreshButton.SetActive(false);
_folderButton.SetActive(false);
_newDivider.SetActive(false);
if (!_presetHolder)
{
_presetHolder = new GameObject("PresetHolder");
_presetHolder.transform.SetParent(presetTab.transform, false);
_presetHolder.transform.localScale = new Vector3(0.9f, 0.9f, 1);
_presetHolder.transform.localPosition = new Vector3(-1.9f, 1.7f, 0);
_arrange = _presetHolder.AddComponent<GridArrange>();
_arrange.Alignment = GridArrange.StartAlign.Right;
_arrange.CellSize = new Vector2(2.15f, -.55f);
_arrange.MaxColumns = 2;
}
}
private static void Refresh()
{
Error("Refreshing presets");
if (GameSettingMenuPatches.SelectedMod == null)
Info("Refreshing presets");
if (MenuState.Instance.CurrentModIdx == 0)
{
Error("Selected mod is null, cannot refresh presets");
Error("Current page is vanilla, cannot refresh presets");
return;
}
lock (Lock)
{
PresetManager.LoadPresets(GameSettingMenuPatches.SelectedMod);
PresetManager.LoadPresets(MenuState.Instance.CurrentMod);
}
GameSettingMenu.Instance.ChangeTab(0, false); // refresh the tab
}
+15 -641
View File
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using HarmonyLib;
using MiraAPI.GameOptions;
using MiraAPI.Modifiers;
@@ -19,663 +18,38 @@ namespace MiraAPI.Patches.Options;
[HarmonyPatch(typeof(GameSettingMenu))]
internal static class GameSettingMenuPatches
{
public static int SelectedModIdx { get; private set; }
public static MiraPluginInfo? SelectedMod { get; private set; }
private static TextMeshPro? _text;
private static Vector3 _roleBtnOgPos;
private static Vector3 _smallRoleBtnOgPos;
private static Vector3 _modifierBtnOgPos;
private static Vector3 _customOneBtnOgPos;
private static Vector3 _customTwoBtnOgPos;
private static GameOptionsMenu? _modifiersTab;
private static PassiveButton? _modifiersButton;
private static PassiveButton? _smallRolesButton;
private static GameOptionsMenu? _customOneTab;
private static PassiveButton? _customOneButton;
private static GameOptionsMenu? _customTwoTab;
private static PassiveButton? _customTwoButton;
private static GameObject? _nextModButton;
private static GameObject? _previousModButton;
private static Dictionary<int, Vector3> OptionsPositions { get; } = [];
private static Dictionary<int, Vector3> ModifiersPositions { get; } = [];
private static Dictionary<int, Vector3> CustomOnePositions { get; } = [];
private static Dictionary<int, Vector3> CustomTwoPositions { get; } = [];
private static void SaveScrollPositions(GameSettingMenu gameSettingMenu)
{
if (_modifiersTab)
{
ModifiersPositions[SelectedModIdx] = _modifiersTab!.scrollBar.Inner.localPosition;
}
else if (_customOneTab)
{
CustomOnePositions[SelectedModIdx] = _customOneTab!.scrollBar.Inner.localPosition;
}
else if (_customTwoTab)
{
CustomTwoPositions[SelectedModIdx] = _customTwoTab!.scrollBar.Inner.localPosition;
}
RoleSettingMenuPatches.RolePositions[SelectedModIdx] = gameSettingMenu.RoleSettingsTab.scrollBar.Inner.localPosition;
OptionsPositions[SelectedModIdx] = gameSettingMenu.GameSettingsTab.scrollBar.Inner.localPosition;
}
[HarmonyPrefix]
[HarmonyPatch(nameof(GameSettingMenu.ChangeTab))]
public static void ChangeTabPrefix(GameSettingMenu __instance)
{
SaveScrollPositions(__instance);
}
[HarmonyPostfix]
[HarmonyPatch(nameof(GameSettingMenu.ChangeTab))]
public static void ChangeTabPostfix(GameSettingMenu __instance, int tabNum, bool previewOnly)
{
if ((previewOnly && Controller.currentTouchType == Controller.TouchType.Joystick) || !previewOnly)
{
if (_modifiersTab)
{
_modifiersTab!.gameObject.SetActive(tabNum == 3);
}
if (_customOneTab)
{
_customOneTab!.gameObject.SetActive(tabNum == 4);
}
if (_customTwoTab)
{
_customTwoTab!.gameObject.SetActive(tabNum == 5);
}
_modifiersButton?.SelectButton(tabNum == 3);
_smallRolesButton?.SelectButton(tabNum == 2);
_customOneButton?.SelectButton(tabNum == 4);
_customTwoButton?.SelectButton(tabNum == 5);
if (tabNum >= 3 && tabNum <= 5)
{
if (__instance.RoleSettingsButton.gameObject.active)
{
__instance.RoleSettingsButton.SelectButton(true);
}
else
{
switch (tabNum)
{
case 3:
_modifiersButton?.SelectButton(true);
__instance.MenuDescriptionText.text = SelectedMod != null ? SelectedMod.MiraPlugin.ModifierMenuDescription : "Configure modifiers and their settings here!";
break;
case 4:
_customOneButton?.SelectButton(true);
__instance.MenuDescriptionText.text = SelectedMod != null ? SelectedMod.MiraPlugin.CustomOptionMenuOneDescription : "Apply game settings for this mod!";
break;
case 5:
_customTwoButton?.SelectButton(true);
__instance.MenuDescriptionText.text = SelectedMod != null ? SelectedMod.MiraPlugin.CustomOptionMenuTwoDescription : "Apply game settings for this mod!";
break;
}
}
}
}
if (previewOnly)
{
return;
}
_modifiersButton?.SelectButton(tabNum == 3);
_smallRolesButton?.SelectButton(tabNum == 2);
_customOneButton?.SelectButton(tabNum == 4);
_customTwoButton?.SelectButton(tabNum == 5);
if (tabNum >= 3 && tabNum <= 5)
{
if (__instance.RoleSettingsButton.gameObject.active)
{
__instance.RoleSettingsButton.SelectButton(true);
}
else
{
switch (tabNum)
{
case 3:
_modifiersButton?.SelectButton(true);
break;
case 4:
_customOneButton?.SelectButton(true);
break;
case 5:
_customTwoButton?.SelectButton(true);
break;
}
}
}
}
/// <summary>
/// Prefix for the <see cref="GameSettingMenu.Start"/> method. Sets up the custom options.
/// Prefix for the <see cref="GameSettingMenu.OnEnable"/> method. Sets up the custom options.
/// </summary>
/// <param name="__instance">The <see cref="GameSettingMenu"/> instance.</param>
[HarmonyPrefix]
[HarmonyPatch(nameof(GameSettingMenu.Start))]
public static void StartPrefix(GameSettingMenu __instance)
[HarmonyPatch(nameof(GameSettingMenu.OnEnable))]
public static void OnEnablePrefix(GameSettingMenu __instance)
{
_roleBtnOgPos = __instance.RoleSettingsButton.transform.localPosition;
__instance.transform.FindChild("GameSettingsLabel").gameObject.SetActive(false);
var helpThing = __instance.transform.FindChild("What Is This?");
var tmpText = Object.Instantiate(helpThing.transform.FindChild("InfoText"), helpThing.parent).gameObject;
tmpText.GetComponent<TextTranslatorTMP>().Destroy();
tmpText.name = "SelectedMod";
tmpText.transform.localPosition = new Vector3(-3.3382f, 1.5399f, -2);
_text = tmpText.GetComponent<TextMeshPro>();
_text.fontSizeMax = 3.2f;
_text.overflowMode = TextOverflowModes.Overflow;
_text.alignment = TextAlignmentOptions.Center;
_nextModButton = Object.Instantiate(__instance.BackButton, __instance.BackButton.transform.parent).gameObject;
_nextModButton.transform.localPosition = new Vector3(-2.2663f, 1.5272f, -25f);
_nextModButton.name = "RightArrowButton";
_nextModButton.transform.FindChild("Inactive").gameObject.GetComponent<SpriteRenderer>().sprite =
MiraAssets.NextButton.LoadAsset();
_nextModButton.transform.FindChild("Active").gameObject.GetComponent<SpriteRenderer>().sprite =
MiraAssets.NextButtonActive.LoadAsset();
_nextModButton.gameObject.GetComponent<CloseButtonConsoleBehaviour>().DestroyImmediate();
var passiveButton = _nextModButton.gameObject.GetComponent<PassiveButton>();
passiveButton.OnClick = new ButtonClickedEvent();
passiveButton.OnClick.AddListener(
(UnityAction)(() =>
{
SaveScrollPositions(__instance);
SelectedModIdx += 1;
if (SelectedModIdx > MiraPluginManager.Instance.RegisteredOptionablePlugins.Length)
{
SelectedModIdx = 0;
}
UpdateText(__instance, __instance.GameSettingsTab, __instance.RoleSettingsTab);
}));
_previousModButton = Object.Instantiate(_nextModButton, __instance.BackButton.transform.parent).gameObject;
_previousModButton.transform.localPosition = new Vector3(-4.4209f, 1.5272f, -25f);
_previousModButton.name = "LeftArrowButton";
_previousModButton.gameObject.GetComponent<CloseButtonConsoleBehaviour>().Destroy();
_previousModButton.transform.FindChild("Active").gameObject.GetComponent<SpriteRenderer>().flipX =
_previousModButton.transform.FindChild("Inactive").gameObject.GetComponent<SpriteRenderer>().flipX = true;
_previousModButton.gameObject.GetComponent<PassiveButton>().OnClick.AddListener(
(UnityAction)(() =>
{
SaveScrollPositions(__instance);
SelectedModIdx -= 1;
if (SelectedModIdx < 0)
{
SelectedModIdx = MiraPluginManager.Instance.RegisteredOptionablePlugins.Length;
}
UpdateText(__instance, __instance.GameSettingsTab, __instance.RoleSettingsTab);
}));
// clone game settings tab for modifiers
_modifiersTab = Object.Instantiate(__instance.GameSettingsTab, __instance.GameSettingsTab.transform.parent);
_modifiersTab.name = "MODIFIERS TAB";
// clone game settings tab for both custom tabs
_customOneTab = Object.Instantiate(__instance.GameSettingsTab, __instance.GameSettingsTab.transform.parent);
_customOneTab.name = "CUSTOM TAB 1";
_customTwoTab = Object.Instantiate(__instance.GameSettingsTab, __instance.GameSettingsTab.transform.parent);
_customTwoTab.name = "CUSTOM TAB 2";
// create button for the first custom category
var customPos = new Vector3(
__instance.RoleSettingsButton.transform.localPosition.x,
__instance.RoleSettingsButton.transform.localPosition.y,
__instance.RoleSettingsButton.transform.localPosition.z);
_customOneButton = Object.Instantiate(__instance.RoleSettingsButton, __instance.RoleSettingsButton.transform.parent);
_customOneButton.buttonText.gameObject.GetComponent<TextTranslatorTMP>().Destroy();
_customOneButton.OnClick = new ButtonClickedEvent();
_customOneButton.OnClick.AddListener(
(UnityAction)(() =>
{
__instance.ChangeTab(4, false);
}));
_customOneButton.OnMouseOver = new UnityEvent();
_customOneButton.OnMouseOver.AddListener(
(UnityAction)(() =>
{
__instance.ChangeTab(4, true);
}));
_customOneButton.buttonText.text = SelectedMod != null ? SelectedMod.MiraPlugin.CustomOptionMenuNameOne : "Custom Category 1";
customPos.y -= 0.637f;
_customOneButton.transform.localPosition = customPos;
_customOneButton.name = "CustomOneButton";
_customOneBtnOgPos = _customOneButton.transform.localPosition;
// create button for the first custom category
_customTwoButton = Object.Instantiate(_customOneButton, _customOneButton.transform.parent);
_customTwoButton.OnClick = new ButtonClickedEvent();
_customTwoButton.OnClick.AddListener(
(UnityAction)(() =>
{
__instance.ChangeTab(5, false);
}));
_customTwoButton.OnMouseOver = new UnityEvent();
_customTwoButton.OnMouseOver.AddListener(
(UnityAction)(() =>
{
__instance.ChangeTab(5, true);
}));
_customTwoButton.buttonText.text = SelectedMod != null ? SelectedMod.MiraPlugin.CustomOptionMenuNameTwo : "Custom Category 2";
customPos.y -= 0.637f;
_customTwoButton.transform.localPosition = customPos;
_customTwoButton.name = "CustomTwoButton";
_customTwoBtnOgPos = _customTwoButton.transform.localPosition;
// create button for modifiers
__instance.RoleSettingsButton.buttonText.gameObject.GetComponent<TextTranslatorTMP>().Destroy();
__instance.RoleSettingsButton.gameObject.SetActive(false);
_smallRolesButton = Object.Instantiate(
__instance.RoleSettingsButton,
__instance.RoleSettingsButton.transform.parent);
var pos = new Vector3(
-3.65f,
_smallRolesButton.transform.localPosition.y,
_smallRolesButton.transform.localPosition.z);
_smallRolesButton.transform.localPosition = pos;
_smallRolesButton.OnClick.AddListener(
(UnityAction)(() =>
{
__instance.ChangeTab(2, false);
}));
_smallRolesButton.OnMouseOver.AddListener(
(UnityAction)(() =>
{
__instance.ChangeTab(2, true);
}));
_smallRoleBtnOgPos = _smallRolesButton.transform.localPosition;
var roleText = _smallRolesButton.buttonText;
roleText.text = "Roles";
roleText.GetComponent<TextTranslatorTMP>().Destroy();
roleText.alignment = TextAlignmentOptions.Center;
roleText.transform.parent.localPosition = new Vector3(
-.525f,
roleText.transform.parent.localPosition.y,
roleText.transform.parent.localPosition.z);
foreach (var collider in _smallRolesButton.Colliders)
// Add Menu State object
if (!__instance.gameObject.GetComponent<MenuState>())
{
if (collider.TryCast<BoxCollider2D>() is { } col)
{
col.size = new Vector2(col.size.x / 2, col.size.y);
}
}
foreach (var rend in _smallRolesButton.GetComponentsInChildren<SpriteRenderer>(true))
{
rend.size = new Vector2(rend.size.x / 2, rend.size.y);
}
_modifiersButton = Object.Instantiate(_smallRolesButton, _smallRolesButton.transform.parent);
_modifiersButton.OnClick = new ButtonClickedEvent();
_modifiersButton.OnClick.AddListener(
(UnityAction)(() =>
{
__instance.ChangeTab(3, false);
}));
_modifiersButton.OnMouseOver = new UnityEvent();
_modifiersButton.OnMouseOver.AddListener(
(UnityAction)(() =>
{
__instance.ChangeTab(3, true);
}));
_modifiersButton.buttonText.text = "Modifiers";
pos.x = -2.27f;
_modifiersButton.transform.localPosition = pos;
_modifiersButton.name = "ModifiersButton";
_modifierBtnOgPos = _modifiersButton.transform.localPosition;
foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins)
{
PresetManager.LoadPresets(plugin);
}
UpdateText(__instance, __instance.GameSettingsTab, __instance.RoleSettingsTab);
}
private static void ChangeRoleSettingButton(bool replace, GameSettingMenu menu)
{
menu.RoleSettingsButton.OnClick = new ButtonClickedEvent();
menu.RoleSettingsButton.OnMouseOver = new UnityEvent();
if (replace)
{
menu.RoleSettingsButton.buttonText.text = "Modifiers Settings";
menu.RoleSettingsButton.OnClick.AddListener(
(UnityAction)(() =>
{
menu.ChangeTab(3, false);
}));
menu.RoleSettingsButton.OnMouseOver.AddListener(
(UnityAction)(() =>
{
menu.ChangeTab(3, true);
}));
__instance.gameObject.AddComponent<MenuState>();
Info("MenuState component added to GameSettingMenu.");
}
else
{
menu.RoleSettingsButton.buttonText.text = "Roles Settings";
menu.RoleSettingsButton.OnClick.AddListener(
(UnityAction)(() =>
{
menu.ChangeTab(2, false);
}));
menu.RoleSettingsButton.OnMouseOver.AddListener(
(UnityAction)(() =>
{
menu.ChangeTab(2, true);
}));
Info("MenuState component already added to GameSettingMenu.");
}
}
private static void UpdateText(GameSettingMenu menu, GameOptionsMenu settings, RolesSettingsMenu roles)
[HarmonyPrefix]
[HarmonyPatch(nameof(GameSettingMenu.ChangeTab))]
public static bool ChangeTabPrefix(GameSettingMenu __instance, int tabNum, bool previewOnly)
{
if (_text is not null && SelectedModIdx == 0)
if (MenuState.Instance)
{
_text.text = $"<size=40%>(Page 0/{MiraPluginManager.Instance.RegisteredOptionablePlugins.Length})</size>\nMain";
_text.fontSizeMax = 3.2f;
SelectedMod = null;
}
else if (_text is not null)
{
_text.fontSizeMax = 2.3f;
SelectedMod = MiraPluginManager.Instance.RegisteredOptionablePlugins[SelectedModIdx - 1];
var name = SelectedMod.MiraPlugin.OptionsTitleText;
_text.text = $"<size=50%>(Page {SelectedModIdx}/{MiraPluginManager.Instance.RegisteredOptionablePlugins.Length})</size>\n" + name[..Math.Min(name.Length, 25)];
}
bool replaceWithModifiers = true;
_smallRolesButton!.transform.localPosition = _smallRoleBtnOgPos;
_modifiersButton!.transform.localPosition = _modifierBtnOgPos;
_customOneButton!.transform.localPosition = _customOneBtnOgPos;
_customTwoButton!.transform.localPosition = _customTwoBtnOgPos;
menu.RoleSettingsButton.transform.localPosition = _roleBtnOgPos;
CleanupTab(settings, roles);
if (SelectedModIdx != 0)
{
_customOneButton.buttonText.text = SelectedMod!.MiraPlugin.CustomOptionMenuNameOne;
_customTwoButton.buttonText.text = SelectedMod!.MiraPlugin.CustomOptionMenuNameTwo;
var modHasRoles = SelectedMod!.InternalRoles.Count != 0;
var modHasCustomOne = SelectedMod.InternalOptionGroups.Exists(
x => x.ParentMenu == MenuCategory.CustomOne);
var modHasCustomTwo = SelectedMod.InternalOptionGroups.Exists(
x => x.ParentMenu == MenuCategory.CustomTwo);
var modHasModifiers = SelectedMod.InternalOptionGroups.Exists(
x => x.ParentMenu == MenuCategory.Modifiers || x.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true);
var modHasOptions =
SelectedMod.InternalOptionGroups.Exists(x => x.OptionableType == null &&
x.ParentMenu == MenuCategory.Game);
_modifiersButton.gameObject.SetActive(true);
_smallRolesButton.gameObject.SetActive(true);
_customOneButton.gameObject.SetActive(true);
_customTwoButton.gameObject.SetActive(true);
menu.GameSettingsButton.gameObject.SetActive(true);
menu.RoleSettingsButton.gameObject.SetActive(false);
var defaultButton = menu.RoleSettingsButton;
// If there are no registered custom roles in the selected mod, hide the button.
if (!modHasRoles)
{
_smallRolesButton.gameObject.SetActive(false);
if (roles.gameObject.active)
{
menu.ChangeTab(0, false);
}
// If the mod has modifiers, we can disable the smaller modifier button and enable the bigger one.
if (modHasModifiers)
{
_modifiersButton.gameObject.SetActive(false);
menu.RoleSettingsButton.gameObject.SetActive(true);
defaultButton = _modifiersButton;
if (_modifiersTab!.gameObject.active)
{
menu.RoleSettingsButton.SelectButton(true);
}
}
}
// If there are no modifiers in the selected mod, hide the modifiers button.
if (!modHasModifiers)
{
replaceWithModifiers = false;
_modifiersButton.gameObject.SetActive(false);
if (_modifiersTab!.gameObject.active)
{
menu.ChangeTab(0, false);
}
// If the mod has roles, we can enable the bigger role button and disable the small one.
if (modHasRoles)
{
defaultButton = menu.RoleSettingsButton;
menu.RoleSettingsButton.gameObject.SetActive(true);
_smallRolesButton.gameObject.SetActive(false);
}
}
// If there are no custom game options registered (that aren't modifier options), hide game settings button.
if (!modHasOptions)
{
menu.GameSettingsButton.gameObject.SetActive(false);
if (settings.gameObject.active)
{
menu.ChangeTab(0, false);
}
defaultButton = menu.RoleSettingsButton;
// If the mod has roles and modifiers, we can move their buttons to the game settings button position, since nothing is there.
if (menu.RoleSettingsButton.gameObject.active)
{
menu.RoleSettingsButton.transform.localPosition = menu.GameSettingsButton.transform.localPosition;
}
else if (_modifiersButton.gameObject.active && _smallRolesButton.gameObject.active)
{
_modifiersButton.transform.localPosition = new Vector3(
_modifiersButton.transform.localPosition.x,
menu.GameSettingsButton.transform.localPosition.y,
_modifiersButton.transform.localPosition.z);
_smallRolesButton.transform.localPosition = new Vector3(
_smallRolesButton.transform.localPosition.x,
menu.GameSettingsButton.transform.localPosition.y,
_smallRolesButton.transform.localPosition.z);
}
}
// If the mod has no primary custom tab, we can go ahead and hide the button.
if (!modHasCustomOne)
{
_customOneButton.gameObject.SetActive(false);
if (_customOneTab!.gameObject.active)
{
defaultButton.SelectButton(true);
}
}
// If the mod has no secondary custom tab, we can go ahead and hide the button.
if (!modHasCustomTwo)
{
_customTwoButton.gameObject.SetActive(false);
if (_customTwoTab!.gameObject.active)
{
defaultButton.SelectButton(true);
}
}
MenuState.Instance.ChangeTabPatch(__instance, tabNum, previewOnly);
}
else
{
_modifiersButton.gameObject.SetActive(false);
_smallRolesButton.gameObject.SetActive(false);
_customOneButton.gameObject.SetActive(false);
_customTwoButton.gameObject.SetActive(false);
menu.RoleSettingsButton.gameObject.SetActive(true);
menu.GameSettingsButton.gameObject.SetActive(true);
replaceWithModifiers = false;
if (_modifiersTab!.gameObject.active || _customOneTab!.gameObject.active || _customTwoTab!.gameObject.active)
{
menu.ChangeTab(0, false);
}
}
if (menu.RoleSettingsButton.gameObject.active)
{
ChangeRoleSettingButton(replaceWithModifiers, menu);
}
if (menu.PresetsTab.gameObject.active)
{
menu.ChangeTab(0, false);
}
}
private static void ClearOptions(Il2CppSystem.Collections.Generic.List<OptionBehaviour> options)
{
foreach (var child in options)
{
if (child.TryCast<GameOptionsMapPicker>() || !child.gameObject)
{
continue;
}
child.gameObject.DestroyImmediate();
}
options.Clear();
}
private static void CleanupTab(GameOptionsMenu settings, RolesSettingsMenu roles)
{
if (roles.roleChances != null)
{
CleanupRoleSettings(roles);
}
if (settings.Children != null)
{
CleanupSettings(settings);
}
if (_modifiersTab?.Children?.Count > 0)
{
CleanupSettings(_modifiersTab, MenuCategory.Modifiers);
}
if (_customOneTab?.Children?.Count > 0)
{
CleanupSettings(_customOneTab, MenuCategory.CustomOne);
}
if (_customTwoTab?.Children?.Count > 0)
{
CleanupSettings(_customTwoTab, MenuCategory.CustomTwo);
}
Utilities.Extensions.ClearGarbageCollector();
void CleanupRoleSettings(RolesSettingsMenu rolesMenu)
{
if (rolesMenu.advancedSettingChildren != null)
{
ClearOptions(rolesMenu.advancedSettingChildren);
rolesMenu.advancedSettingChildren = null;
}
rolesMenu.RoleChancesSettings
.transform
.GetComponentsInChildren<CategoryHeaderEditRole>()
.ToList()
.ForEach(header => header.gameObject.DestroyImmediate());
foreach (var role in rolesMenu.roleChances)
{
role.gameObject.DestroyImmediate();
}
rolesMenu.roleChances?.Clear();
rolesMenu.AdvancedRolesSettings.gameObject.SetActive(false);
rolesMenu.RoleChancesSettings.gameObject.SetActive(true);
rolesMenu.SetQuotaTab();
if (RoleSettingMenuPatches.RolePositions.TryGetValue(SelectedModIdx, out var pos))
{
rolesMenu.scrollBar.Inner.localPosition = pos;
rolesMenu.scrollBar.UpdateScrollBars();
}
else
{
rolesMenu.scrollBar.ScrollToTop();
}
}
void CleanupSettings(GameOptionsMenu gameOptMenu, MenuCategory menuCategory = MenuCategory.Roles)
{
ClearOptions(gameOptMenu.Children);
gameOptMenu.Children = null;
gameOptMenu.settingsContainer
.GetComponentsInChildren<CategoryHeaderMasked>()
.ToList()
.ForEach(header => header.gameObject.DestroyImmediate());
gameOptMenu.Initialize();
var positions = OptionsPositions;
switch (menuCategory)
{
case MenuCategory.Modifiers:
positions = ModifiersPositions;
break;
case MenuCategory.CustomOne:
positions = CustomOnePositions;
break;
case MenuCategory.CustomTwo:
positions = CustomTwoPositions;
break;
}
if (positions.TryGetValue(SelectedModIdx, out var pos))
{
gameOptMenu.scrollBar.Inner.localPosition = pos;
gameOptMenu.scrollBar.UpdateScrollBars();
}
else
{
gameOptMenu.scrollBar.ScrollToTop();
}
Error("MenuState instance is null. Cannot change tab.");
}
return false;
}
}
@@ -25,7 +25,7 @@ public static class LobbyViewPanePatches
private static MiraPluginInfo? SelectedMod => SelectedModIdx == 0
? null
: MiraPluginManager.Instance.RegisteredOptionablePlugins[SelectedModIdx - 1];
: MiraPluginManager.Instance.RegisteredPluginsWithOptions[SelectedModIdx - 1];
private static PassiveButton? ModifiersTabButton { get; set; }
@@ -75,7 +75,7 @@ public static class LobbyViewPanePatches
(UnityAction)(() =>
{
SelectedModIdx += 1;
if (SelectedModIdx > MiraPluginManager.Instance.RegisteredOptionablePlugins.Length)
if (SelectedModIdx > MiraPluginManager.Instance.RegisteredPluginsWithOptions.Length)
{
SelectedModIdx = 0;
}
@@ -99,7 +99,7 @@ public static class LobbyViewPanePatches
SelectedModIdx -= 1;
if (SelectedModIdx < 0)
{
SelectedModIdx = MiraPluginManager.Instance.RegisteredOptionablePlugins.Length;
SelectedModIdx = MiraPluginManager.Instance.RegisteredPluginsWithOptions.Length;
}
Refresh(__instance);
+682
View File
@@ -0,0 +1,682 @@
using System;
using System.Collections.Generic;
using Il2CppInterop.Runtime.Attributes;
using MiraAPI.GameOptions;
using MiraAPI.Modifiers;
using MiraAPI.PluginLoading;
using MiraAPI.Presets;
using MiraAPI.Utilities.Assets;
using Reactor.Utilities.Attributes;
using Reactor.Utilities.Extensions;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
namespace MiraAPI.Patches.Options;
[RegisterInIl2Cpp]
public class MenuState(IntPtr cppPtr) : MonoBehaviour(cppPtr)
{
public static MenuState Instance { get; private set; } = null!;
public static int ModCount => MiraPluginManager.Instance.RegisteredPluginsWithOptions.Length;
private GameSettingMenu Gsm { get; set; }
public MenuCategory CurrentMenu { get; private set; }
public int CurrentModIdx { get; private set; }
public GameObject CurrentContainer => Containers[CurrentMenu][CurrentModIdx];
[HideFromIl2Cpp]
public MiraPluginInfo CurrentMod => MiraPluginManager.Instance.RegisteredPluginsWithOptions[CurrentModIdx - 1];
// UI elements
private TextMeshPro _text = null!;
private enum MenuButton
{
Roles,
Modifiers,
CustomOne,
CustomTwo,
}
private readonly Dictionary<MenuButton, PassiveButton> _smallButtons = [];
private readonly Dictionary<MenuButton, PassiveButton> _largeButtons = [];
[HideFromIl2Cpp]
public Dictionary<MenuCategory, Dictionary<int, GameObject>> Containers { get; } = new()
{
{ MenuCategory.Preset, [] },
{ MenuCategory.Game, [] },
{ MenuCategory.Roles, [] },
{ MenuCategory.Modifiers, [] },
{ MenuCategory.CustomOne, [] },
{ MenuCategory.CustomTwo, [] },
};
internal Dictionary<int, bool> FinishedRoleMenus = [];
public void Awake()
{
if (Instance)
{
Error($"MenuState already exists! Destroying duplicate {name}.");
Destroy(this);
return;
}
Instance = this;
Gsm = GetComponent<GameSettingMenu>();
if (!Gsm)
{
Error($"MenuState could not find GameSettingMenu component! Destroying {name}.");
Destroy(this);
return;
}
// Reload presets
foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins)
{
PresetManager.LoadPresets(plugin);
}
// Initialize and setup UI
InitializeContainers();
InitializeUi();
UpdateUi();
}
private void InitializeUi()
{
// Disable game settings label
Gsm.transform.FindChild("GameSettingsLabel").gameObject.SetActive(false);
// Create mod name text
var helpThing = Gsm.transform.FindChild("What Is This?");
var tmpText = Instantiate(helpThing.transform.FindChild("InfoText"), helpThing.parent).gameObject;
Destroy(tmpText.GetComponent<TextTranslatorTMP>());
tmpText.name = "SelectedMod";
tmpText.transform.localPosition = new Vector3(-3.3382f, 1.5399f, -2);
_text = tmpText.GetComponent<TextMeshPro>();
_text.fontSizeMax = 3.2f;
_text.overflowMode = TextOverflowModes.Overflow;
_text.alignment = TextAlignmentOptions.Center;
_text.rectTransform.sizeDelta = new Vector2(1.5f, 0.8f);
// Create next and previous buttons
var nextButton = Instantiate(Gsm.BackButton, Gsm.BackButton.transform.parent).gameObject;
nextButton.transform.localPosition = new Vector3(-2.2663f, 1.5272f, -25f);
nextButton.name = "RightArrowButton";
nextButton.transform.FindChild("Inactive").gameObject.GetComponent<SpriteRenderer>().sprite =
MiraAssets.NextButton.LoadAsset();
nextButton.transform.FindChild("Active").gameObject.GetComponent<SpriteRenderer>().sprite =
MiraAssets.NextButtonActive.LoadAsset();
nextButton.gameObject.GetComponent<CloseButtonConsoleBehaviour>().DestroyImmediate();
var nextPassiveButton = nextButton.gameObject.GetComponent<PassiveButton>();
nextPassiveButton.OnClick = new Button.ButtonClickedEvent();
nextPassiveButton.OnClick.AddListener((UnityAction)NextMod);
var previousButton = Instantiate(nextButton, Gsm.BackButton.transform.parent).gameObject;
previousButton.transform.localPosition = new Vector3(-4.4209f, 1.5272f, -25f);
previousButton.name = "LeftArrowButton";
previousButton.gameObject.GetComponent<CloseButtonConsoleBehaviour>().Destroy();
previousButton.transform.FindChild("Active").gameObject.GetComponent<SpriteRenderer>().flipX =
previousButton.transform.FindChild("Inactive").gameObject.GetComponent<SpriteRenderer>().flipX = true;
var prevPassiveButton = previousButton.gameObject.GetComponent<PassiveButton>();
prevPassiveButton.OnClick = new Button.ButtonClickedEvent();
prevPassiveButton.OnClick.AddListener((UnityAction)PreviousMod);
// Create menu buttons
_largeButtons[(int)MenuButton.Roles] = Gsm.RoleSettingsButton;
// modifiers button
var mBtn = Instantiate(Gsm.RoleSettingsButton, Gsm.RoleSettingsButton.transform.parent);
_largeButtons[MenuButton.Modifiers] = mBtn;
mBtn.name = "ModifiersButton";
mBtn.buttonText.gameObject.GetComponent<TextTranslatorTMP>().Destroy();
mBtn.buttonText.text = "Modifiers";
mBtn.OnClick = new Button.ButtonClickedEvent();
mBtn.OnClick.AddListener(
(UnityAction)(() =>
{
Gsm.ChangeTab((int)MenuCategory.Modifiers, false);
}));
mBtn.OnMouseOver = new UnityEvent();
mBtn.OnMouseOver.AddListener(
(UnityAction)(() =>
{
Gsm.ChangeTab((int)MenuCategory.Modifiers, true);
}));
// first custom menu
var c1Btn = Instantiate(mBtn, mBtn.transform.parent);
_largeButtons[MenuButton.CustomOne] = c1Btn;
c1Btn.name = "CustomOneButton";
c1Btn.buttonText.gameObject.GetComponent<TextTranslatorTMP>().Destroy();
c1Btn.OnClick = new Button.ButtonClickedEvent();
c1Btn.OnClick.AddListener(
(UnityAction)(() =>
{
Gsm.ChangeTab((int)MenuCategory.CustomOne, false);
}));
c1Btn.OnMouseOver = new UnityEvent();
c1Btn.OnMouseOver.AddListener(
(UnityAction)(() =>
{
Gsm.ChangeTab((int)MenuCategory.CustomOne, true);
}));
// second custom menu
var c2Btn = Instantiate(c1Btn, c1Btn.transform.parent);
_largeButtons[MenuButton.CustomTwo] = c2Btn;
c2Btn.name = "CustomTwoButton";
c2Btn.OnClick = new Button.ButtonClickedEvent();
c2Btn.OnClick.AddListener(
(UnityAction)(() =>
{
Gsm.ChangeTab((int)MenuCategory.CustomTwo, false);
}));
c2Btn.OnMouseOver = new UnityEvent();
c2Btn.OnMouseOver.AddListener(
(UnityAction)(() =>
{
Gsm.ChangeTab((int)MenuCategory.CustomTwo, true);
}));
// small roles button
var smRolesBtn = Instantiate(
Gsm.RoleSettingsButton,
Gsm.RoleSettingsButton.transform.parent);
_smallButtons[MenuButton.Roles] = smRolesBtn;
smRolesBtn.name = "SmallRolesButton";
smRolesBtn.OnClick = new Button.ButtonClickedEvent();
smRolesBtn.OnClick.AddListener(
(UnityAction)(() =>
{
Gsm.ChangeTab((int)MenuCategory.Roles, false);
}));
smRolesBtn.OnMouseOver = new UnityEvent();
smRolesBtn.OnMouseOver.AddListener(
(UnityAction)(() =>
{
Gsm.ChangeTab((int)MenuCategory.Roles, true);
}));
var roleText = smRolesBtn.buttonText;
roleText.text = "Roles";
roleText.GetComponent<TextTranslatorTMP>().Destroy();
roleText.alignment = TextAlignmentOptions.Center;
roleText.transform.parent.localPosition = new Vector3(
-.525f,
roleText.transform.parent.localPosition.y,
roleText.transform.parent.localPosition.z);
foreach (var collider in smRolesBtn.Colliders)
{
if (collider.TryCast<BoxCollider2D>() is { } col)
{
col.size = new Vector2(col.size.x / 2, col.size.y);
}
}
foreach (var rend in smRolesBtn.GetComponentsInChildren<SpriteRenderer>(true))
{
rend.size = new Vector2(rend.size.x / 2, rend.size.y);
}
// small modifiers button
var smModBtn = Instantiate(smRolesBtn, smRolesBtn.transform.parent);
_smallButtons[MenuButton.Modifiers] = smModBtn;
smModBtn.name = "SmallModifiersButton";
smModBtn.buttonText.text = "Modifiers";
smModBtn.OnClick = new Button.ButtonClickedEvent();
smModBtn.OnClick.AddListener(
(UnityAction)(() =>
{
Gsm.ChangeTab((int)MenuCategory.Modifiers, false);
}));
smModBtn.OnMouseOver = new UnityEvent();
smModBtn.OnMouseOver.AddListener(
(UnityAction)(() =>
{
Gsm.ChangeTab((int)MenuCategory.Modifiers, true);
}));
// small c1 button
var smC1Btn = Instantiate(smModBtn, smModBtn.transform.parent);
_smallButtons[MenuButton.CustomOne] = smC1Btn;
smC1Btn.name = "SmallCustomOneButton";
smC1Btn.buttonText.text = "Custom 1";
smC1Btn.OnClick = new Button.ButtonClickedEvent();
smC1Btn.OnClick.AddListener(
(UnityAction)(() =>
{
Gsm.ChangeTab((int)MenuCategory.CustomOne, false);
}));
smC1Btn.OnMouseOver = new UnityEvent();
smC1Btn.OnMouseOver.AddListener(
(UnityAction)(() =>
{
Gsm.ChangeTab((int)MenuCategory.CustomOne, true);
}));
// small c2 button
var smC2Btn = Instantiate(smModBtn, smModBtn.transform.parent);
_smallButtons[MenuButton.CustomTwo] = smC2Btn;
smC2Btn.name = "SmallCustomTwoButton";
smC2Btn.buttonText.text = "Custom 2";
smC2Btn.OnClick = new Button.ButtonClickedEvent();
smC2Btn.OnClick.AddListener(
(UnityAction)(() =>
{
Gsm.ChangeTab((int)MenuCategory.CustomTwo, false);
}));
smC2Btn.OnMouseOver = new UnityEvent();
smC2Btn.OnMouseOver.AddListener(
(UnityAction)(() =>
{
Gsm.ChangeTab((int)MenuCategory.CustomTwo, true);
}));
}
private void InitializeContainers()
{
// Set vanilla containers
Containers[MenuCategory.Game][0] = Gsm.GameSettingsTab.settingsContainer.gameObject;
Containers[MenuCategory.Roles][0] = Gsm.RoleSettingsTab.RoleChancesSettings;
for (var idx = 0; idx < ModCount; idx++)
{
var mod = MiraPluginManager.Instance.RegisteredPluginsWithOptions[idx];
// Create preset container for each mod
var presetContainer = new GameObject($"PresetHolder_{mod.PluginInfo.Metadata.Name}");
presetContainer.transform.SetParent(Gsm.PresetsTab.transform, false);
presetContainer.transform.localScale = new Vector3(0.9f, 0.9f, 1);
presetContainer.transform.localPosition = new Vector3(-1.9f, 1.7f, 0);
presetContainer.gameObject.SetActive(false);
var arrange = presetContainer.AddComponent<GridArrange>();
arrange.Alignment = GridArrange.StartAlign.Right;
arrange.CellSize = new Vector2(2.15f, -.55f);
arrange.MaxColumns = 2;
Containers[MenuCategory.Preset][idx + 1] = presetContainer;
// Create game options container
var gameContainer = Containers[MenuCategory.Game][idx + 1] = Instantiate(
Gsm.GameSettingsTab.settingsContainer.gameObject,
Gsm.GameSettingsTab.settingsContainer.gameObject.transform.parent);
gameContainer.name = $"GameSettings_{mod.PluginInfo.Metadata.Name}";
gameContainer.transform.DestroyChildren();
gameContainer.SetActive(false);
// Roles container
var rolesContainer = Containers[MenuCategory.Roles][idx + 1] = Instantiate(
Gsm.RoleSettingsTab.RoleChancesSettings,
Gsm.RoleSettingsTab.RoleChancesSettings.transform.parent);
rolesContainer.name = $"RoleSettings_{mod.PluginInfo.Metadata.Name}";
rolesContainer.transform.DestroyChildren();
rolesContainer.SetActive(false);
// Modifiers + Custom containers
for (var i = MenuCategory.Modifiers; i <= MenuCategory.CustomTwo; i++)
{
var cont = Containers[i][idx + 1] = Instantiate(
Gsm.GameSettingsTab.settingsContainer.gameObject,
Gsm.GameSettingsTab.settingsContainer.gameObject.transform.parent);
cont.name = $"{i}_{mod.PluginInfo.Metadata.Name}";
cont.transform.DestroyChildren();
cont.SetActive(false);
}
}
}
private void UpdateContainers()
{
foreach (var (_, dict) in Containers)
{
foreach (var (_, container) in dict)
{
container.SetActive(false);
}
}
Info($"Enabled {CurrentMenu} for {CurrentModIdx}");
// Preset containers
if (CurrentModIdx != 0)
{
Containers[MenuCategory.Preset][CurrentModIdx].SetActive(true);
}
// Game / Modifier / Custom Settings tab
// GST.settingsContainer is same as GST.scrollBar.Inner
if (CurrentMenu != MenuCategory.Preset && CurrentMenu != MenuCategory.Roles)
{
Gsm.GameSettingsTab.settingsContainer = Gsm.GameSettingsTab.scrollBar.Inner = CurrentContainer.transform;
CurrentContainer.SetActive(true);
}
// Role Settings Tab
Gsm.RoleSettingsTab.RoleChancesSettings = Containers[MenuCategory.Roles][CurrentModIdx];
Gsm.RoleSettingsTab.scrollBar.Inner = Containers[MenuCategory.Roles][CurrentModIdx].transform;
Containers[MenuCategory.Roles][CurrentModIdx].SetActive(true);
}
private void UpdateUi()
{
// Update mod text
if (CurrentModIdx == 0)
{
_text.text = $"<size=40%>(Page 0/{ModCount})</size>\nMain";
}
else
{
var modName = CurrentMod.PluginInfo.Metadata.Name;
_text.text = $"<size=50%>(Page {CurrentModIdx}/{ModCount})</size>\n" +
modName[..Math.Min(modName.Length, 25)];
}
UpdateContainers();
// Update buttons
foreach (var (_, btn) in _smallButtons)
{
btn.gameObject.SetActive(false);
}
foreach (var (_, btn) in _largeButtons)
{
btn.gameObject.SetActive(false);
}
if (CurrentModIdx == 0)
{
Gsm.GameSettingsButton.gameObject.SetActive(true);
Gsm.RoleSettingsButton.gameObject.SetActive(true);
}
else
{
var hasOptions =
CurrentMod.InternalOptionGroups.Exists(g =>
g.OptionableType == null && g.ParentMenu == MenuCategory.Game);
var hasRoles = CurrentMod.InternalRoles.Count > 0;
var hasModifiers = CurrentMod.InternalOptionGroups.Exists(g =>
g.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true
|| g.ParentMenu == MenuCategory.Modifiers);
var hasC1 = CurrentMod.InternalOptionGroups.Exists(g => g.ParentMenu == MenuCategory.CustomOne);
var hasC2 = CurrentMod.InternalOptionGroups.Exists(g => g.ParentMenu == MenuCategory.CustomTwo);
const float leftPos = -3.65f;
const float rightPos = -2.27f;
var smallRolesModifiers = hasRoles && hasModifiers && (hasC1 || hasC2);
var smallC1C2 = hasC1 && hasC2;
Info("hasOptions: " + hasOptions);
Info("hasRoles: " + hasRoles);
Info("hasModifiers: " + hasModifiers);
Info("hasC1: " + hasC1);
Info("hasC2: " + hasC2);
Info("smallC1C2: " + smallC1C2);
Info("smallRolesModifiers: " + smallRolesModifiers);
var position = Gsm.GameSettingsButton.transform.localPosition;
// If this mod has game options, enable the game settings button
if (hasOptions)
{
Gsm.GameSettingsButton.gameObject.SetActive(true);
position.y -= 0.637f;
}
else
{
Gsm.GameSettingsButton.gameObject.SetActive(false);
}
// If we should use small roles/modifiers buttons, enable them and position them accordingly
if (smallRolesModifiers)
{
_smallButtons[MenuButton.Roles].gameObject.SetActive(true);
_smallButtons[MenuButton.Roles].transform.localPosition = new Vector3(leftPos, position.y, position.z);
_smallButtons[MenuButton.Modifiers].gameObject.SetActive(true);
_smallButtons[MenuButton.Modifiers].transform.localPosition =
new Vector3(rightPos, position.y, position.z);
position.y -= 0.637f;
}
// If we should use large roles button, enable and position, otherwise disable
if (hasRoles && !smallRolesModifiers)
{
_largeButtons[MenuButton.Roles].gameObject.SetActive(true);
_largeButtons[MenuButton.Roles].transform.localPosition = position;
position.y -= 0.637f;
}
else
{
_largeButtons[MenuButton.Roles].gameObject.SetActive(false);
}
// If we should use large modifiers button, enable and position, otherwise disable
if (hasModifiers && !smallRolesModifiers)
{
_largeButtons[MenuButton.Modifiers].gameObject.SetActive(true);
_largeButtons[MenuButton.Modifiers].transform.localPosition = position;
position.y -= 0.637f;
}
else
{
_largeButtons[MenuButton.Modifiers].gameObject.SetActive(false);
}
// if we should use small custom buttons, enable and position accordingly
if (smallC1C2)
{
_smallButtons[MenuButton.CustomOne].gameObject.SetActive(true);
_smallButtons[MenuButton.CustomOne].transform.localPosition =
new Vector3(leftPos, position.y, position.z);
_smallButtons[MenuButton.CustomOne].buttonText.text = CurrentMod.MiraPlugin.CustomOptionMenuNameOne;
_smallButtons[MenuButton.CustomTwo].gameObject.SetActive(true);
_smallButtons[MenuButton.CustomTwo].transform.localPosition =
new Vector3(rightPos, position.y, position.z);
_smallButtons[MenuButton.CustomTwo].buttonText.text = CurrentMod.MiraPlugin.CustomOptionMenuNameTwo;
position.y -= 0.637f;
}
// C1 only
if (hasC1 && !smallC1C2)
{
_largeButtons[MenuButton.CustomOne].gameObject.SetActive(true);
_largeButtons[MenuButton.CustomOne].transform.localPosition = position;
_largeButtons[MenuButton.CustomOne].buttonText.text = CurrentMod.MiraPlugin.CustomOptionMenuNameOne;
position.y -= 0.637f;
}
else
{
_largeButtons[MenuButton.CustomOne].gameObject.SetActive(false);
}
// C2 only
if (hasC2 && !smallC1C2)
{
_largeButtons[MenuButton.CustomTwo].gameObject.SetActive(true);
_largeButtons[MenuButton.CustomTwo].transform.localPosition = position;
_largeButtons[MenuButton.CustomTwo].buttonText.text = CurrentMod.MiraPlugin.CustomOptionMenuNameTwo;
position.y -= 0.637f;
}
else
{
_largeButtons[MenuButton.CustomTwo].gameObject.SetActive(false);
}
}
}
internal void ChangeTabPatch(GameSettingMenu menu, int tabNum, bool previewOnly)
{
Info($"Changed tab to {(MenuCategory)tabNum} (previewOnly: {previewOnly}) for mod {CurrentModIdx}");
if ((previewOnly && Controller.currentTouchType == Controller.TouchType.Joystick) || !previewOnly)
{
CurrentMenu = (MenuCategory)tabNum;
UpdateContainers();
menu.PresetsTab.gameObject.SetActive(false);
menu.GameSettingsTab.gameObject.SetActive(false);
menu.RoleSettingsTab.gameObject.SetActive(false);
menu.GamePresetsButton.SelectButton(false);
menu.GameSettingsButton.SelectButton(false);
menu.RoleSettingsButton.SelectButton(false);
foreach (var (_, btn) in _largeButtons)
{
btn.SelectButton(false);
}
foreach (var (_, btn) in _smallButtons)
{
btn.SelectButton(false);
}
switch (CurrentMenu)
{
case MenuCategory.Preset:
menu.PresetsTab.gameObject.SetActive(true);
menu.MenuDescriptionText.text = TranslationController.Instance.GetString(StringNames.GamePresetsDescription);
break;
case MenuCategory.Game:
menu.GameSettingsTab.gameObject.SetActive(true);
menu.MenuDescriptionText.text = TranslationController.Instance.GetString(StringNames.GameSettingsDescription);
break;
case MenuCategory.Roles:
menu.RoleSettingsTab.gameObject.SetActive(true);
menu.RoleSettingsTab.OpenMenu(false);
menu.MenuDescriptionText.text = TranslationController.Instance.GetString(StringNames.RoleSettingsDescription);
break;
case MenuCategory.Modifiers:
menu.GameSettingsTab.gameObject.SetActive(true);
menu.MenuDescriptionText.text = CurrentMod.MiraPlugin.ModifierMenuDescription;
break;
case MenuCategory.CustomOne:
menu.GameSettingsTab.gameObject.SetActive(true);
menu.MenuDescriptionText.text = CurrentMod.MiraPlugin.CustomOptionMenuOneDescription;
break;
case MenuCategory.CustomTwo:
menu.GameSettingsTab.gameObject.SetActive(true);
menu.MenuDescriptionText.text = CurrentMod.MiraPlugin.CustomOptionMenuTwoDescription;
break;
}
}
if (previewOnly)
{
menu.ToggleLeftSideDarkener(false);
menu.ToggleRightSideDarkener(true);
return;
}
menu.ToggleLeftSideDarkener(true);
menu.ToggleRightSideDarkener(false);
CurrentMenu = (MenuCategory)tabNum;
UpdateContainers();
switch (CurrentMenu)
{
case MenuCategory.Preset:
menu.PresetsTab.OpenMenu();
menu.GamePresetsButton.SelectButton(true);
break;
case MenuCategory.Roles:
menu.RoleSettingsTab.OpenMenu();
SelectButton(MenuButton.Roles, MenuCategory.Roles);
break;
case MenuCategory.Game:
case MenuCategory.Modifiers:
case MenuCategory.CustomOne:
case MenuCategory.CustomTwo:
menu.GameSettingsButton.SelectButton(CurrentMenu == MenuCategory.Game);
SelectButton(MenuButton.Modifiers, MenuCategory.Modifiers);
SelectButton(MenuButton.CustomOne, MenuCategory.CustomOne);
SelectButton(MenuButton.CustomTwo, MenuCategory.CustomTwo);
break;
}
}
private void SelectButton(MenuButton button, MenuCategory category)
{
if (_largeButtons[button].gameObject.activeInHierarchy)
{
_largeButtons[button].SelectButton(CurrentMenu == category);
}
if (_smallButtons[button].gameObject.activeInHierarchy)
{
_smallButtons[button].SelectButton(CurrentMenu == category);
}
}
private MenuCategory GetNewPageIfNeeded()
{
if (CurrentModIdx == 0)
{
if (CurrentMenu >= MenuCategory.Modifiers)
{
return MenuCategory.Game;
}
}
else
{
var hasOptions =
CurrentMod.InternalOptionGroups.Exists(g =>
g.OptionableType == null && g.ParentMenu == MenuCategory.Game);
var hasRoles = CurrentMod.InternalRoles.Count > 0;
var hasModifiers = CurrentMod.InternalOptionGroups.Exists(g =>
g.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true
|| g.ParentMenu == MenuCategory.Modifiers);
var hasC1 = CurrentMod.InternalOptionGroups.Exists(g => g.ParentMenu == MenuCategory.CustomOne);
var hasC2 = CurrentMod.InternalOptionGroups.Exists(g => g.ParentMenu == MenuCategory.CustomTwo);
switch (CurrentMenu)
{
case MenuCategory.Game when !hasOptions:
case MenuCategory.Roles when !hasRoles:
case MenuCategory.Modifiers when !hasModifiers:
case MenuCategory.CustomOne when !hasC1:
case MenuCategory.CustomTwo when !hasC2:
return MenuCategory.Preset;
}
}
return CurrentMenu;
}
public void NextMod()
{
CurrentModIdx++;
// The existence of vanilla settings offsets the off-by-one errors.
if (CurrentModIdx > ModCount) CurrentModIdx = 0;
CurrentMenu = GetNewPageIfNeeded();
UpdateUi();
ChangeTabPatch(Gsm, (int)CurrentMenu, false);
}
public void PreviousMod()
{
CurrentModIdx--;
// The existence of vanilla settings offsets the off-by-one errors.
if (CurrentModIdx < 0) CurrentModIdx = ModCount;
CurrentMenu = GetNewPageIfNeeded();
UpdateUi();
ChangeTabPatch(Gsm, (int)CurrentMenu, false);
}
}
+228 -232
View File
@@ -24,11 +24,7 @@ namespace MiraAPI.Patches.Options;
[HarmonyPatch(typeof(RolesSettingsMenu))]
public static class RoleSettingMenuPatches
{
public static Dictionary<int, Vector3> RolePositions { get; set; } = [];
private static Dictionary<RoleOptionsGroup, bool> RoleGroupHidden { get; set; } = [];
private static List<GameObject> Headers { get; set; } = [];
private static List<RoleOptionSetting> RoleOptionSettings { get; set; } = [];
private static float ScrollerNum { get; set; } = 0.522f;
@@ -42,48 +38,20 @@ public static class RoleSettingMenuPatches
HudManager.Instance.PlayerCam.OverrideScreenShakeEnabled = false;
}
[HarmonyPostfix]
[HarmonyPatch(nameof(RolesSettingsMenu.SetQuotaTab))]
public static void SetQuotaTabPostfix(RolesSettingsMenu __instance)
{
__instance.scrollBar.SetScrollBounds(__instance);
}
private static IEnumerator CoQuotaTabPatch(RolesSettingsMenu roleMenu)
{
foreach (var obj in Headers)
{
if (!obj)
{
continue;
}
obj.Destroy();
}
Headers.Clear();
foreach (var obj in RoleOptionSettings)
{
if (!obj)
{
continue;
}
obj.gameObject.Destroy();
}
RoleOptionSettings.Clear();
CurrentRole = null;
CurrentRoleOptions = null;
roleMenu.roleChances = new Il2CppSystem.Collections.Generic.List<RoleOptionSetting>();
roleMenu.advancedSettingChildren = new Il2CppSystem.Collections.Generic.List<OptionBehaviour>();
roleMenu.QuotaTabSelectables.Clear();
roleMenu.roleChances.Clear();
roleMenu.advancedSettingChildren.Clear();
var maskBg = roleMenu.scrollBar.transform.FindChild("MaskBg");
var hitbox = roleMenu.scrollBar.transform.FindChild("Hitbox");
var dividerImage = roleMenu.transform.FindChild("HeaderButtons/DividerImage");
if (GameSettingMenuPatches.SelectedModIdx == 0)
if (MenuState.Instance.CurrentModIdx == 0)
{
roleMenu.AllButton.transform.parent.gameObject.SetActive(true);
roleMenu.AllButton.gameObject.SetActive(true);
@@ -93,7 +61,34 @@ public static class RoleSettingMenuPatches
hitbox.localPosition = new Vector3(0.3297f, -.2333f, 4f);
hitbox.localScale = new Vector3(1, 1, 1);
dividerImage.gameObject.SetActive(true);
}
else
{
roleMenu.AllButton.transform.parent.gameObject.SetActive(false);
roleMenu.AllButton.gameObject.SetActive(false);
roleMenu.scrollBar.transform.localPosition = new Vector3(-1.4957f, 1.5261f, -4);
maskBg.localPosition = new Vector3(1.5353f, -1.0607f, -.1f);
maskBg.localScale = new Vector3(6.6811f, 4.1563f, 0.5598f);
hitbox.localPosition = new Vector3(0.3297f, -.6333f, 4f);
hitbox.localScale = new Vector3(1, 1.2f, 1);
dividerImage.gameObject.SetActive(false);
}
if (MenuState.Instance.FinishedRoleMenus.TryGetValue(MenuState.Instance.CurrentModIdx, out var finished) && finished)
{
var roleOptionSettings = roleMenu.scrollBar.Inner.GetComponentsInChildren<RoleOptionSetting>(true);
foreach (var r in roleOptionSettings)
{
roleMenu.roleChances.Add(r);
roleMenu.QuotaTabSelectables.AddRange(new Il2CppSystem.Collections.Generic.IEnumerable<UiElement>(r.ControllerSelectable.Pointer));
}
Info($"Already created role options for {MenuState.Instance.CurrentModIdx}");
yield break;
}
if (MenuState.Instance.CurrentModIdx == 0)
{
var num = 0.662f;
var list = CustomRoleManager.AllRoles.Where(r =>
@@ -152,208 +147,186 @@ public static class RoleSettingMenuPatches
}
Info($"Created {num3} role options for the default game.");
_quotaTabCoroutine = null;
MenuState.Instance.FinishedRoleMenus[MenuState.Instance.CurrentModIdx] = true;
yield break;
}
ScrollerNum = 0.522f;
roleMenu.AllButton.transform.parent.gameObject.SetActive(false);
roleMenu.AllButton.gameObject.SetActive(false);
roleMenu.scrollBar.transform.localPosition = new Vector3(-1.4957f, 1.5261f, -4);
maskBg.localPosition = new Vector3(1.5353f, -1.0607f, -.1f);
maskBg.localScale = new Vector3(6.6811f, 4.1563f, 0.5598f);
hitbox.localPosition = new Vector3(0.3297f, -.6333f, 4f);
hitbox.localScale = new Vector3(1, 1.2f, 1);
dividerImage.gameObject.SetActive(false);
var num4 = 0;
var roleGroups = GameSettingMenuPatches.SelectedMod?.InternalRoles.Values.OfType<ICustomRole>()
var roleGroups = MenuState.Instance.CurrentMod.InternalRoles.Values.OfType<ICustomRole>()
.ToLookup(x => x.RoleOptionsGroup);
if (roleGroups is null)
if (roleGroups.Count == 0)
{
Error("No role groups found for the selected mod.");
yield break;
}
// sort the groups by priority
var sortedRoleGroups = roleGroups
.OrderBy(x => x.Key.Priority)
.ThenBy(x => x.Key.Name);
var quotaThing = roleMenu.categoryHeaderEditRoleOrigin.transform.FindChild("QuotaHeader");
var usingNewQuota = false;
var template = roleMenu.transform.parent.parent.GetComponent<GameSettingMenu>().GameSettingsTab
.categoryHeaderOrigin;
foreach (var grouping in sortedRoleGroups)
else
{
if (!grouping.Any() || grouping.All(x => x.Configuration.HideSettings || !x.VisibleInSettings()))
// sort the groups by priority
var sortedRoleGroups = roleGroups
.OrderBy(x => x.Key.Priority)
.ThenBy(x => x.Key.Name);
var quotaThing = roleMenu.categoryHeaderEditRoleOrigin.transform.FindChild("QuotaHeader");
var usingNewQuota = false;
var template = roleMenu.transform.parent.parent.GetComponent<GameSettingMenu>().GameSettingsTab
.categoryHeaderOrigin;
foreach (var grouping in sortedRoleGroups)
{
continue;
}
var group = grouping.Key;
RoleGroupHidden.TryAdd(group, false);
var name = group.Name switch
{
"Crewmate" => StringNames.CrewmateRolesHeader,
"Impostor" => StringNames.ImpostorRolesHeader,
_ => CustomStringName.CreateAndRegister(group.Name),
};
var categoryHeaderMasked = Object.Instantiate(
template,
Vector3.zero,
Quaternion.identity,
roleMenu.RoleChancesSettings.transform);
categoryHeaderMasked.SetHeader(name, 20);
var quotaInst = Object.Instantiate(quotaThing, categoryHeaderMasked.transform);
quotaInst.transform.localScale = new Vector3(1.3f, 1.3f, 1.3f);
quotaInst.transform.localPosition = new Vector3(0.7f, -0.82f, 0);
var chanceText = quotaInst.transform.FindChild("Chance Text");
chanceText.transform.localPosition = new Vector3(4.3f, 0.0993f, 0);
var countText = quotaInst.transform.FindChild("# Text");
countText.transform.localPosition = new Vector3(1.9f, 0.0993f, 0f);
if (!usingNewQuota)
{
var blankLabel = quotaInst.transform.FindChild("BlankLabel").gameObject;
var chanceLabel = quotaInst.transform.FindChild("Chance Label").gameObject;
var countLabel = quotaInst.transform.FindChild("# Label").gameObject;
blankLabel.Destroy();
chanceLabel.Destroy();
countLabel.Destroy();
usingNewQuota = true;
quotaThing = quotaInst;
}
categoryHeaderMasked.Background.sprite = MiraAssets.CategoryHeader.LoadAsset();
categoryHeaderMasked.Background.sprite.texture.filterMode = FilterMode.Bilinear;
categoryHeaderMasked.Background.sprite.texture.wrapMode = TextureWrapMode.Clamp;
categoryHeaderMasked.Background.transform.localPosition = new Vector3(0.5f, -0.1833f, 0);
switch (name)
{
case StringNames.CrewmateRolesHeader:
categoryHeaderMasked.Title.color = Palette.CrewmateRoleHeaderTextBlue;
categoryHeaderMasked.Background.color = Palette.CrewmateRoleHeaderBlue;
break;
case StringNames.ImpostorRolesHeader:
categoryHeaderMasked.Title.color = Palette.ImpostorRoleHeaderTextRed;
categoryHeaderMasked.Background.color = Palette.ImpostorRoleHeaderRed;
break;
default:
categoryHeaderMasked.Title.color = group.Color.Equals(MiraApiPlugin.DefaultHeaderColor)
? Color.white
: group.Color.FindAlternateColor();
categoryHeaderMasked.Divider.color = group.Color;
categoryHeaderMasked.Background.color = group.Color;
break;
}
categoryHeaderMasked.Title.fontStyle = roleMenu.categoryHeaderEditRoleOrigin.Title.fontStyle;
categoryHeaderMasked.Title.font = roleMenu.categoryHeaderEditRoleOrigin.Title.font;
categoryHeaderMasked.Title.fontMaterial = roleMenu.categoryHeaderEditRoleOrigin.Title.fontMaterial;
categoryHeaderMasked.Divider.color = categoryHeaderMasked.Background.color;
categoryHeaderMasked.Background.transform.localPosition = new Vector3(0.55f, -0.1833f, 0);
categoryHeaderMasked.Background.size = new Vector2(
categoryHeaderMasked.Background.size.x + 1.5f,
categoryHeaderMasked.Background.size.y);
categoryHeaderMasked.transform.localScale = Vector3.one * 0.63f;
categoryHeaderMasked.transform.localPosition = new Vector3(-0.44f, ScrollerNum, -2f);
categoryHeaderMasked.gameObject.SetActive(true);
quotaInst.gameObject.SetActive(!RoleGroupHidden[group]);
var label = RoleGroupHidden[group]
? "(Click to open)"
: "(Click to close)";
var newText = Object.Instantiate(categoryHeaderMasked.Title, categoryHeaderMasked.transform);
newText.text = $"<size=70%>{label}</size>";
newText.transform.localPosition = new Vector3(2.6249f, -0.165f, 0f);
newText.gameObject.GetComponent<TextTranslatorTMP>().Destroy();
Headers.Add(categoryHeaderMasked.gameObject);
ScrollerNum -= 0.422f;
if (!RoleGroupHidden[group])
{
yield return
grouping.CoLoopWithBudget(role =>
{
if (role is not RoleBehaviour roleBehaviour)
{
return;
}
var option = CreateQuotaOption(roleMenu, roleBehaviour, num4);
if (option is not null)
{
RoleOptionSettings.Add(option);
num4++;
}
});
}
var boxCol = categoryHeaderMasked.gameObject.AddComponent<BoxCollider2D>();
boxCol.size = new Vector2(7, 0.7f);
boxCol.offset = new Vector2(1.5f, -0.3f);
var headerBtn = categoryHeaderMasked.gameObject.AddComponent<PassiveButton>();
headerBtn.ClickSound = roleMenu.BackButton.GetComponent<PassiveButton>().ClickSound;
headerBtn.OnMouseOver = new UnityEvent();
headerBtn.OnMouseOut = new UnityEvent();
headerBtn.OnClick.AddListener(
(UnityAction)(() =>
if (!grouping.Any() || grouping.All(x => x.Configuration.HideSettings || !x.VisibleInSettings()))
{
RolePositions[GameSettingMenuPatches.SelectedModIdx] = roleMenu.scrollBar.Inner.localPosition;
if (RoleGroupHidden.TryGetValue(group, out var groupHidden))
{
RoleGroupHidden[group] = !groupHidden;
}
continue;
}
foreach (var obj in Headers)
{
if (!obj)
var group = grouping.Key;
RoleGroupHidden.TryAdd(group, false);
var name = group.Name switch
{
"Crewmate" => StringNames.CrewmateRolesHeader,
"Impostor" => StringNames.ImpostorRolesHeader,
_ => CustomStringName.CreateAndRegister(group.Name),
};
var categoryHeaderMasked = Object.Instantiate(
template,
Vector3.zero,
Quaternion.identity,
roleMenu.RoleChancesSettings.transform);
categoryHeaderMasked.SetHeader(name, 20);
var quotaInst = Object.Instantiate(quotaThing, categoryHeaderMasked.transform);
quotaInst.transform.localScale = new Vector3(1.3f, 1.3f, 1.3f);
quotaInst.transform.localPosition = new Vector3(0.7f, -0.82f, 0);
var chanceText = quotaInst.transform.FindChild("Chance Text");
chanceText.transform.localPosition = new Vector3(4.3f, 0.0993f, 0);
var countText = quotaInst.transform.FindChild("# Text");
countText.transform.localPosition = new Vector3(1.9f, 0.0993f, 0f);
if (!usingNewQuota)
{
var blankLabel = quotaInst.transform.FindChild("BlankLabel").gameObject;
var chanceLabel = quotaInst.transform.FindChild("Chance Label").gameObject;
var countLabel = quotaInst.transform.FindChild("# Label").gameObject;
blankLabel.Destroy();
chanceLabel.Destroy();
countLabel.Destroy();
usingNewQuota = true;
quotaThing = quotaInst;
}
categoryHeaderMasked.Background.sprite = MiraAssets.CategoryHeader.LoadAsset();
categoryHeaderMasked.Background.sprite.texture.filterMode = FilterMode.Bilinear;
categoryHeaderMasked.Background.sprite.texture.wrapMode = TextureWrapMode.Clamp;
categoryHeaderMasked.Background.transform.localPosition = new Vector3(0.5f, -0.1833f, 0);
switch (name)
{
case StringNames.CrewmateRolesHeader:
categoryHeaderMasked.Title.color = Palette.CrewmateRoleHeaderTextBlue;
categoryHeaderMasked.Background.color = Palette.CrewmateRoleHeaderBlue;
break;
case StringNames.ImpostorRolesHeader:
categoryHeaderMasked.Title.color = Palette.ImpostorRoleHeaderTextRed;
categoryHeaderMasked.Background.color = Palette.ImpostorRoleHeaderRed;
break;
default:
categoryHeaderMasked.Title.color = group.Color.Equals(MiraApiPlugin.DefaultHeaderColor)
? Color.white
: group.Color.FindAlternateColor();
categoryHeaderMasked.Divider.color = group.Color;
categoryHeaderMasked.Background.color = group.Color;
break;
}
categoryHeaderMasked.Title.fontStyle = roleMenu.categoryHeaderEditRoleOrigin.Title.fontStyle;
categoryHeaderMasked.Title.font = roleMenu.categoryHeaderEditRoleOrigin.Title.font;
categoryHeaderMasked.Title.fontMaterial = roleMenu.categoryHeaderEditRoleOrigin.Title.fontMaterial;
categoryHeaderMasked.Divider.color = categoryHeaderMasked.Background.color;
categoryHeaderMasked.Background.transform.localPosition = new Vector3(0.55f, -0.1833f, 0);
categoryHeaderMasked.Background.size = new Vector2(
categoryHeaderMasked.Background.size.x + 1.5f,
categoryHeaderMasked.Background.size.y);
categoryHeaderMasked.transform.localScale = Vector3.one * 0.63f;
categoryHeaderMasked.transform.localPosition = new Vector3(-0.44f, ScrollerNum, -2f);
categoryHeaderMasked.gameObject.SetActive(true);
quotaInst.gameObject.SetActive(!RoleGroupHidden[group]);
var label = RoleGroupHidden[group]
? "(Click to open)"
: "(Click to close)";
var newText = Object.Instantiate(categoryHeaderMasked.Title, categoryHeaderMasked.transform);
newText.text = $"<size=70%>{label}</size>";
newText.transform.localPosition = new Vector3(2.6249f, -0.165f, 0f);
newText.gameObject.GetComponent<TextTranslatorTMP>().Destroy();
ScrollerNum -= 0.422f;
if (!RoleGroupHidden[group])
{
yield return
grouping.CoLoopWithBudget(role =>
{
continue;
if (role is not RoleBehaviour roleBehaviour)
{
return;
}
var option = CreateQuotaOption(roleMenu, roleBehaviour, num4);
if (option is not null)
{
num4++;
}
});
}
var boxCol = categoryHeaderMasked.gameObject.AddComponent<BoxCollider2D>();
boxCol.size = new Vector2(7, 0.7f);
boxCol.offset = new Vector2(1.5f, -0.3f);
var headerBtn = categoryHeaderMasked.gameObject.AddComponent<PassiveButton>();
headerBtn.ClickSound = roleMenu.BackButton.GetComponent<PassiveButton>().ClickSound;
headerBtn.OnMouseOver = new UnityEvent();
headerBtn.OnMouseOut = new UnityEvent();
headerBtn.OnClick.AddListener(
(UnityAction)(() =>
{
if (RoleGroupHidden.TryGetValue(group, out var groupHidden))
{
RoleGroupHidden[group] = !groupHidden;
}
obj.Destroy();
}
MenuState.Instance.FinishedRoleMenus[MenuState.Instance.CurrentModIdx] = false;
Headers.Clear();
foreach (var obj in RoleOptionSettings)
{
if (!obj)
foreach (var child in roleMenu.RoleChancesSettings.transform)
{
continue;
var transform = child.Cast<Transform>();
Object.Destroy(transform.gameObject);
}
obj.gameObject.Destroy();
}
roleMenu.OpenChancesTab();
}));
headerBtn.SetButtonEnableState(true);
RoleOptionSettings.Clear();
roleMenu.SetQuotaTab();
}));
headerBtn.SetButtonEnableState(true);
if (RoleGroupHidden.TryGetValue(group, out var value) && !value)
{
ScrollerNum -= 0.4f;
if (RoleGroupHidden.TryGetValue(group, out var value) && !value)
{
ScrollerNum -= 0.4f;
}
}
}
MenuState.Instance.FinishedRoleMenus[MenuState.Instance.CurrentModIdx] = true;
roleMenu.scrollBar.SetScrollBounds(roleMenu);
_quotaTabCoroutine = null;
}
@@ -361,36 +334,62 @@ public static class RoleSettingMenuPatches
private static Coroutine? _quotaTabCoroutine;
[HarmonyPrefix]
[HarmonyPatch(nameof(RolesSettingsMenu.SetQuotaTab))]
public static bool SetQuotaTabPatch(RolesSettingsMenu __instance)
[HarmonyPatch(nameof(RolesSettingsMenu.OpenChancesTab))]
public static bool OpenChancesTabPatch(RolesSettingsMenu __instance, bool controllerSelected)
{
__instance.selectedRoleTab = 0;
__instance.RoleChancesSettings.SetActive(true);
__instance.AdvancedRolesSettings.SetActive(false);
__instance.ControllerSelectable.Clear();
if (_quotaTabCoroutine != null)
{
GameSettingMenu.Instance.StopCoroutine(_quotaTabCoroutine);
}
_quotaTabCoroutine = GameSettingMenu.Instance.StartCoroutine(CoQuotaTabPatch(__instance).WrapToIl2Cpp());
__instance.QuotaTabSelectables ??= new();
__instance.roleChances ??= new();
__instance.advancedSettingChildren ??= new();
_quotaTabCoroutine = GameSettingMenu.Instance.StartCoroutine(CoOpenChancesTab().WrapToIl2Cpp());
return false;
IEnumerator CoOpenChancesTab()
{
yield return CoQuotaTabPatch(__instance);
if (controllerSelected)
{
__instance.ControllerSelectable.AddRange(
new Il2CppSystem.Collections.Generic.IEnumerable<UiElement>(
__instance.QuotaTabSelectables.Pointer));
}
__instance.scrollBar.CalculateAndSetYBounds(__instance.roleChances.Count + 3f, 1f, 6f, 0.43f);
__instance.scrollBar.SetScrollBounds(__instance);
if (controllerSelected)
{
ControllerManager.Instance.CurrentUiState.SelectableUiElements = __instance.ControllerSelectable;
ControllerManager.Instance.SetDefaultSelection(__instance.roleChances[0].ControllerSelectable[0]);
}
var passiveButton = __instance.currentTabButton;
if (passiveButton)
{
passiveButton.SelectButton(false);
}
__instance.AllButton.SelectButton(true);
__instance.currentTabButton = __instance.AllButton;
}
}
private static void SetScrollBounds(this Scroller scroller, RolesSettingsMenu roleMenu)
{
if (GameSettingMenuPatches.SelectedModIdx == 0)
if (MenuState.Instance.CurrentModIdx == 0)
{
scroller.CalculateAndSetYBounds(roleMenu.roleChances.Count + 3, 1f, 6f, 0.43f);
return;
}
scroller.SetYBoundsMax(-ScrollerNum - 2);
if (RolePositions.TryGetValue(GameSettingMenuPatches.SelectedModIdx, out var scroll))
{
scroller.Inner.localPosition = scroll;
scroller.UpdateScrollBars();
}
else
{
scroller.ScrollToTop();
}
}
[HarmonyPrefix]
@@ -398,7 +397,7 @@ public static class RoleSettingMenuPatches
[HarmonyPatch(nameof(RolesSettingsMenu.Update))]
public static bool UpdatePatch(RolesSettingsMenu __instance)
{
if (GameSettingMenuPatches.SelectedModIdx == 0) return true;
if (MenuState.Instance.CurrentModIdx == 0) return true;
if (CurrentRole == null || CurrentRoleOptions == null) return false;
var hasImage = CurrentRole.RoleScreenshot != null;
@@ -489,10 +488,10 @@ public static class RoleSettingMenuPatches
__instance.advancedSettingChildren.Clear();
// TODO: create sub groups under the role settings.
var filteredOptions = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
var filteredOptions = MenuState.Instance.CurrentMod.InternalOptionGroups
.Where(x => x.GroupVisible() && x.OptionableType == role.GetType())
.SelectMany(x => x.Options)
.ToList() ?? [];
.ToList();
foreach (var option in filteredOptions)
{
@@ -536,8 +535,6 @@ public static class RoleSettingMenuPatches
return;
}
RolePositions[GameSettingMenuPatches.SelectedModIdx] = __instance.scrollBar.Inner.localPosition;
__instance.roleDescriptionText.text = customRole.RoleLongDescription;
__instance.roleTitleText.text = role.GetRoleName();
@@ -670,8 +667,7 @@ public static class RoleSettingMenuPatches
rend.maskInteraction = SpriteMaskInteraction.VisibleInsideMask;
}
if (GameSettingMenuPatches.SelectedMod is null ||
GameSettingMenuPatches.SelectedMod.InternalOptionGroups
if (MenuState.Instance.CurrentMod.InternalOptionGroups
.Exists(x => x.GroupVisible() && x.OptionableType == role.GetType() && x.Options.Any(y => y.Visible())))
{
var newButton = Object.Instantiate(roleOptionSetting.buttons[0], roleOptionSetting.transform);
@@ -703,7 +699,7 @@ public static class RoleSettingMenuPatches
roleOptionSetting.ChancePlusBtn.gameObject.SetActive(false);
}
if (index < GameSettingMenuPatches.SelectedMod?.InternalRoles.Count - 1)
if (index < MenuState.Instance.CurrentMod.InternalRoles.Count - 1)
{
ScrollerNum -= 0.43f;
}
+3 -9
View File
@@ -17,6 +17,7 @@ using MiraAPI.Keybinds;
using MiraAPI.LocalSettings;
using MiraAPI.LocalSettings.Attributes;
using MiraAPI.Modifiers;
using MiraAPI.Patches.Options;
using MiraAPI.Presets;
using MiraAPI.Roles;
using MiraAPI.Utilities;
@@ -33,10 +34,7 @@ public sealed class MiraPluginManager
private readonly Dictionary<Assembly, MiraPluginInfo> _registeredPlugins = [];
internal MiraPluginInfo[] RegisteredPlugins { get; private set; } = null!;
private readonly Dictionary<Assembly, MiraPluginInfo> _registeredOptionablePlugins = [];
internal MiraPluginInfo[] RegisteredOptionablePlugins { get; private set; } = null!;
internal MiraPluginInfo[] RegisteredPluginsWithOptions { get; private set; } = null!;
internal Dictionary<MiraPluginInfo, List<Type>> QueuedRoleRegistrations { get; } = [];
internal static MiraPluginManager Instance { get; private set; } = new();
@@ -131,10 +129,6 @@ public sealed class MiraPluginManager
QueuedRoleRegistrations.Add(info, roles);
_registeredPlugins.Add(assembly, info);
if (miraPlugin.DisplayOnOptionsMenu)
{
_registeredOptionablePlugins.Add(assembly, info);
}
info.SavePublicCollections();
PresetManager.CreateDefaultPreset(info);
@@ -149,7 +143,7 @@ public sealed class MiraPluginManager
// Cache all the registered plugins into an array for easy access
RegisteredPlugins = [.. _registeredPlugins.Values];
RegisteredOptionablePlugins = [.. _registeredOptionablePlugins.Values];
RegisteredPluginsWithOptions = [.. RegisteredPlugins.Where(m => m.MiraPlugin.DisplayOnOptionsMenu)];
ModifierManager.Modifiers = new ReadOnlyCollection<BaseModifier>(ModifierManager.InternalModifiers);
};
+5 -1
View File
@@ -141,7 +141,11 @@ public static class CustomRoleManager
roleBehaviour.CanVent = customRole.Configuration.CanUseVent || customRole.Configuration.GetsVentData;
roleBehaviour.DefaultGhostRole = customRole.Configuration.GhostRole;
roleBehaviour.MaxCount = customRole.Configuration.MaxRoleCount;
roleBehaviour.RoleScreenshot = customRole.Configuration.OptionsScreenshot?.LoadAsset();
roleBehaviour.RoleScreenshot = Sprite.Create(
customRole.Configuration.OptionsScreenshot?.LoadAsset().texture,
new Rect(0, 0, 370, 230),
Vector2.one / 2,
100);
_emptySettings ??= new(0);
_emptyKillAnimations ??= new(0);
+19 -1
View File
@@ -1,4 +1,6 @@
namespace MiraAPI.Utilities.Assets;
using Reactor.Utilities.Extensions;
namespace MiraAPI.Utilities.Assets;
/// <summary>
/// An abstract class that provides a simple pattern for loading assets.
@@ -18,4 +20,20 @@ public abstract class LoadableAsset<T> where T : UnityEngine.Object
/// </summary>
/// <returns>The loaded asset.</returns>
public abstract T LoadAsset();
/// <summary>
/// Unloads an asset.
/// </summary>
/// <returns>True if the asset was unloaded, false otherwise.</returns>
public virtual bool UnloadAsset()
{
if (LoadedAsset == null || !LoadedAsset)
{
return false;
}
LoadedAsset.DestroyImmediate();
LoadedAsset = null;
return true;
}
}
+2 -6
View File
@@ -354,10 +354,6 @@ public static class Extensions
UnityEngine.Object.Destroy(go);
yield return null;
if (clearGc)
{
ClearGarbageCollector();
}
}
/// <summary>
@@ -366,8 +362,8 @@ public static class Extensions
public static void ClearGarbageCollector()
{
Resources.UnloadUnusedAssets();
Il2CppSystem.GC.Collect(0, Il2CppSystem.GCCollectionMode.Forced);
GC.Collect(0, GCCollectionMode.Forced);
Il2CppSystem.GC.Collect();
GC.Collect();
}
/// <summary>