mirror of
https://github.com/Gurge44/EndlessHostRoles.git
synced 2026-10-20 01:46:25 +00:00
1696 lines
73 KiB
C#
1696 lines
73 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Linq;
|
|
using BepInEx.Unity.IL2CPP.Utils.Collections;
|
|
using EHR.Gamemodes;
|
|
using EHR.Modules;
|
|
using EHR.Patches;
|
|
using EHR.Roles;
|
|
using HarmonyLib;
|
|
using Il2CppSystem.Collections.Generic;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
|
|
// ReSharper disable PossibleLossOfFraction
|
|
|
|
namespace EHR;
|
|
// Credit: https://github.com/Yumenopai/TownOfHost_Y
|
|
|
|
public static class ModGameOptionsMenu
|
|
{
|
|
public static int TabIndex;
|
|
public static readonly Dictionary<OptionBehaviour, int> OptionList = new();
|
|
public static readonly Dictionary<int, OptionBehaviour> BehaviourList = new();
|
|
public static readonly Dictionary<int, CategoryHeaderMasked> CategoryHeaderList = new();
|
|
public static readonly System.Collections.Generic.Dictionary<CustomRoles, Transform> HelpIconList = [];
|
|
public static readonly System.Collections.Generic.Dictionary<OptionItem, BaseGameSetting> BaseGameSettingCache = [];
|
|
|
|
public static T Track<T>(T obj) where T : Object
|
|
{
|
|
return obj;
|
|
}
|
|
|
|
public static Color32 GetCurrentThemeColor(TabGroup? tab = null)
|
|
{
|
|
CustomGameMode gm = Options.CurrentGameMode;
|
|
Color32 color = gm == CustomGameMode.Standard ? (tab ?? (TabGroup)(TabIndex - 3)) switch
|
|
{
|
|
TabGroup.ImpostorRoles => new Color32(255, 25, 25, 255),
|
|
TabGroup.CrewmateRoles => new Color32(140, 255, 255, 255),
|
|
TabGroup.NeutralRoles => new Color32(255, 171, 27, 255),
|
|
TabGroup.CovenRoles => new Color32(123, 63, 187, 255),
|
|
_ => new Color32(0, 165, 255, 255)
|
|
} : Main.GameModeColors.TryGetValue(gm, out var c) ? c : new Color32(0, 165, 255, 255);
|
|
return color;
|
|
}
|
|
}
|
|
|
|
[HarmonyPatch(typeof(GameOptionsMenu))]
|
|
public static class GameOptionsMenuPatch
|
|
{
|
|
internal static readonly Dictionary<GameOptionsMenu, Coroutine> _buildCoroutines = new();
|
|
|
|
[HarmonyPatch(nameof(GameOptionsMenu.Initialize))]
|
|
[HarmonyPrefix]
|
|
private static bool InitializePrefix(GameOptionsMenu __instance)
|
|
{
|
|
if (ModGameOptionsMenu.TabIndex < 3) return true;
|
|
|
|
if (__instance.Children == null || __instance.Children.Count == 0)
|
|
{
|
|
__instance.MapPicker.gameObject.SetActive(false);
|
|
__instance.Children = new();
|
|
__instance.CreateSettings();
|
|
__instance.cachedData = GameOptionsManager.Instance.CurrentGameOptions;
|
|
__instance.InitializeControllerNavigation();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
[HarmonyPatch(nameof(GameOptionsMenu.Initialize))]
|
|
[HarmonyPostfix]
|
|
private static void InitializePostfix()
|
|
{
|
|
GameObject optionMenu = GameObject.Find("PlayerOptionsMenu(Clone)");
|
|
if (!optionMenu) return;
|
|
|
|
optionMenu.transform.FindChild("Background")?.gameObject.SetActive(false);
|
|
|
|
// By TommyXL
|
|
LateTask.New(() =>
|
|
{
|
|
if (!optionMenu) return;
|
|
|
|
Transform menuDescription = optionMenu.transform.FindChild("What Is This?");
|
|
if (!menuDescription) return;
|
|
|
|
Transform infoImage = menuDescription.transform.FindChild("InfoImage");
|
|
|
|
if (infoImage)
|
|
{
|
|
infoImage.transform.localPosition = new(-4.65f, 0.16f, -1f);
|
|
infoImage.transform.localScale = new(0.2202f, 0.2202f, 0.3202f);
|
|
}
|
|
|
|
Transform infoText = menuDescription.transform.FindChild("InfoText");
|
|
|
|
if (infoText)
|
|
{
|
|
infoText.transform.localPosition = new(-3.5f, 0.83f, -2f);
|
|
infoText.transform.localScale = new(1f, 1f, 1f);
|
|
}
|
|
|
|
Transform cubeObject = menuDescription.transform.FindChild("Cube");
|
|
|
|
if (cubeObject)
|
|
{
|
|
cubeObject.transform.localPosition = new(-3.2f, 0.55f, -0.1f);
|
|
cubeObject.transform.localScale = new(0.61f, 0.64f, 1f);
|
|
}
|
|
|
|
if (GameSettingMenu.Instance && GameSettingMenu.Instance.MenuDescriptionText)
|
|
GameSettingMenu.Instance.MenuDescriptionText.m_marginWidth = 2.5f;
|
|
}, 0.2f, log: false);
|
|
}
|
|
|
|
[HarmonyPatch(nameof(GameOptionsMenu.CreateSettings))]
|
|
[HarmonyPrefix]
|
|
private static bool CreateSettingsPrefix(GameOptionsMenu __instance)
|
|
{
|
|
if (ModGameOptionsMenu.TabIndex < 3) return true;
|
|
|
|
if (!__instance.gameObject.activeInHierarchy) return false;
|
|
|
|
var modTab = (TabGroup)(ModGameOptionsMenu.TabIndex - 3);
|
|
|
|
if (modTab == TabGroup.PresetExplorer)
|
|
{
|
|
OnlinePresetsManager.CreatePresetExplorerUI(__instance);
|
|
return false;
|
|
}
|
|
|
|
__instance.scrollBar.SetYBoundsMax(CalculateScrollBarYBoundsMax());
|
|
|
|
// Cancel any in-progress build coroutine on this instance before starting a new one
|
|
if (_buildCoroutines.TryGetValue(__instance, out Coroutine existing) && existing != null)
|
|
{
|
|
__instance.StopCoroutine(existing);
|
|
_buildCoroutines.Remove(__instance);
|
|
}
|
|
|
|
_buildCoroutines[__instance] = __instance.StartCoroutine(CoRoutine().WrapToIl2Cpp());
|
|
return false;
|
|
|
|
IEnumerator CoRoutine()
|
|
{
|
|
var num = 2.0f;
|
|
const float posX = 0.952f;
|
|
const float posZ = -2.0f;
|
|
|
|
TextOptionItem header = null;
|
|
|
|
for (var index = 0; index < OptionItem.AllOptions.Count; index++)
|
|
{
|
|
try
|
|
{
|
|
OptionItem option = OptionItem.AllOptions[index];
|
|
if (option.Tab != modTab) continue;
|
|
|
|
bool enabledOrNotCollapsed = !option.IsCurrentlyHidden() && AllParentsEnabledAndVisible(option.Parent);
|
|
bool enabled = !option.IsCurrentlyHidden(checkCollapsedSection: false) && AllParentsEnabledAndVisible(option.Parent, checkCollapsedSection: false);
|
|
|
|
if (option is TextOptionItem toi)
|
|
{
|
|
CategoryHeaderMasked categoryHeaderMasked = ModGameOptionsMenu.CategoryHeaderList.TryGetValue(index, out CategoryHeaderMasked cache) ? cache : ModGameOptionsMenu.Track(Object.Instantiate(__instance.categoryHeaderOrigin, Vector3.zero, Quaternion.identity, __instance.settingsContainer));
|
|
categoryHeaderMasked.SetHeader(StringNames.RolesCategory, 20);
|
|
categoryHeaderMasked.Title.SetText(option.GetName(disableColor: true).Trim('★', ' '));
|
|
categoryHeaderMasked.Background.color = categoryHeaderMasked.Divider.color = option.NameColor;
|
|
categoryHeaderMasked.transform.localScale = Vector3.one * 0.63f;
|
|
categoryHeaderMasked.transform.localPosition = new(-0.903f, num, posZ);
|
|
var chmText = categoryHeaderMasked.transform.FindChild("HeaderText").GetComponent<TextMeshPro>();
|
|
chmText.fontStyle = FontStyles.Bold | FontStyles.SmallCaps;
|
|
chmText.fontWeight = FontWeight.Black;
|
|
chmText.outlineWidth = 0.17f;
|
|
var chmCollider = categoryHeaderMasked.gameObject.AddComponent<BoxCollider2D>();
|
|
chmCollider.size = new Vector2(7, 0.7f);
|
|
chmCollider.offset = new Vector2(1.5f, -0.3f);
|
|
var chmButton = categoryHeaderMasked.gameObject.AddComponent<PassiveButton>();
|
|
chmButton.ClickSound = __instance.BackButton.GetComponent<PassiveButton>().ClickSound;
|
|
chmButton.OnMouseOver = new();
|
|
chmButton.OnMouseOut = new();
|
|
chmButton.OnClick.AddListener((Action)(() =>
|
|
{
|
|
toi.CollapsesSection = !toi.CollapsesSection;
|
|
ReCreateSettings(__instance);
|
|
}));
|
|
chmButton.SetButtonEnableState(true);
|
|
categoryHeaderMasked.gameObject.SetActive(enabled);
|
|
ModGameOptionsMenu.CategoryHeaderList[index] = categoryHeaderMasked;
|
|
|
|
if (enabledOrNotCollapsed) num -= 0.63f;
|
|
header = toi;
|
|
continue;
|
|
}
|
|
|
|
option.Header = header;
|
|
|
|
if (option.IsHeader && enabledOrNotCollapsed)
|
|
num -= 0.18f;
|
|
|
|
BaseGameSetting baseGameSetting = GetSetting(option);
|
|
if (!baseGameSetting) continue;
|
|
|
|
if (!ModGameOptionsMenu.BehaviourList.TryGetValue(index, out OptionBehaviour optionBehaviour))
|
|
{
|
|
try
|
|
{
|
|
optionBehaviour = baseGameSetting.Type switch
|
|
{
|
|
OptionTypes.Checkbox => ModGameOptionsMenu.Track(Object.Instantiate(__instance.checkboxOrigin, Vector3.zero, Quaternion.identity, __instance.settingsContainer)),
|
|
OptionTypes.String => ModGameOptionsMenu.Track(Object.Instantiate(__instance.stringOptionOrigin, Vector3.zero, Quaternion.identity, __instance.settingsContainer)),
|
|
OptionTypes.Float or OptionTypes.Int => ModGameOptionsMenu.Track(Object.Instantiate(__instance.numberOptionOrigin, Vector3.zero, Quaternion.identity, __instance.settingsContainer)),
|
|
_ => throw new Exception()
|
|
};
|
|
}
|
|
catch { continue; }
|
|
|
|
optionBehaviour.transform.localPosition = new(posX, num, posZ);
|
|
OptionBehaviourSetSizeAndPosition(optionBehaviour, option, baseGameSetting.Type);
|
|
}
|
|
else
|
|
{
|
|
optionBehaviour.transform.localPosition = new(posX, num, posZ);
|
|
}
|
|
|
|
optionBehaviour.transform.localPosition = new(0.952f, num, -2f);
|
|
optionBehaviour.SetClickMask(__instance.ButtonClickMask);
|
|
optionBehaviour.SetUpFromData(baseGameSetting, 20);
|
|
optionBehaviour.gameObject.SetActive(enabledOrNotCollapsed);
|
|
optionBehaviour.OnValueChanged = new Action<OptionBehaviour>(__instance.ValueChanged);
|
|
|
|
ModGameOptionsMenu.OptionList[optionBehaviour] = index;
|
|
ModGameOptionsMenu.BehaviourList[index] = optionBehaviour;
|
|
|
|
__instance.Children.Add(optionBehaviour);
|
|
option.OptionBehaviour = optionBehaviour;
|
|
|
|
if (enabledOrNotCollapsed) num -= 0.45f;
|
|
}
|
|
catch (Exception e) { Utils.ThrowException(e); }
|
|
|
|
if (index % 100 == 0)
|
|
{
|
|
yield return null;
|
|
}
|
|
}
|
|
|
|
__instance.ControllerSelectable.Clear();
|
|
|
|
foreach (UiElement x in __instance.scrollBar.GetComponentsInChildren<UiElement>())
|
|
__instance.ControllerSelectable.Add(x);
|
|
|
|
Canvas.ForceUpdateCanvases();
|
|
_buildCoroutines.Remove(__instance);
|
|
}
|
|
|
|
float CalculateScrollBarYBoundsMax()
|
|
{
|
|
var num = 2.0f;
|
|
|
|
foreach (OptionItem option in OptionItem.AllOptions)
|
|
{
|
|
if (option.Tab != modTab) continue;
|
|
|
|
bool enabledOrNotCollapsed = !option.IsCurrentlyHidden() && AllParentsEnabledAndVisible(option.Parent);
|
|
|
|
if (option is TextOptionItem)
|
|
num -= 0.63f;
|
|
else if (enabledOrNotCollapsed)
|
|
{
|
|
if (option.IsHeader) num -= 0.18f;
|
|
num -= 0.45f;
|
|
}
|
|
}
|
|
|
|
return -num - 1.65f;
|
|
}
|
|
}
|
|
|
|
private static bool AllParentsEnabledAndVisible(OptionItem o, bool checkCollapsedSection = true)
|
|
{
|
|
while (true)
|
|
{
|
|
if (o == null) return true;
|
|
if (o.IsCurrentlyHidden(forLobbyView: false, checkCollapsedSection: checkCollapsedSection) || !o.GetBool()) return false;
|
|
o = o.Parent;
|
|
}
|
|
}
|
|
|
|
private static void OptionBehaviourSetSizeAndPosition(OptionBehaviour optionBehaviour, OptionItem option, OptionTypes type)
|
|
{
|
|
Vector3 positionOffset = new(0f, 0f, 0f);
|
|
Vector3 scaleOffset = new(0f, 0f, 0f);
|
|
Color color = new(0.35f, 0.35f, 0.35f);
|
|
var sizeDeltaX = 5.7f;
|
|
|
|
if (option.Parent?.Parent?.Parent != null)
|
|
{
|
|
scaleOffset = new(-0.18f, 0, 0);
|
|
positionOffset = new(0.3f, 0f, 0f);
|
|
color = new(0.35f, 0f, 0f);
|
|
sizeDeltaX = 5.1f;
|
|
}
|
|
else if (option.Parent?.Parent != null)
|
|
{
|
|
scaleOffset = new(-0.12f, 0, 0);
|
|
positionOffset = new(0.2f, 0f, 0f);
|
|
color = new(0.35f, 0.35f, 0f);
|
|
sizeDeltaX = 5.3f;
|
|
}
|
|
else if (option.Parent != null)
|
|
{
|
|
scaleOffset = new(-0.05f, 0, 0);
|
|
positionOffset = new(0.1f, 0f, 0f);
|
|
color = new(0f, 0f, 0.35f);
|
|
sizeDeltaX = 5.5f;
|
|
}
|
|
|
|
Transform labelBackground = optionBehaviour.transform.FindChild("LabelBackground");
|
|
labelBackground.GetComponent<SpriteRenderer>().color = color;
|
|
labelBackground.localScale += new Vector3(0.9f, -0.2f, 0f) + scaleOffset;
|
|
labelBackground.localPosition += new Vector3(-0.4f, 0f, 0f) + positionOffset;
|
|
|
|
Transform titleText = optionBehaviour.transform.FindChild("Title Text");
|
|
titleText.localPosition += new Vector3(-0.4f, 0f, 0f) + positionOffset;
|
|
titleText.GetComponent<RectTransform>().sizeDelta = new(sizeDeltaX, 0.37f);
|
|
var textMeshPro = titleText.GetComponent<TextMeshPro>();
|
|
textMeshPro.alignment = TextAlignmentOptions.MidlineLeft;
|
|
textMeshPro.fontStyle = FontStyles.Bold;
|
|
textMeshPro.outlineWidth = 0.17f;
|
|
|
|
switch (type)
|
|
{
|
|
case OptionTypes.Checkbox:
|
|
{
|
|
optionBehaviour.transform.FindChild("Toggle").localPosition = new(1.46f, -0.042f);
|
|
break;
|
|
}
|
|
case OptionTypes.String:
|
|
{
|
|
Transform plusButton = optionBehaviour.transform.FindChild("PlusButton");
|
|
Transform minusButton = optionBehaviour.transform.FindChild("MinusButton");
|
|
plusButton.localPosition += new Vector3(option.IsText ? 500f : 1.7f, option.IsText ? 500f : 0f, option.IsText ? 500f : 0f);
|
|
minusButton.localPosition += new Vector3(option.IsText ? 500f : 0.9f, option.IsText ? 500f : 0f, option.IsText ? 500f : 0f);
|
|
Transform valueTMP = optionBehaviour.transform.FindChild("Value_TMP (1)");
|
|
valueTMP.localPosition += new Vector3(1.3f, 0f, 0f);
|
|
valueTMP.GetComponent<RectTransform>().sizeDelta = new(2.3f, 0.4f);
|
|
goto default;
|
|
}
|
|
case OptionTypes.Float:
|
|
case OptionTypes.Int:
|
|
{
|
|
optionBehaviour.transform.FindChild("PlusButton").localPosition += new Vector3(option.IsText ? 500f : 1.7f, option.IsText ? 500f : 0f, option.IsText ? 500f : 0f);
|
|
optionBehaviour.transform.FindChild("MinusButton").localPosition += new Vector3(option.IsText ? 500f : 0.9f, option.IsText ? 500f : 0f, option.IsText ? 500f : 0f);
|
|
optionBehaviour.transform.FindChild("Value_TMP").localPosition += new Vector3(1.3f, 0f, 0f);
|
|
goto default;
|
|
}
|
|
default:
|
|
{
|
|
Transform valueBox = optionBehaviour.transform.FindChild("ValueBox");
|
|
valueBox.localScale += new Vector3(0.2f, 0f, 0f);
|
|
valueBox.localPosition += new Vector3(1.3f, 0f, 0f);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
[HarmonyPatch(nameof(GameOptionsMenu.Update))]
|
|
[HarmonyPrefix]
|
|
public static void UpdatePostfix(GameOptionsMenu __instance)
|
|
{
|
|
// Disable scroll options when chat open
|
|
if (!HudManager.InstanceExists || !__instance.gameObject.activeSelf || !GameSettingMenu.Instance) return;
|
|
|
|
__instance.scrollBar.enabled = !HudManager.Instance.Chat.IsOpenOrOpening;
|
|
}
|
|
|
|
[HarmonyPatch(nameof(GameOptionsMenu.ValueChanged))]
|
|
[HarmonyPrefix]
|
|
private static bool ValueChangedPrefix(GameOptionsMenu __instance, OptionBehaviour option)
|
|
{
|
|
if (ModGameOptionsMenu.TabIndex < 3) return true;
|
|
|
|
if (ModGameOptionsMenu.OptionList.TryGetValue(option, out int index))
|
|
{
|
|
OptionItem item = OptionItem.AllOptions[index];
|
|
if (item != null && item.Children.Count > 0) ReCreateSettings(__instance);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public static void ReCreateSettings(TabGroup tab)
|
|
{
|
|
if (!GameSettingMenuPatch.ModSettingsTabs.TryGetValue(tab, out GameOptionsMenu menu)) return;
|
|
ReCreateSettings(menu, tab);
|
|
}
|
|
|
|
internal static int _reCreateGeneration;
|
|
internal static Coroutine _reCreateAllCoroutine;
|
|
|
|
public static void ReCreateAllSettings()
|
|
{
|
|
_reCreateGeneration++;
|
|
int generation = _reCreateGeneration;
|
|
|
|
if (_reCreateAllCoroutine != null)
|
|
{
|
|
Main.Instance.StopCoroutine(_reCreateAllCoroutine);
|
|
_reCreateAllCoroutine = null;
|
|
}
|
|
|
|
_reCreateAllCoroutine = Main.Instance.StartCoroutine(CoReCreateAll(generation).WrapToIl2Cpp());
|
|
|
|
IEnumerator CoReCreateAll(int gen)
|
|
{
|
|
TabGroup[] tabs = Main.TabGroupValues;
|
|
foreach (TabGroup tab in tabs)
|
|
{
|
|
if (gen != _reCreateGeneration) yield break;
|
|
|
|
// Wait for any in-progress build coroutine on this tab to finish before recreating
|
|
if (GameSettingMenuPatch.ModSettingsTabs.TryGetValue(tab, out GameOptionsMenu menu) && menu)
|
|
{
|
|
while (_buildCoroutines.TryGetValue(menu, out Coroutine existing) && existing != null)
|
|
{
|
|
if (gen != _reCreateGeneration) yield break;
|
|
yield return null;
|
|
}
|
|
}
|
|
|
|
ReCreateSettings(tab);
|
|
yield return null;
|
|
}
|
|
_reCreateAllCoroutine = null;
|
|
}
|
|
}
|
|
|
|
public static void ReCreateSettings(GameOptionsMenu __instance, TabGroup? modTab = null)
|
|
{
|
|
if (!modTab.HasValue && ModGameOptionsMenu.TabIndex < 3) return;
|
|
if (!__instance) return;
|
|
|
|
modTab ??= (TabGroup)(ModGameOptionsMenu.TabIndex - 3);
|
|
|
|
var num = 2.0f;
|
|
|
|
for (var index = 0; index < OptionItem.AllOptions.Count; index++)
|
|
{
|
|
OptionItem option = OptionItem.AllOptions[index];
|
|
if (option.Tab != modTab) continue;
|
|
|
|
bool enabledOrNotCollapsed = !option.IsCurrentlyHidden() && AllParentsEnabledAndVisible(option.Parent);
|
|
bool enabled = !option.IsCurrentlyHidden(checkCollapsedSection: false) && AllParentsEnabledAndVisible(option.Parent, checkCollapsedSection: false);
|
|
|
|
if (ModGameOptionsMenu.CategoryHeaderList.TryGetValue(index, out CategoryHeaderMasked categoryHeaderMasked) && categoryHeaderMasked)
|
|
{
|
|
categoryHeaderMasked.transform.localPosition = new(-0.903f, num, -2f);
|
|
categoryHeaderMasked.gameObject.SetActive(enabled);
|
|
if (enabledOrNotCollapsed) num -= 0.63f;
|
|
}
|
|
else if (option.IsHeader && enabledOrNotCollapsed) num -= 0.18f;
|
|
|
|
if (ModGameOptionsMenu.BehaviourList.TryGetValue(index, out OptionBehaviour optionBehaviour) && optionBehaviour)
|
|
{
|
|
optionBehaviour.transform.localPosition = new(0.952f, num, -2f);
|
|
optionBehaviour.gameObject.SetActive(enabledOrNotCollapsed);
|
|
if (enabledOrNotCollapsed) num -= 0.45f;
|
|
}
|
|
}
|
|
|
|
if (!__instance.scrollBar) return;
|
|
__instance.ControllerSelectable.Clear();
|
|
|
|
foreach (UiElement x in __instance.scrollBar.GetComponentsInChildren<UiElement>())
|
|
__instance.ControllerSelectable.Add(x);
|
|
|
|
__instance.scrollBar.SetYBoundsMax(-num - 1.65f);
|
|
}
|
|
|
|
public static void RefreshSettingValues()
|
|
{
|
|
// ReSharper disable once UseDeconstruction - unsupported here by Il2Cpp
|
|
foreach (KeyValuePair<OptionBehaviour, int> kvp in ModGameOptionsMenu.OptionList)
|
|
{
|
|
OptionBehaviour optionBehaviour = kvp.Key;
|
|
if (!optionBehaviour) continue;
|
|
|
|
OptionItem optionItem = OptionItem.AllOptions[kvp.Value];
|
|
|
|
switch (optionItem)
|
|
{
|
|
case BooleanOptionItem when optionBehaviour is ToggleOption toggleOption:
|
|
var color = ModGameOptionsMenu.GetCurrentThemeColor(optionItem.Tab);
|
|
toggleOption.CheckMark.color = color;
|
|
toggleOption.CheckMark.enabled = optionItem.GetBool();
|
|
toggleOption.CheckMark.transform.parent.FindChild("ActiveSprite").GetComponent<SpriteRenderer>().color = color;
|
|
break;
|
|
case StringOptionItem stringOptionItem when optionBehaviour is StringOption stringOption:
|
|
stringOption.Value = stringOptionItem.GetInt();
|
|
stringOption.oldValue = stringOption.Value;
|
|
string selection = stringOptionItem.Selections[stringOptionItem.Rule.GetValueByIndex(stringOption.Value)];
|
|
if (!stringOptionItem.noTranslation) selection = Translator.GetString(selection);
|
|
stringOption.ValueText.SetText(selection);
|
|
if (!StringOptionPatch.NameCache.TryGetValue(stringOption, out string name))
|
|
stringOption.Initialize();
|
|
else
|
|
stringOption.TitleText.SetText(name);
|
|
break;
|
|
case FloatOptionItem or IntegerOptionItem when optionBehaviour is NumberOption numberOption:
|
|
numberOption.Value = optionItem.GetFloat();
|
|
break;
|
|
}
|
|
}
|
|
|
|
GameOptionsManager.Instance.GameHostOptions = GameOptionsManager.Instance.CurrentGameOptions;
|
|
GameManager.Instance.LogicOptions.SyncOptions();
|
|
}
|
|
|
|
public static void RefreshTabButtons()
|
|
{
|
|
foreach ((TabGroup tab, PassiveButton button) in GameSettingMenuPatch.ModSettingsButtons)
|
|
button.gameObject.SetActive(tab.IsRelevant());
|
|
}
|
|
|
|
public static void RefreshCheckMarkColors()
|
|
{
|
|
// ReSharper disable once UseDeconstruction - unsupported here by Il2Cpp
|
|
foreach (KeyValuePair<OptionBehaviour, int> kvp in ModGameOptionsMenu.OptionList)
|
|
{
|
|
if (kvp.Key is ToggleOption toggleOption)
|
|
{
|
|
var color = ModGameOptionsMenu.GetCurrentThemeColor(OptionItem.AllOptions[kvp.Value].Tab);
|
|
toggleOption.CheckMark.color = color;
|
|
toggleOption.CheckMark.transform.parent.FindChild("ActiveSprite").GetComponent<SpriteRenderer>().color = color;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static BaseGameSetting GetSetting(OptionItem item)
|
|
{
|
|
if (ModGameOptionsMenu.BaseGameSettingCache.TryGetValue(item, out BaseGameSetting cache)) return cache;
|
|
|
|
BaseGameSetting baseGameSetting;
|
|
|
|
switch (item)
|
|
{
|
|
case BooleanOptionItem:
|
|
var checkboxGameSetting = ScriptableObject.CreateInstance<CheckboxGameSetting>();
|
|
checkboxGameSetting.Type = OptionTypes.Checkbox;
|
|
baseGameSetting = checkboxGameSetting;
|
|
break;
|
|
case IntegerOptionItem integerOptionItem:
|
|
var intGameSetting = ScriptableObject.CreateInstance<IntGameSetting>();
|
|
intGameSetting.Type = OptionTypes.Int;
|
|
intGameSetting.Value = integerOptionItem.GetInt();
|
|
intGameSetting.Increment = integerOptionItem.Rule.Step;
|
|
intGameSetting.ValidRange = new(integerOptionItem.Rule.MinValue, integerOptionItem.Rule.MaxValue);
|
|
intGameSetting.ZeroIsInfinity = false;
|
|
intGameSetting.SuffixType = NumberSuffixes.Multiplier;
|
|
intGameSetting.FormatString = string.Empty;
|
|
baseGameSetting = intGameSetting;
|
|
break;
|
|
case FloatOptionItem floatOptionItem:
|
|
var floatGameSetting = ScriptableObject.CreateInstance<FloatGameSetting>();
|
|
floatGameSetting.Type = OptionTypes.Float;
|
|
floatGameSetting.Value = floatOptionItem.GetFloat();
|
|
floatGameSetting.Increment = floatOptionItem.Rule.Step;
|
|
floatGameSetting.ValidRange = new(floatOptionItem.Rule.MinValue, floatOptionItem.Rule.MaxValue);
|
|
floatGameSetting.ZeroIsInfinity = false;
|
|
floatGameSetting.SuffixType = NumberSuffixes.Multiplier;
|
|
floatGameSetting.FormatString = string.Empty;
|
|
baseGameSetting = floatGameSetting;
|
|
break;
|
|
case StringOptionItem stringOptionItem:
|
|
var stringGameSetting = ScriptableObject.CreateInstance<StringGameSetting>();
|
|
stringGameSetting.Type = OptionTypes.String;
|
|
stringGameSetting.Values = new StringNames[stringOptionItem.Selections.Count];
|
|
stringGameSetting.Index = stringOptionItem.GetInt();
|
|
baseGameSetting = stringGameSetting;
|
|
break;
|
|
default:
|
|
return null;
|
|
}
|
|
|
|
baseGameSetting.Title = StringNames.Accept;
|
|
ModGameOptionsMenu.BaseGameSettingCache[item] = baseGameSetting;
|
|
return baseGameSetting;
|
|
}
|
|
}
|
|
|
|
[HarmonyPatch(typeof(ToggleOption))]
|
|
public static class ToggleOptionPatch
|
|
{
|
|
[HarmonyPatch(nameof(ToggleOption.Initialize))]
|
|
[HarmonyPrefix]
|
|
private static bool InitializePrefix(ToggleOption __instance)
|
|
{
|
|
if (ModGameOptionsMenu.OptionList.TryGetValue(__instance, out int index))
|
|
{
|
|
OptionItem item = OptionItem.AllOptions[index];
|
|
|
|
var color = ModGameOptionsMenu.GetCurrentThemeColor();
|
|
__instance.CheckMark.sprite = Utils.LoadSprite("EHR.Resources.Images.Checkmark.png", 100f);
|
|
__instance.CheckMark.color = color;
|
|
var renderer = __instance.CheckMark.transform.parent.FindChild("ActiveSprite").GetComponent<SpriteRenderer>();
|
|
renderer.sprite = Utils.LoadSprite("EHR.Resources.Images.CheckMarkBox.png", 100f);
|
|
renderer.color = color;
|
|
|
|
__instance.TitleText.SetText(item.GetName());
|
|
__instance.CheckMark.enabled = item.GetBool();
|
|
item.OptionBehaviour = __instance;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// For some reason, ToggleOption.UpdateValue isn't called for Steam users
|
|
[HarmonyPatch(nameof(ToggleOption.Toggle))]
|
|
[HarmonyPrefix]
|
|
private static bool TogglePrefix(ToggleOption __instance)
|
|
{
|
|
if (ModGameOptionsMenu.OptionList.TryGetValue(__instance, out int index))
|
|
{
|
|
__instance.CheckMark.enabled = !__instance.CheckMark.enabled;
|
|
OptionItem item = OptionItem.AllOptions[index];
|
|
item.SetValue(__instance.GetBool() ? 1 : 0, doSave: true, doSync: false);
|
|
__instance.OnValueChanged.Invoke(__instance);
|
|
NotificationPopperPatch.AddSettingsChangeMessage(item);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
[HarmonyPatch(typeof(NumberOption))]
|
|
public static class NumberOptionPatch
|
|
{
|
|
//private static bool IsVanillaServer => GameStates.CurrentServerType == GameStates.ServerType.Vanilla;
|
|
|
|
private static int IncrementMultiplier
|
|
{
|
|
get
|
|
{
|
|
if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) return 5;
|
|
if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)) return 10;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
[HarmonyPatch(nameof(NumberOption.Initialize))]
|
|
[HarmonyPrefix]
|
|
private static bool InitializePrefix(NumberOption __instance)
|
|
{
|
|
switch (__instance.Title)
|
|
{
|
|
case StringNames.GameVotingTime:
|
|
__instance.ValidRange = new(0, 600);
|
|
__instance.Value = (float)Math.Round(__instance.Value, 2);
|
|
break;
|
|
case StringNames.GameShortTasks:
|
|
case StringNames.GameLongTasks:
|
|
case StringNames.GameCommonTasks:
|
|
__instance.ValidRange = new(0, 85);
|
|
__instance.Value = (float)Math.Round(__instance.Value, 2);
|
|
break;
|
|
case StringNames.GameKillCooldown:
|
|
__instance.ValidRange = new(0, 180);
|
|
__instance.Increment = 0.5f;
|
|
__instance.Value = (float)Math.Round(__instance.Value, 2);
|
|
break;
|
|
case StringNames.GamePlayerSpeed:
|
|
__instance.Increment = 0.05f;
|
|
__instance.Value = Mathf.Clamp((float)Math.Round(__instance.Value, 2), __instance.ValidRange.min, __instance.ValidRange.max);
|
|
break;
|
|
case StringNames.GameCrewLight:
|
|
case StringNames.GameImpostorLight:
|
|
__instance.Increment = 0.05f;
|
|
__instance.Value = (float)Math.Round(__instance.Value, 2);
|
|
break;
|
|
case StringNames.GameNumImpostors:
|
|
__instance.ValidRange = new(0, Crowded.MaxImpostors);
|
|
__instance.Value = Mathf.Clamp((float)Math.Round(__instance.Value, 2), __instance.ValidRange.min, __instance.ValidRange.max);
|
|
if (DebugModeManager.AmDebugger) __instance.ValidRange.min = 0;
|
|
break;
|
|
case StringNames.CapacityLabel:
|
|
__instance.ValidRange = new(4, 127);
|
|
__instance.Value = Mathf.Clamp(__instance.Value, __instance.ValidRange.min, __instance.ValidRange.max);
|
|
break;
|
|
}
|
|
|
|
if (ModGameOptionsMenu.OptionList.TryGetValue(__instance, out int index))
|
|
{
|
|
OptionItem item = OptionItem.AllOptions[index];
|
|
__instance.TitleText.SetText(item.GetName());
|
|
item.OptionBehaviour = __instance;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
[HarmonyPatch(nameof(NumberOption.UpdateValue))]
|
|
[HarmonyPrefix]
|
|
private static bool UpdateValuePrefix(NumberOption __instance)
|
|
{
|
|
if (ModGameOptionsMenu.OptionList.TryGetValue(__instance, out int index))
|
|
{
|
|
OptionItem item = OptionItem.AllOptions[index];
|
|
|
|
switch (item)
|
|
{
|
|
case IntegerOptionItem integerOptionItem:
|
|
integerOptionItem.SetValue(integerOptionItem.Rule.GetNearestIndex(__instance.GetInt()), doSave: true, doSync: false);
|
|
break;
|
|
case FloatOptionItem floatOptionItem:
|
|
floatOptionItem.SetValue(floatOptionItem.Rule.GetNearestIndex(__instance.GetFloat()), doSave: true, doSync: false);
|
|
break;
|
|
}
|
|
|
|
NotificationPopperPatch.AddSettingsChangeMessage(item);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
[HarmonyPatch(nameof(NumberOption.FixedUpdate))]
|
|
[HarmonyPrefix]
|
|
private static bool FixedUpdatePrefix(NumberOption __instance)
|
|
{
|
|
if (ModGameOptionsMenu.OptionList.TryGetValue(__instance, out int index))
|
|
{
|
|
__instance.MinusBtn.SetInteractable(true);
|
|
__instance.PlusBtn.SetInteractable(true);
|
|
|
|
if (!Mathf.Approximately(__instance.oldValue, __instance.Value))
|
|
{
|
|
__instance.oldValue = __instance.Value;
|
|
__instance.ValueText.SetText(GetValueString(__instance, __instance.Value, OptionItem.AllOptions[index]));
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private static string GetValueString(NumberOption __instance, float value, OptionItem item)
|
|
{
|
|
if (__instance.ZeroIsInfinity && Mathf.Abs(value) < 0.0001f) return "<b>∞</b>";
|
|
|
|
return item == null ? value.ToString(__instance.FormatString) : item.GetString();
|
|
}
|
|
|
|
[HarmonyPatch(nameof(NumberOption.Increase))]
|
|
[HarmonyPrefix]
|
|
public static bool IncreasePrefix(NumberOption __instance)
|
|
{
|
|
if (Mathf.Approximately(__instance.Value, __instance.ValidRange.max))
|
|
{
|
|
__instance.Value = __instance.ValidRange.min;
|
|
__instance.UpdateValue();
|
|
__instance.OnValueChanged.Invoke(__instance);
|
|
return false;
|
|
}
|
|
|
|
float increment = IncrementMultiplier * __instance.Increment;
|
|
|
|
if (__instance.Value + increment < __instance.ValidRange.max)
|
|
{
|
|
__instance.Value += increment;
|
|
__instance.UpdateValue();
|
|
__instance.OnValueChanged.Invoke(__instance);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
[HarmonyPatch(nameof(NumberOption.Decrease))]
|
|
[HarmonyPrefix]
|
|
public static bool DecreasePrefix(NumberOption __instance)
|
|
{
|
|
if (Mathf.Approximately(__instance.Value, __instance.ValidRange.min))
|
|
{
|
|
__instance.Value = __instance.ValidRange.max;
|
|
__instance.UpdateValue();
|
|
__instance.OnValueChanged.Invoke(__instance);
|
|
return false;
|
|
}
|
|
|
|
float increment = IncrementMultiplier * __instance.Increment;
|
|
|
|
if (__instance.Value - increment > __instance.ValidRange.min)
|
|
{
|
|
__instance.Value -= increment;
|
|
__instance.UpdateValue();
|
|
__instance.OnValueChanged.Invoke(__instance);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
[HarmonyPatch(typeof(StringOption))]
|
|
public static class StringOptionPatch
|
|
{
|
|
private static long HelpShowEndTS;
|
|
internal static readonly Dictionary<StringOption, string> NameCache = new();
|
|
|
|
[HarmonyPatch(nameof(StringOption.Initialize))]
|
|
[HarmonyPrefix]
|
|
private static bool InitializePrefix(StringOption __instance)
|
|
{
|
|
if (__instance.name.StartsWith(nameof(OnlinePresetsManager))) return false;
|
|
|
|
if (ModGameOptionsMenu.OptionList.TryGetValue(__instance, out int index))
|
|
{
|
|
OptionItem item = OptionItem.AllOptions[index];
|
|
string name = item.GetName();
|
|
item.OptionBehaviour = __instance;
|
|
string name1 = name;
|
|
|
|
if (Main.CustomRoleValues.FindFirst(x => Translator.GetString($"{x}") == name1.RemoveHtmlTags(), out CustomRoles role))
|
|
{
|
|
if (role.ToString().Contains("GuardianAngel")) role = CustomRoles.GA;
|
|
|
|
name = name.RemoveHtmlTags();
|
|
|
|
switch (Options.UsePets.GetBool())
|
|
{
|
|
case false when role.OnlySpawnsWithPets():
|
|
name += Translator.GetString("RequiresPetIndicator");
|
|
break;
|
|
case true when role.PetActivatedAbility():
|
|
name += Translator.GetString("SupportsPetIndicator");
|
|
break;
|
|
}
|
|
|
|
if (role.IsExperimental()) name += $"<size=2>{Translator.GetString("ExperimentalRoleIndicator")}</size>";
|
|
if (role.IsGhostRole()) name += GetGhostRoleTeam(role);
|
|
if (role.IsDevFavoriteRole()) name += " <size=2><#00ffff>★</color></size>";
|
|
|
|
__instance.TitleText.fontWeight = FontWeight.Black;
|
|
__instance.TitleText.outlineColor = new(255, 255, 255, 255);
|
|
__instance.TitleText.outlineWidth = 0.04f;
|
|
__instance.LabelBackground.color = Utils.GetRoleColor(role);
|
|
__instance.TitleText.color = Color.white;
|
|
name = $"<size=3.5>{name}</size>";
|
|
SetupHelpIcon(role, __instance);
|
|
}
|
|
|
|
__instance.TitleText.SetText(name);
|
|
NameCache[__instance] = name;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private static void SetupHelpIcon(CustomRoles role, StringOption option)
|
|
{
|
|
Transform template = option.MinusBtn.transform;
|
|
bool cached = ModGameOptionsMenu.HelpIconList.TryGetValue(role, out Transform cache);
|
|
Transform icon = cached ? cache : ModGameOptionsMenu.Track(Object.Instantiate(template, template.parent, true));
|
|
icon.name = $"{role}HelpIcon";
|
|
var text = icon.GetComponentInChildren<TextMeshPro>();
|
|
text.SetText("?");
|
|
text.color = Color.white;
|
|
icon.FindChild("ButtonSprite").GetComponent<SpriteRenderer>().color = Color.black;
|
|
var gameOptionButton = icon.GetComponent<GameOptionButton>();
|
|
gameOptionButton.OnClick = new();
|
|
|
|
gameOptionButton.OnClick.AddListener((Action)(() =>
|
|
{
|
|
if (ModGameOptionsMenu.OptionList.TryGetValue(option, out int index))
|
|
{
|
|
OptionItem item = OptionItem.AllOptions[index];
|
|
string name = item.GetName();
|
|
|
|
if (Main.CustomRoleValues.FindFirst(x => Translator.GetString($"{x}") == name.RemoveHtmlTags(), out CustomRoles value))
|
|
{
|
|
string roleName = value.IsVanilla() ? value + "EHR" : value.ToString();
|
|
string str = Translator.GetString($"{roleName}InfoLong").FixRoleName(value);
|
|
string infoLong;
|
|
|
|
try { infoLong = CustomHnS.AllHnSRoles.Contains(value) ? str : str[(str.IndexOf('\n') + 1)..str.Split("\n\n")[0].Length]; }
|
|
catch { infoLong = str; }
|
|
|
|
GameObject.Find("PlayerOptionsMenu(Clone)").transform.FindChild("What Is This?").gameObject.SetActive(true);
|
|
GameSettingMenuPatch.GMButtons.Values.Do(x => x.gameObject.SetActive(false));
|
|
|
|
var info = $"{value.ToColoredString()}: {infoLong}";
|
|
GameSettingMenu.Instance.MenuDescriptionText.SetText(info);
|
|
|
|
long now = Utils.TimeStamp;
|
|
bool startCoRoutine = now > HelpShowEndTS;
|
|
HelpShowEndTS = now + 15;
|
|
if (startCoRoutine) GameSettingMenu.Instance.StartCoroutine(CoRoutine().WrapToIl2Cpp());
|
|
|
|
IEnumerator CoRoutine()
|
|
{
|
|
while (HelpShowEndTS > Utils.TimeStamp)
|
|
yield return new WaitForSecondsRealtime(1f);
|
|
|
|
GameObject gameObject = GameObject.Find("PlayerOptionsMenu(Clone)");
|
|
|
|
if (gameObject)
|
|
{
|
|
Transform findChild = gameObject.transform.FindChild("What Is This?");
|
|
if (findChild) findChild.gameObject.SetActive(false);
|
|
}
|
|
|
|
GameSettingMenuPatch.GMButtons.Values.Do(x => x.gameObject.SetActive(true));
|
|
}
|
|
}
|
|
}
|
|
}));
|
|
|
|
gameOptionButton.interactableColor = Color.black;
|
|
gameOptionButton.interactableHoveredColor = ModGameOptionsMenu.GetCurrentThemeColor();
|
|
// Only apply position offset on first creation, not on reuse (prevents icon drifting left on switching presets)
|
|
if (!cached)
|
|
icon.localPosition += new Vector3(-0.8f, 0f, 0f);
|
|
icon.SetAsLastSibling();
|
|
ModGameOptionsMenu.HelpIconList[role] = icon;
|
|
}
|
|
|
|
public static string GetGhostRoleTeam(CustomRoles role)
|
|
{
|
|
IGhostRole instance = GhostRolesManager.CreateGhostRoleInstance(role);
|
|
if (instance == null) return string.Empty;
|
|
|
|
Team team = instance.Team;
|
|
if ((int)team is 1 or 2 or 4 or 8) return $" <size=2>{GetColoredShortTeamName(team)}</size>";
|
|
|
|
Team[] teams = (int)team switch
|
|
{
|
|
3 => [Team.Impostor, Team.Neutral],
|
|
5 => [Team.Impostor, Team.Crewmate],
|
|
6 => [Team.Neutral, Team.Crewmate],
|
|
7 => [Team.Impostor, Team.Neutral, Team.Crewmate],
|
|
9 => [Team.Impostor, Team.Coven],
|
|
10 => [Team.Neutral, Team.Coven],
|
|
11 => [Team.Impostor, Team.Neutral, Team.Coven],
|
|
12 => [Team.Crewmate, Team.Coven],
|
|
13 => [Team.Impostor, Team.Crewmate, Team.Coven],
|
|
14 => [Team.Neutral, Team.Crewmate, Team.Coven],
|
|
15 => [Team.Impostor, Team.Neutral, Team.Crewmate, Team.Coven],
|
|
_ => []
|
|
};
|
|
|
|
return $" <size=2>{string.Join('/', teams.Select(GetColoredShortTeamName))}</size>";
|
|
|
|
string GetColoredShortTeamName(Team t) => Utils.ColorString(t.GetColor(), Translator.GetString($"ShortTeamName.{t}").ToUpper());
|
|
}
|
|
|
|
[HarmonyPatch(nameof(StringOption.UpdateValue))]
|
|
[HarmonyPrefix]
|
|
private static bool UpdateValuePrefix(StringOption __instance)
|
|
{
|
|
if (__instance.name.StartsWith(nameof(OnlinePresetsManager))) return false;
|
|
|
|
if (ModGameOptionsMenu.OptionList.TryGetValue(__instance, out int index))
|
|
{
|
|
OptionItem item = OptionItem.AllOptions[index];
|
|
item.SetValue(__instance.GetInt(), doSave: true, doSync: false);
|
|
NotificationPopperPatch.AddSettingsChangeMessage(item);
|
|
if (!NameCache.TryGetValue(__instance, out string name))
|
|
__instance.Initialize();
|
|
else
|
|
__instance.TitleText.SetText(name);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
[HarmonyPatch(nameof(StringOption.FixedUpdate))]
|
|
[HarmonyPrefix]
|
|
private static bool FixedUpdatePrefix(StringOption __instance)
|
|
{
|
|
if (__instance.name.StartsWith(nameof(OnlinePresetsManager)))
|
|
{
|
|
__instance.TitleText.SetText(__instance.name.Split(';')[1]);
|
|
return false;
|
|
}
|
|
|
|
if (ModGameOptionsMenu.OptionList.TryGetValue(__instance, out int index))
|
|
{
|
|
__instance.MinusBtn.SetInteractable(true);
|
|
__instance.PlusBtn.SetInteractable(true);
|
|
|
|
if (__instance.oldValue != __instance.Value && OptionItem.AllOptions[index] is StringOptionItem stringOptionItem)
|
|
{
|
|
__instance.oldValue = __instance.Value;
|
|
string selection = stringOptionItem.Selections[stringOptionItem.Rule.GetValueByIndex(__instance.Value)];
|
|
if (!stringOptionItem.noTranslation) selection = Translator.GetString(selection);
|
|
__instance.ValueText.SetText(selection);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
[HarmonyPatch(nameof(StringOption.Increase))]
|
|
[HarmonyPrefix]
|
|
public static bool IncreasePrefix(StringOption __instance)
|
|
{
|
|
if (__instance.Value == __instance.Values.Length - 1)
|
|
{
|
|
__instance.Value = 0;
|
|
__instance.UpdateValue();
|
|
__instance.OnValueChanged?.Invoke(__instance);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
[HarmonyPatch(nameof(StringOption.Decrease))]
|
|
[HarmonyPrefix]
|
|
public static bool DecreasePrefix(StringOption __instance)
|
|
{
|
|
if (__instance.Value == 0)
|
|
{
|
|
__instance.Value = __instance.Values.Length - 1;
|
|
__instance.UpdateValue();
|
|
__instance.OnValueChanged?.Invoke(__instance);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
[HarmonyPatch(typeof(GameSettingMenu))]
|
|
public static class GameSettingMenuPatch
|
|
{
|
|
public static readonly System.Collections.Generic.Dictionary<CustomGameMode, GameObject> GMButtons = [];
|
|
|
|
private static readonly Vector3 ButtonPositionLeft = new(-3.9f, -0.55f, 0f);
|
|
private static readonly Vector3 ButtonPositionRight = new(-2.4f, -0.55f, 0f);
|
|
|
|
private static readonly Vector3 ButtonSize = new(0.45f, 0.35f, 1f);
|
|
|
|
private static GameOptionsMenu TemplateGameOptionsMenu;
|
|
private static PassiveButton TemplateGameSettingsButton;
|
|
|
|
public static readonly System.Collections.Generic.Dictionary<TabGroup, PassiveButton> ModSettingsButtons = [];
|
|
public static readonly System.Collections.Generic.Dictionary<TabGroup, GameOptionsMenu> ModSettingsTabs = [];
|
|
|
|
public static long LastPresetChange;
|
|
public static bool ChangingPreset;
|
|
|
|
public static FreeChatInputField InputField;
|
|
private static System.Collections.Generic.List<OptionItem> HiddenBySearch = [];
|
|
public static Action SearchForOptionsAction;
|
|
|
|
private static int NumImpsOnOpen = 1;
|
|
private static int MinImpsOnOpen = 1;
|
|
private static int MaxImpsOnOpen = 1;
|
|
|
|
private static float VanillaKillCooldownOnOpen = 1;
|
|
private static float ModdedKillCooldownOnOpen = 1;
|
|
|
|
[HarmonyPatch(nameof(GameSettingMenu.Start))]
|
|
[HarmonyPrefix]
|
|
[HarmonyPriority(Priority.First)]
|
|
public static void StartPrefix(ref GameSettingMenu __instance)
|
|
{
|
|
try
|
|
{
|
|
(OptionItem minSetting, OptionItem maxSetting) = Options.FactionMinMaxSettings[Team.Impostor];
|
|
MinImpsOnOpen = minSetting.GetInt();
|
|
MaxImpsOnOpen = maxSetting.GetInt();
|
|
NumImpsOnOpen = Main.NormalOptions.NumImpostors;
|
|
|
|
VanillaKillCooldownOnOpen = Main.NormalOptions.KillCooldown;
|
|
ModdedKillCooldownOnOpen = Options.FallBackKillCooldownValue.GetFloat();
|
|
}
|
|
catch (Exception e) { Utils.ThrowException(e); }
|
|
|
|
foreach (TabGroup tab in Main.TabGroupValues)
|
|
{
|
|
bool cached = ModSettingsButtons.TryGetValue(tab, out PassiveButton cache);
|
|
PassiveButton button = cached ? cache : ModGameOptionsMenu.Track(Object.Instantiate(TemplateGameSettingsButton, __instance.GameSettingsButton.transform.parent));
|
|
if (cached) button.transform.SetParent(__instance.GameSettingsButton.transform.parent, false);
|
|
button.gameObject.SetActive(tab.IsRelevant());
|
|
button.name = "Button_" + tab;
|
|
var label = button.GetComponentInChildren<TextMeshPro>();
|
|
label.DestroyTranslator();
|
|
label.SetText(Translator.GetString($"TabGroup.{tab}"));
|
|
label.color = Color.white;
|
|
button.activeTextColor = button.inactiveTextColor = Color.white;
|
|
button.selectedTextColor = new(0.7f, 0.7f, 0.7f);
|
|
Color color = tab.GetTabColor();
|
|
|
|
button.inactiveSprites.GetComponent<SpriteRenderer>().color = color;
|
|
button.activeSprites.GetComponent<SpriteRenderer>().color = color;
|
|
button.selectedSprites.GetComponent<SpriteRenderer>().color = color;
|
|
|
|
// ReSharper disable once PossibleLossOfFraction
|
|
Vector3 offset = new(0f, (0.3f * (((int)tab + 1) / 2)), 0f);
|
|
button.transform.localPosition = (((int)tab + 1) % 2 == 0 ? ButtonPositionLeft : ButtonPositionRight) - offset;
|
|
button.transform.localScale = ButtonSize;
|
|
|
|
var buttonComponent = button.GetComponent<PassiveButton>();
|
|
buttonComponent.OnClick = new();
|
|
buttonComponent.OnClick.AddListener((Action)(() => GameSettingMenu.Instance.ChangeTab((int)tab + 3, false)));
|
|
|
|
ModSettingsButtons[tab] = button;
|
|
}
|
|
|
|
foreach (TabGroup tab in Main.TabGroupValues)
|
|
{
|
|
bool cached = ModSettingsTabs.TryGetValue(tab, out GameOptionsMenu cache);
|
|
GameOptionsMenu setTab = cached ? cache : ModGameOptionsMenu.Track(Object.Instantiate(TemplateGameOptionsMenu, __instance.GameSettingsTab.transform.parent));
|
|
if (cached) setTab.transform.SetParent(__instance.GameSettingsTab.transform.parent, false);
|
|
setTab.name = "tab_" + tab;
|
|
setTab.gameObject.SetActive(false);
|
|
|
|
ModSettingsTabs[tab] = setTab;
|
|
}
|
|
|
|
foreach (TabGroup tab in Main.TabGroupValues)
|
|
{
|
|
if (ModSettingsButtons.TryGetValue(tab, out PassiveButton button))
|
|
__instance.ControllerSelectable.Add(button);
|
|
}
|
|
|
|
HiddenBySearch.Do(x => x.SetHidden(false));
|
|
HiddenBySearch.Clear();
|
|
|
|
SetupExtendedUI(__instance);
|
|
}
|
|
|
|
private static readonly System.Collections.Generic.Dictionary<int, GameObject> ExtraObjectsCache = [];
|
|
|
|
// Thanks: Drakos for the preset button and search bar code (https://github.com/0xDrMoe/TownofHost-Enhanced/pull/1115)
|
|
private static void SetupExtendedUI(GameSettingMenu __instance)
|
|
{
|
|
Transform parentLeftPanel = __instance.GamePresetsButton.transform.parent;
|
|
bool cached = ExtraObjectsCache.TryGetValue(0, out GameObject cache);
|
|
GameObject preset = cached ? cache : ModGameOptionsMenu.Track(Object.Instantiate(GameObject.Find("ModeValue"), parentLeftPanel));
|
|
if (cached)
|
|
{
|
|
preset.transform.SetParent(parentLeftPanel, false);
|
|
preset.SetActive(true);
|
|
}
|
|
ExtraObjectsCache[0] = preset;
|
|
|
|
preset.transform.localPosition = new(-1.8f, 0f, -2f);
|
|
preset.transform.localScale = new(0.65f, 0.63f, 1f);
|
|
var renderer = preset.GetComponentInChildren<SpriteRenderer>();
|
|
renderer.color = Color.white;
|
|
renderer.sprite = null;
|
|
|
|
var presetTmp = preset.GetComponentInChildren<TextMeshPro>();
|
|
presetTmp.DestroyTranslator();
|
|
presetTmp.SetText(Translator.GetString($"Preset_{OptionItem.CurrentPreset + 1}"));
|
|
|
|
bool russian = TranslationController.Instance.currentLanguage.languageID == SupportedLangs.Russian;
|
|
float size = !russian ? 2.45f : 1.45f;
|
|
presetTmp.fontSizeMax = presetTmp.fontSizeMin = size;
|
|
|
|
|
|
var gameSettingsLabel = __instance.GameSettingsButton.transform.parent.parent.FindChild("GameSettingsLabel").GetComponent<TextMeshPro>();
|
|
gameSettingsLabel.DestroyTranslator();
|
|
gameSettingsLabel.SetText($"<size=50%>{Translator.GetString($"Mode{Options.CurrentGameMode}")}</size>\n");
|
|
gameSettingsLabel.transform.localPosition += new Vector3(0f, 0.1f, 0f);
|
|
|
|
if (russian)
|
|
{
|
|
gameSettingsLabel.transform.localScale = new(0.7f, 0.7f, 1f);
|
|
gameSettingsLabel.transform.localPosition = new(-3.77f, 1.62f, -4);
|
|
}
|
|
|
|
Vector3 gameSettingsLabelPos = gameSettingsLabel.transform.localPosition;
|
|
|
|
|
|
GameObject tempMinus = GameObject.Find("MinusButton").gameObject;
|
|
cached = ExtraObjectsCache.TryGetValue(1, out cache);
|
|
GameObject gMinus = cached ? cache : ModGameOptionsMenu.Track(Object.Instantiate(__instance.GamePresetsButton.gameObject, preset.transform));
|
|
if (cached) gMinus.transform.SetParent(preset.transform, false);
|
|
ExtraObjectsCache[1] = gMinus;
|
|
gMinus.gameObject.SetActive(true);
|
|
gMinus.transform.localScale = new(0.08f, 0.4f, 1f);
|
|
|
|
var mLabel = gMinus.transform.Find("FontPlacer/Text_TMP").GetComponent<TextMeshPro>();
|
|
mLabel.alignment = TextAlignmentOptions.Center;
|
|
mLabel.DestroyTranslator();
|
|
mLabel.SetText("-");
|
|
if (!cached) mLabel.transform.localPosition = new(mLabel.transform.localPosition.x, mLabel.transform.localPosition.y + 0.26f, mLabel.transform.localPosition.z);
|
|
mLabel.color = Color.white;
|
|
mLabel.SetFaceColor(new Color(255f, 255f, 255f));
|
|
mLabel.transform.localScale = new(12f, 4f, 1f);
|
|
|
|
var minus = gMinus.GetComponent<PassiveButton>();
|
|
minus.OnClick.RemoveAllListeners();
|
|
|
|
minus.OnClick.AddListener((Action)(() =>
|
|
{
|
|
ChangingPreset = true;
|
|
LastPresetChange = Utils.TimeStamp;
|
|
Options.Preset--;
|
|
GameOptionsMenuPatch.ReCreateAllSettings();
|
|
GameOptionsMenuPatch.RefreshSettingValues();
|
|
GameOptionsMenuPatch.RefreshTabButtons();
|
|
presetTmp.SetText(Translator.GetString($"Preset_{OptionItem.CurrentPreset + 1}"));
|
|
gameSettingsLabel.SetText($"<size=50%>{Translator.GetString($"Mode{Options.CurrentGameMode}")}</size>\n");
|
|
Logger.Info($"Current preset: {OptionItem.CurrentPreset + 1}", "GameOptionsMenuPatch");
|
|
ChangingPreset = false;
|
|
NotificationPopperPatch.AddSettingsChangeMessage(Options.Preset);
|
|
}));
|
|
|
|
minus.activeTextColor = minus.inactiveTextColor = minus.disabledTextColor = minus.selectedTextColor = Color.white;
|
|
minus.transform.localPosition = new(-2f, -3.37f, -4f);
|
|
|
|
var inactiveSprites = minus.inactiveSprites.GetComponent<SpriteRenderer>();
|
|
var activeSprites = minus.activeSprites.GetComponent<SpriteRenderer>();
|
|
var selectedSprites = minus.selectedSprites.GetComponent<SpriteRenderer>();
|
|
|
|
inactiveSprites.sprite = tempMinus.GetComponentInChildren<SpriteRenderer>().sprite;
|
|
activeSprites.sprite = tempMinus.GetComponentInChildren<SpriteRenderer>().sprite;
|
|
selectedSprites.sprite = tempMinus.GetComponentInChildren<SpriteRenderer>().sprite;
|
|
|
|
inactiveSprites.color = new Color32(55, 59, 60, 255);
|
|
activeSprites.color = new Color32(0, 165, 255, 255);
|
|
selectedSprites.color = new Color32(0, 255, 165, 255);
|
|
|
|
|
|
cached = ExtraObjectsCache.TryGetValue(2, out cache);
|
|
GameObject plusFab = cached ? cache : ModGameOptionsMenu.Track(Object.Instantiate(gMinus, preset.transform));
|
|
if (cached)
|
|
{
|
|
plusFab.transform.SetParent(preset.transform, false);
|
|
plusFab.SetActive(true);
|
|
}
|
|
ExtraObjectsCache[2] = plusFab;
|
|
var plusLabel = plusFab.transform.Find("FontPlacer/Text_TMP").GetComponent<TextMeshPro>();
|
|
plusLabel.alignment = TextAlignmentOptions.Center;
|
|
plusLabel.DestroyTranslator();
|
|
plusLabel.SetText("+");
|
|
plusLabel.color = Color.white;
|
|
if (!cached) plusLabel.transform.localPosition = new(plusLabel.transform.localPosition.x, plusLabel.transform.localPosition.y + 0.26f, plusLabel.transform.localPosition.z);
|
|
plusLabel.transform.localScale = new(18f, 4f, 1f);
|
|
|
|
var plus = plusFab.GetComponent<PassiveButton>();
|
|
plus.OnClick.RemoveAllListeners();
|
|
|
|
plus.OnClick.AddListener((Action)(() =>
|
|
{
|
|
ChangingPreset = true;
|
|
LastPresetChange = Utils.TimeStamp;
|
|
Options.Preset++;
|
|
GameOptionsMenuPatch.ReCreateAllSettings();
|
|
GameOptionsMenuPatch.RefreshSettingValues();
|
|
GameOptionsMenuPatch.RefreshTabButtons();
|
|
presetTmp.SetText(Translator.GetString($"Preset_{OptionItem.CurrentPreset + 1}"));
|
|
gameSettingsLabel.SetText($"<size=50%>{Translator.GetString($"Mode{Options.CurrentGameMode}")}</size>\n");
|
|
Logger.Info($"Current preset: {OptionItem.CurrentPreset + 1}", "GameOptionsMenuPatch");
|
|
ChangingPreset = false;
|
|
NotificationPopperPatch.AddSettingsChangeMessage(Options.Preset);
|
|
}));
|
|
|
|
plus.activeTextColor = plus.inactiveTextColor = plus.disabledTextColor = plus.selectedTextColor = Color.white;
|
|
plus.transform.localPosition = new(-0.4f, -3.37f, -4f);
|
|
|
|
|
|
GameObject.Find("PlayerOptionsMenu(Clone)").transform.FindChild("What Is This?").gameObject.SetActive(false);
|
|
|
|
var gms = Main.CustomGameModeValues[..^1].ToList();
|
|
gms.Remove(CustomGameMode.TheMindGame);
|
|
|
|
if (SubmergedCompatibility.Loaded && Main.NormalOptions.MapId == 6)
|
|
gms.Remove(CustomGameMode.NaturalDisasters);
|
|
|
|
if (Main.LIMap)
|
|
{
|
|
gms.Remove(CustomGameMode.CaptureTheFlag);
|
|
gms.Remove(CustomGameMode.Quiz);
|
|
gms.Remove(CustomGameMode.BedWars);
|
|
gms.Remove(CustomGameMode.Deathrace);
|
|
}
|
|
|
|
const int totalCols = 2;
|
|
int itemsPerCol = Mathf.CeilToInt(gms.Count / (float)totalCols);
|
|
|
|
GMButtons.Clear();
|
|
|
|
for (var index = 0; index < gms.Count; index++)
|
|
{
|
|
CustomGameMode gm = gms[index];
|
|
|
|
cached = GMButtons.TryGetValue(gm, out cache);
|
|
var gmButton = cached ? cache : ModGameOptionsMenu.Track(Object.Instantiate(gMinus, gameSettingsLabel.transform, true));
|
|
if (cached)
|
|
{
|
|
gmButton.transform.SetParent(gameSettingsLabel.transform, false);
|
|
gmButton.SetActive(true);
|
|
}
|
|
gmButton.transform.localPosition = new Vector3((((index / itemsPerCol) - ((totalCols - 1) / 2f)) * 1.4f) + 0.16f, gameSettingsLabelPos.y - 1.9f - (0.17f * (index % itemsPerCol)), -1f);
|
|
gmButton.transform.localScale = new(0.4f, 0.22f, 1f);
|
|
var color = Main.GameModeColors[gm];
|
|
var gmButtonTmp = gmButton.transform.Find("FontPlacer/Text_TMP").GetComponent<TextMeshPro>();
|
|
gmButtonTmp.alignment = TextAlignmentOptions.Center;
|
|
gmButtonTmp.DestroyTranslator();
|
|
gmButtonTmp.SetText(Translator.GetString(gm.ToString()).ToUpper());
|
|
gmButtonTmp.color = color;
|
|
gmButtonTmp.transform.localPosition = new(gameSettingsLabelPos.x + 3.35f, gameSettingsLabelPos.y - 1.62f, gameSettingsLabelPos.z);
|
|
gmButtonTmp.transform.localScale = new(1f, 1f, 1f);
|
|
|
|
var gmPassiveButton = gmButton.GetComponent<PassiveButton>();
|
|
gmPassiveButton.OnClick.RemoveAllListeners();
|
|
gmPassiveButton.OnClick.AddListener((Action)(() =>
|
|
{
|
|
if (ModSettingsTabLoaded && !((TabGroup)(ModGameOptionsMenu.TabIndex - 3)).IsRelevant(gm))
|
|
GameSettingMenu.Instance.ChangeTab(4, false);
|
|
Options.GameMode.SetValue((int)gm - 1);
|
|
GameOptionsMenuPatch.ReCreateSettings(TabGroup.GameSettings);
|
|
if (gm is CustomGameMode.Standard or CustomGameMode.HideAndSeek)
|
|
{
|
|
GameOptionsMenuPatch.ReCreateSettings(TabGroup.ImpostorRoles);
|
|
GameOptionsMenuPatch.ReCreateSettings(TabGroup.CrewmateRoles);
|
|
GameOptionsMenuPatch.ReCreateSettings(TabGroup.NeutralRoles);
|
|
}
|
|
GameOptionsMenuPatch.RefreshTabButtons();
|
|
GameOptionsMenuPatch.RefreshCheckMarkColors();
|
|
gameSettingsLabel.SetText($"<size=50%>{Translator.GetString($"Mode{Options.CurrentGameMode}")}</size>\n");
|
|
}));
|
|
gmPassiveButton.activeTextColor = Utils.GetTextColor(color);
|
|
gmPassiveButton.inactiveTextColor = color;
|
|
gmPassiveButton.activeSprites.GetComponent<SpriteRenderer>().color = color;
|
|
|
|
GMButtons[gm] = gmButton;
|
|
}
|
|
|
|
|
|
if (!HudManager.InstanceExists) return;
|
|
|
|
FreeChatInputField freeChatField = HudManager.Instance.Chat.freeChatField;
|
|
cached = InputField;
|
|
FreeChatInputField field = cached ? InputField : ModGameOptionsMenu.Track(Object.Instantiate(freeChatField, parentLeftPanel.parent));
|
|
if (cached)
|
|
{
|
|
field.transform.SetParent(parentLeftPanel.parent, false);
|
|
field.gameObject.SetActive(true);
|
|
}
|
|
field.transform.localScale = new(0.3f, 0.59f, 1);
|
|
field.transform.localPosition = new(-0.47f, 2.15f, -5f);
|
|
field.textArea.outputText.transform.localScale = new(3.5f, 2f, 1f);
|
|
field.textArea.outputText.font = plusLabel.font;
|
|
field.charCountText.enabled = false;
|
|
|
|
InputField = field;
|
|
|
|
Transform button = field.transform.FindChild("ChatSendButton");
|
|
|
|
Transform buttonNormal = button.FindChild("Normal");
|
|
Transform buttonHover = button.FindChild("Hover");
|
|
Transform buttonDisabled = button.FindChild("Disabled");
|
|
|
|
Object.Destroy(buttonNormal.FindChild("Icon").GetComponent<SpriteRenderer>());
|
|
Object.Destroy(buttonHover.FindChild("Icon").GetComponent<SpriteRenderer>());
|
|
Object.Destroy(buttonDisabled.FindChild("Icon").GetComponent<SpriteRenderer>());
|
|
Object.Destroy(button.transform.FindChild("Text").GetComponent<TextMeshPro>());
|
|
|
|
Transform buttonNormalBackground = buttonNormal.FindChild("Background");
|
|
Transform buttonHoverBackground = buttonHover.FindChild("Background");
|
|
Transform buttonDisabledBackground = buttonDisabled.FindChild("Background");
|
|
|
|
buttonNormalBackground.GetComponent<SpriteRenderer>().sprite = Utils.LoadSprite("EHR.Resources.Images.SearchIconActive.png", 100f);
|
|
buttonHoverBackground.GetComponent<SpriteRenderer>().sprite = Utils.LoadSprite("EHR.Resources.Images.SearchIconHover.png", 100f);
|
|
buttonDisabledBackground.GetComponent<SpriteRenderer>().sprite = Utils.LoadSprite("EHR.Resources.Images.SearchIcon.png", 100f);
|
|
|
|
if (russian)
|
|
{
|
|
Vector3 fixedScale = new(0.7f, 1f, 1f);
|
|
buttonNormalBackground.transform.localScale = fixedScale;
|
|
buttonHoverBackground.transform.localScale = fixedScale;
|
|
buttonDisabledBackground.transform.localScale = fixedScale;
|
|
}
|
|
|
|
|
|
var passiveButton = button.GetComponent<PassiveButton>();
|
|
|
|
passiveButton.OnClick = new();
|
|
passiveButton.OnClick.AddListener((Action)(() => SearchForOptions(field)));
|
|
|
|
SearchForOptionsAction = () =>
|
|
{
|
|
if (field.textArea.text != string.Empty) SearchForOptions(field);
|
|
};
|
|
|
|
return;
|
|
|
|
static void SearchForOptions(FreeChatInputField textField)
|
|
{
|
|
if (ModGameOptionsMenu.TabIndex < 3) return;
|
|
|
|
HiddenBySearch.Do(x => x.SetHidden(false));
|
|
string text = textField.textArea.text.Trim().ToLower();
|
|
var modTab = (TabGroup)(ModGameOptionsMenu.TabIndex - 3);
|
|
OptionItem[] optionItems = Options.GroupedOptions[modTab];
|
|
System.Collections.Generic.List<OptionItem> result = optionItems.Where(x => x.Parent == null && !x.IsCurrentlyHidden() && !Translator.GetString($"{x.Name}").Contains(text, StringComparison.OrdinalIgnoreCase)).ToList();
|
|
HiddenBySearch = result;
|
|
System.Collections.Generic.List<OptionItem> searchWinners = optionItems.Where(x => x.Parent == null && !x.IsCurrentlyHidden() && !result.Contains(x)).ToList();
|
|
|
|
if (searchWinners.Count == 0 || !ModSettingsTabs.TryGetValue(modTab, out GameOptionsMenu gameSettings) || !gameSettings)
|
|
{
|
|
HiddenBySearch.Clear();
|
|
Logger.SendInGame(Translator.GetString("SearchNoResult"), Palette.Orange);
|
|
return;
|
|
}
|
|
|
|
result.ForEach(x => x.SetHidden(true));
|
|
|
|
gameSettings.scrollBar.ScrollToTop();
|
|
GameOptionsMenuPatch.ReCreateSettings(gameSettings);
|
|
textField.Clear();
|
|
}
|
|
}
|
|
|
|
private static void SetDefaultButton(GameSettingMenu __instance)
|
|
{
|
|
__instance.GamePresetsButton.gameObject.SetActive(false);
|
|
|
|
PassiveButton gameSettingButton = __instance.GameSettingsButton;
|
|
gameSettingButton.transform.localPosition = new(-3f, -0.55f, 0f);
|
|
|
|
var textLabel = gameSettingButton.GetComponentInChildren<TextMeshPro>();
|
|
textLabel.DestroyTranslator();
|
|
textLabel.SetText(Translator.GetString("TabGroup.VanillaSettings"));
|
|
|
|
gameSettingButton.activeTextColor = gameSettingButton.inactiveTextColor = Color.white;
|
|
gameSettingButton.selectedTextColor = Color.gray;
|
|
|
|
gameSettingButton.inactiveSprites.GetComponent<SpriteRenderer>().color = Color.black;
|
|
gameSettingButton.activeSprites.GetComponent<SpriteRenderer>().color = Color.gray;
|
|
gameSettingButton.selectedSprites.GetComponent<SpriteRenderer>().color = Color.black;
|
|
|
|
gameSettingButton.transform.localPosition = ButtonPositionLeft;
|
|
gameSettingButton.transform.localScale = ButtonSize;
|
|
|
|
__instance.RoleSettingsButton.gameObject.SetActive(false);
|
|
|
|
__instance.DefaultButtonSelected = gameSettingButton;
|
|
__instance.ControllerSelectable = new();
|
|
__instance.ControllerSelectable.Add(gameSettingButton);
|
|
}
|
|
|
|
private static bool ModSettingsTabLoaded;
|
|
|
|
[HarmonyPatch(nameof(GameSettingMenu.ChangeTab))]
|
|
[HarmonyPrefix]
|
|
public static bool ChangeTabPrefix(GameSettingMenu __instance, ref int tabNum, [HarmonyArgument(1)] bool previewOnly)
|
|
{
|
|
if (HiddenBySearch.Count > 0)
|
|
{
|
|
HiddenBySearch.Do(x => x.SetHidden(false));
|
|
|
|
if (ModSettingsTabs.TryGetValue((TabGroup)(ModGameOptionsMenu.TabIndex - 3), out GameOptionsMenu gameSettings) && gameSettings)
|
|
GameOptionsMenuPatch.ReCreateSettings(gameSettings);
|
|
|
|
HiddenBySearch.Clear();
|
|
}
|
|
|
|
if (!previewOnly || tabNum != 1) ModGameOptionsMenu.TabIndex = tabNum;
|
|
|
|
GameOptionsMenu settingsTab;
|
|
PassiveButton button;
|
|
|
|
if ((previewOnly && Controller.currentTouchType == Controller.TouchType.Joystick) || !previewOnly)
|
|
{
|
|
TabGroup[] tabGroups = Main.TabGroupValues;
|
|
|
|
foreach (TabGroup tab in tabGroups)
|
|
{
|
|
if (ModSettingsTabs.TryGetValue(tab, out settingsTab) && settingsTab)
|
|
settingsTab.gameObject.SetActive(false);
|
|
}
|
|
|
|
foreach (TabGroup tab in tabGroups)
|
|
{
|
|
if (ModSettingsButtons.TryGetValue(tab, out button) && button)
|
|
button.SelectButton(false);
|
|
}
|
|
}
|
|
|
|
if (tabNum < 3) return true;
|
|
|
|
var tabGroup = (TabGroup)(tabNum - 3);
|
|
|
|
if (!OnlinePresetsManager.PresetsLoaded && tabGroup == TabGroup.PresetExplorer)
|
|
{
|
|
__instance.StartCoroutine(
|
|
OnlinePresetsManager.FetchPresetList(list =>
|
|
{
|
|
OnlinePresetsManager.CachedPresets = list;
|
|
OnlinePresetsManager.PresetsLoaded = true;
|
|
if (ModSettingsTabs.TryGetValue(tabGroup, out settingsTab) && settingsTab)
|
|
OnlinePresetsManager.CreatePresetExplorerUI(settingsTab);
|
|
}).WrapToIl2Cpp()
|
|
);
|
|
}
|
|
|
|
if ((previewOnly && Controller.currentTouchType == Controller.TouchType.Joystick) || !previewOnly)
|
|
{
|
|
__instance.PresetsTab.gameObject.SetActive(false);
|
|
__instance.GameSettingsTab.gameObject.SetActive(false);
|
|
__instance.RoleSettingsTab.gameObject.SetActive(false);
|
|
__instance.GamePresetsButton.SelectButton(false);
|
|
__instance.GameSettingsButton.SelectButton(false);
|
|
__instance.RoleSettingsButton.SelectButton(false);
|
|
|
|
if (ModSettingsTabs.TryGetValue(tabGroup, out settingsTab) && settingsTab)
|
|
{
|
|
settingsTab.gameObject.SetActive(true);
|
|
__instance.MenuDescriptionText.DestroyTranslator();
|
|
__instance.MenuDescriptionText.SetText(Translator.GetString("TabInfoTip"));
|
|
}
|
|
}
|
|
|
|
if (previewOnly)
|
|
{
|
|
__instance.ToggleLeftSideDarkener(false);
|
|
__instance.ToggleRightSideDarkener(true);
|
|
return false;
|
|
}
|
|
|
|
__instance.ToggleLeftSideDarkener(true);
|
|
__instance.ToggleRightSideDarkener(false);
|
|
|
|
if (ModSettingsButtons.TryGetValue(tabGroup, out button) && button)
|
|
button.SelectButton(true);
|
|
|
|
if (tabGroup == TabGroup.GameSettings)
|
|
ModSettingsTabLoaded = true;
|
|
|
|
return false;
|
|
}
|
|
|
|
[HarmonyPatch(nameof(GameSettingMenu.OnEnable))]
|
|
[HarmonyPrefix]
|
|
private static bool OnEnablePrefix(GameSettingMenu __instance)
|
|
{
|
|
if (!TemplateGameOptionsMenu)
|
|
{
|
|
TemplateGameOptionsMenu = ModGameOptionsMenu.Track(Object.Instantiate(__instance.GameSettingsTab, __instance.GameSettingsTab.transform.parent));
|
|
TemplateGameOptionsMenu.gameObject.SetActive(false);
|
|
}
|
|
|
|
if (!TemplateGameSettingsButton)
|
|
{
|
|
TemplateGameSettingsButton = ModGameOptionsMenu.Track(Object.Instantiate(__instance.GameSettingsButton, __instance.GameSettingsButton.transform.parent));
|
|
TemplateGameSettingsButton.gameObject.SetActive(false);
|
|
}
|
|
|
|
SetDefaultButton(__instance);
|
|
|
|
ControllerManager.Instance.OpenOverlayMenu(__instance.name, __instance.BackButton, __instance.DefaultButtonSelected, __instance.ControllerSelectable);
|
|
if (HudManager.InstanceExists) HudManager.Instance.menuNavigationPrompts.SetActive(false);
|
|
if (Controller.currentTouchType != Controller.TouchType.Joystick) __instance.ChangeTab(1, false);
|
|
|
|
__instance.StartCoroutine(__instance.CoSelectDefault());
|
|
|
|
return false;
|
|
}
|
|
|
|
private static Transform TempParent;
|
|
|
|
[HarmonyPatch(nameof(GameSettingMenu.Close))]
|
|
[HarmonyPrefix]
|
|
public static void Close_Prefix()
|
|
{
|
|
if (TempParent == null)
|
|
{
|
|
var go = new GameObject("EHR Settings UI Cache Root");
|
|
Object.DontDestroyOnLoad(go);
|
|
go.SetActive(true);
|
|
TempParent = go.transform;
|
|
}
|
|
|
|
GameOptionsMenuPatch._reCreateGeneration++;
|
|
|
|
if (GameOptionsMenuPatch._reCreateAllCoroutine != null)
|
|
{
|
|
Main.Instance.StopCoroutine(GameOptionsMenuPatch._reCreateAllCoroutine);
|
|
GameOptionsMenuPatch._reCreateAllCoroutine = null;
|
|
}
|
|
|
|
// Clear GMButton listeners before hiding them so they can't fire stale callbacks
|
|
foreach (var x in GMButtons.Values)
|
|
{
|
|
var pb = x.GetComponent<PassiveButton>();
|
|
if (pb) pb.OnClick.RemoveAllListeners();
|
|
SetTempParent(x);
|
|
}
|
|
|
|
foreach (var x in ModSettingsTabs.Values) SetTempParent(x.gameObject);
|
|
foreach (var x in ModSettingsButtons.Values) SetTempParent(x.gameObject);
|
|
foreach (var x in ExtraObjectsCache.Values) SetTempParent(x);
|
|
foreach (var x in GMButtons.Values) SetTempParent(x);
|
|
SetTempParent(InputField.gameObject);
|
|
return;
|
|
|
|
void SetTempParent(GameObject go)
|
|
{
|
|
go.gameObject.transform.SetParent(TempParent, false);
|
|
go.gameObject.SetActive(false);
|
|
}
|
|
}
|
|
|
|
[HarmonyPatch(nameof(GameSettingMenu.Close))]
|
|
[HarmonyPostfix]
|
|
public static void Close_Postfix()
|
|
{
|
|
try
|
|
{
|
|
int numImpostors = Main.NormalOptions.NumImpostors;
|
|
(OptionItem minSetting, OptionItem maxSetting) = Options.FactionMinMaxSettings[Team.Impostor];
|
|
|
|
if (numImpostors != NumImpsOnOpen && MinImpsOnOpen == minSetting.GetInt() && MaxImpsOnOpen == maxSetting.GetInt())
|
|
{
|
|
minSetting.SetValue(numImpostors);
|
|
maxSetting.SetValue(numImpostors);
|
|
Logger.SendInGame(string.Format(Translator.GetString("MinMaxModdedImpCountsSettingsChangedAuto"), numImpostors));
|
|
}
|
|
|
|
|
|
float vanillaKillCooldown = Main.NormalOptions.KillCooldown;
|
|
float moddedKillCooldown = Options.FallBackKillCooldownValue.GetFloat();
|
|
|
|
bool vanillaChanged = !Mathf.Approximately(vanillaKillCooldown, VanillaKillCooldownOnOpen);
|
|
bool moddedChanged = !Mathf.Approximately(moddedKillCooldown, ModdedKillCooldownOnOpen);
|
|
|
|
if (!Mathf.Approximately(vanillaKillCooldown, moddedKillCooldown) && vanillaChanged && !moddedChanged)
|
|
Options.FallBackKillCooldownValue.SetValue((int)Math.Round((Math.Round(vanillaKillCooldown, 1) / 0.5f) - 1));
|
|
}
|
|
catch (Exception e) { Utils.ThrowException(e); }
|
|
|
|
try
|
|
{
|
|
if (Options.AutoGMRotationRecompileOnClose)
|
|
Options.CompileAutoGMRotationSettings();
|
|
}
|
|
catch (Exception e) { Utils.ThrowException(e); }
|
|
|
|
Options.AutoSetFactionMinMaxSettings();
|
|
|
|
NotificationPopperPatch.ReleaseRpcs();
|
|
}
|
|
}
|
|
|
|
[HarmonyPatch(typeof(FreeChatInputField), nameof(FreeChatInputField.UpdateCharCount))]
|
|
public static class FixInputChatField
|
|
{
|
|
public static bool Prefix(FreeChatInputField __instance)
|
|
{
|
|
if (GameSettingMenuPatch.InputField && __instance == GameSettingMenuPatch.InputField && __instance.gameObject.activeSelf)
|
|
{
|
|
Vector2 size = __instance.Background.size;
|
|
size.y = Math.Max(0.62f, __instance.textArea.TextHeight + 0.2f);
|
|
__instance.Background.size = size;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
[HarmonyPatch(typeof(ChatController), nameof(ChatController.Update))]
|
|
public static class FixDarkThemeForSearchBar
|
|
{
|
|
public static void Postfix()
|
|
{
|
|
if (!GameSettingMenu.Instance) return;
|
|
|
|
FreeChatInputField field = GameSettingMenuPatch.InputField;
|
|
|
|
if (field && field.gameObject.activeSelf)
|
|
{
|
|
field.background.color = new Color32(40, 40, 40, byte.MaxValue);
|
|
field.textArea.compoText.Color(Color.white);
|
|
field.textArea.outputText.color = Color.white;
|
|
}
|
|
}
|
|
}
|