Files
EndlessHostRoles/Modules/OptionItem/OptionItem.cs
T
TommyXL 6baebb12a9 Lobby View Settings (#521)
* First

* RoleTabs

* Add fix in text

* Fix Max title and move pos

* Fix LanguageID in some languages

* Fix

* Fix id

* Some fix and changes

* Fix role settings

* Changes

* Recode Y positions for role settings

* Try

* Hide settings/roles when categoryHeaderRoleOrigin was pressed

* Viwe supprort gamemodes & fix collapsed sections Y pos

* Set size labelBackground & Hide Option Shower

* Fix role category header color

* Rename

* Add right and left arrow button

* Some fixes

* Colored settings & Fix children settings roles

* Fix chat for modded clients when lobby view open

* Some fix

* Optimaze DrawOptions() and DrawRoles()

* Change role text scale

* Change

* Add colored check mark & Show only enabled roles button & More fixes

* Change

* Fix missed PresetExplorer color
2026-03-09 13:27:20 +01:00

390 lines
11 KiB
C#

using EHR.Modules;
using EHR.Patches;
using System;
using System.Collections.Generic;
using UnityEngine;
namespace EHR;
public abstract class OptionItem
{
public const int NumPresets = 20;
public const int PresetId = 0;
public readonly List<OptionItem> Children;
private Dictionary<string, string> _replacementDictionary;
public OptionBehaviour OptionBehaviour;
protected OptionItem(int id, string name, int defaultValue, TabGroup tab, bool isSingleValue)
{
Id = id;
Name = name;
DefaultValue = defaultValue;
Tab = tab;
IsSingleValue = isSingleValue;
NameColor = Color.white;
ValueFormat = OptionFormat.None;
GameMode = CustomGameMode.All;
IsHeader = false;
IsHidden = false;
IsText = false;
Children = [];
if (Id == PresetId)
{
SingleValue = DefaultValue;
CurrentPreset = SingleValue;
}
else if (IsSingleValue)
SingleValue = DefaultValue;
else
{
for (var i = 0; i < NumPresets; i++)
AllValues[i] = DefaultValue;
}
if (FastOpts.TryAdd(id, this))
Options.Add(this);
else
Logger.Error($"Duplicate ID: {id} ({name})", "OptionItem");
}
public int Id { get; }
public string Name { get; }
public int DefaultValue { get; }
public TabGroup Tab { get; }
public bool IsSingleValue { get; }
public Color NameColor { get; set; }
private OptionFormat ValueFormat { get; set; }
public CustomGameMode GameMode { get; private set; }
public bool IsHeader { get; protected set; }
private bool IsHidden { get; set; }
public bool IsText { get; protected set; }
public TextOptionItem Header { get; set; } = null;
public Dictionary<string, string> ReplacementDictionary
{
get => _replacementDictionary;
set
{
if (value == null)
_replacementDictionary?.Clear();
else
_replacementDictionary = value;
}
}
public int[] AllValues { get; private set; } = new int[NumPresets];
public int CurrentValue
{
get => GetValue();
set => SetValue(value);
}
public int SingleValue { get; private set; }
public OptionItem Parent { get; private set; }
public List<Action<OptionItem, int, int>> UpdateValueEvent;
public bool UpdateValueEventRunsOnLoad { get; private set; }
// Setter
private OptionItem Do(Action<OptionItem> action)
{
action(this);
return this;
}
public OptionItem SetColor(Color value)
{
return Do(i => i.NameColor = value);
}
public OptionItem SetValueFormat(OptionFormat value)
{
return Do(i => i.ValueFormat = value);
}
public OptionItem SetGameMode(CustomGameMode value)
{
return Do(i => i.GameMode = value);
}
public OptionItem SetHeader(bool value)
{
return Do(i => i.IsHeader = value);
}
public OptionItem SetHidden(bool value)
{
return Do(i => i.IsHidden = value);
}
public OptionItem SetText(bool value)
{
return Do(i => i.IsText = value);
}
public OptionItem SetParent(OptionItem parent)
{
return Do(i =>
{
foreach (KeyValuePair<CustomRoles, StringOptionItem> role in EHR.Options.CustomRoleSpawnChances)
{
if (role.Value.Name == parent.Name)
{
string roleName = Translator.GetString(Enum.GetName(typeof(CustomRoles), role.Key));
ReplacementDictionary ??= [];
ReplacementDictionary.TryAdd(roleName, Utils.ColorString(Utils.GetRoleColor(role.Key), roleName));
break;
}
}
i.Parent = parent;
parent.SetChild(i);
});
}
private void SetChild(OptionItem child)
{
Do(i => i.Children.Add(child));
}
/// <summary>
/// Register an event that will be called when the value of this option is updated.
/// </summary>
/// <param name="handler">
/// The action that has three parameters:
/// the first argument is the OptionItem instance that was updated,
/// the second one is the value before the update,
/// the third one is the value after the update.
/// </param>
/// <returns></returns>
public OptionItem RegisterUpdateValueEvent(Action<OptionItem, int, int> handler)
{
UpdateValueEvent ??= [];
return Do(_ => UpdateValueEvent.Add(handler));
}
public OptionItem SetRunEventOnLoad(bool value)
{
return Do(_ => UpdateValueEventRunsOnLoad = value);
}
public OptionItem AddReplacement((string key, string value) kvp)
{
return Do(_ =>
{
ReplacementDictionary ??= [];
ReplacementDictionary.Add(kvp.key, kvp.value);
});
}
public OptionItem RemoveReplacement(string key)
{
return Do(_ => ReplacementDictionary?.Remove(key));
}
// Getter
public string GetName(bool disableColor = false, bool console = false)
{
if (Name.Contains("CTA.FLAG")) return Utils.ColorString(NameColor, Translator.GetString("CTA.TeamEnabled.Prefix") + Name[8..] + Translator.GetString("CTA.TeamEnabled.Suffix"));
return disableColor ? Translator.GetString(Name, ReplacementDictionary, console) : Utils.ColorString(NameColor, Translator.GetString(Name, ReplacementDictionary));
}
public bool GetBool()
{
return (Parent == null || Parent.GetBool()) && Name switch
{
"LoverDieConsequence" => GetValue() == 1,
"Bargainer.LensOfTruth.DurationSwitch" => GetValue() == 3,
"BlackHoleDespawnMode" => GetValue() == 1,
"CTF_TaggedPlayersGet" => GetValue() == 2,
"CTF_GameEndCriteria" => true,
_ => CurrentValue != 0
};
}
public virtual int GetInt()
{
return CurrentValue;
}
public virtual float GetFloat()
{
return CurrentValue;
}
public virtual string GetString()
{
return ApplyFormat(CurrentValue.ToString());
}
public virtual int GetValue()
{
return IsSingleValue ? SingleValue : AllValues[CurrentPreset];
}
public bool IsCurrentlyHidden(bool forLobbyView = false, bool checkCollapsedSection = true)
{
try
{
for (OptionItem current = this; current != null; current = current.Parent)
{
if (Hidden(current, forLobbyView, checkCollapsedSection))
return true;
}
}
catch (Exception e) { Utils.ThrowException(e); }
return false;
static bool Hidden(OptionItem oi, bool forLobbyView, bool checkCollapsedSection)
{
if (checkCollapsedSection && oi.Header is { CollapsesSection: true }) return true;
CustomGameMode mode = !forLobbyView ? EHR.Options.CurrentGameMode : LobbyViewSettingsPanePatch.LastGameModeSelected;
const CustomGameMode nd = CustomGameMode.NaturalDisasters;
return (oi.IsHidden || (oi.GameMode != CustomGameMode.All && oi.GameMode != mode) ||
(oi.Name == "IntegrateNaturalDisasters" && mode == nd)) &&
!(oi.GameMode == nd && EHR.Options.IntegrateNaturalDisasters.GetBool());
}
}
protected string ApplyFormat(string value)
{
if (ValueFormat == OptionFormat.None) return value;
return string.Format(Translator.GetString("Format." + ValueFormat), value);
}
private void Refresh()
{
if (OptionBehaviour is StringOption opt)
{
opt.TitleText.text = GetName();
opt.ValueText.text = GetString();
opt.oldValue = opt.Value = CurrentValue;
}
}
public void SetValue(int afterValue, bool doSave, bool doSync = true)
{
int beforeValue = CurrentValue;
if (IsSingleValue)
SingleValue = afterValue;
else
AllValues[CurrentPreset] = afterValue;
CallUpdateValueEvent(beforeValue, afterValue);
Refresh();
if (doSync) SyncAllOptions();
if (doSave) OptionSaver.Save();
}
public virtual void SetValue(int afterValue, bool doSync = true)
{
SetValue(afterValue, true, doSync);
}
public void SetAllValues(int[] values)
{
if (values.Length == AllValues.Length)
AllValues = values;
else
{
for (var i = 0; i < values.Length; i++)
AllValues[i] = values[i];
}
}
public static OptionItem operator ++(OptionItem item)
{
return item.Do(item => item.SetValue(item.CurrentValue + 1));
}
public static OptionItem operator --(OptionItem item)
{
return item.Do(item => item.SetValue(item.CurrentValue - 1));
}
protected static void SwitchPreset(int newPreset)
{
CurrentPreset = Math.Clamp(newPreset, 0, NumPresets - 1);
foreach (OptionItem op in AllOptions)
op.Refresh();
SyncAllOptions();
}
public static void SyncAllOptions(int targetId = -1)
{
if (
PlayerControl.AllPlayerControls.Count <= 1
|| !AmongUsClient.Instance.AmHost
|| PlayerControl.LocalPlayer == null
)
return;
RPC.SyncCustomSettingsRPC(targetId);
}
public void CallUpdateValueEvent(int beforeValue, int currentValue)
{
UpdateValueEvent?.ForEach(action =>
{
try { action(this, beforeValue, currentValue); }
catch (Exception ex)
{
Logger.Error($"[{Name}] - Exception occurred when calling UpdateValueEvent", "OptionItem.UpdateValueEvent");
Logger.Exception(ex, "OptionItem.UpdateValueEvent");
}
});
}
#region static
public static IReadOnlyList<OptionItem> AllOptions => Options;
private static readonly List<OptionItem> Options = new(1024);
public static IReadOnlyDictionary<int, OptionItem> FastOptions => FastOpts;
private static readonly Dictionary<int, OptionItem> FastOpts = new(1024);
public static int CurrentPreset { get; private set; }
#endregion
}
public enum TabGroup
{
SystemSettings,
GameSettings,
TaskSettings,
ImpostorRoles,
CrewmateRoles,
NeutralRoles,
CovenRoles,
Addons,
OtherRoles,
PresetExplorer
}
public enum OptionFormat
{
None,
Players,
Seconds,
Percent,
Times,
Multiplier,
Votes,
Pieces,
Health,
Level
}