mirror of
https://github.com/All-Of-Us-Mods/MiraAPI.git
synced 2026-10-19 15:51:48 +00:00
WIP code documentation and various fixes
This commit is contained in:
1 parent
a52f46972e
commit
409113e37a
26 files changed
+737
-467
No files matched your search
@@ -31,14 +31,13 @@ public class FreezeButton : CustomActionButton<PlayerControl>
|
||||
Target?.cosmetics.SetOutline(active, new Il2CppSystem.Nullable<Color>(Palette.Blue));
|
||||
}
|
||||
|
||||
public override bool IsTargetValid(PlayerControl target)
|
||||
public override bool IsTargetValid(PlayerControl? target)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Enabled(RoleBehaviour role)
|
||||
public override bool Enabled(RoleBehaviour? role)
|
||||
{
|
||||
return role is FreezerRole;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -4,31 +4,34 @@ using UnityEngine;
|
||||
|
||||
namespace MiraAPI.GameOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for option groups. An option group is a collection of options that are displayed together in the options menu.
|
||||
/// </summary>
|
||||
public abstract class AbstractOptionGroup
|
||||
{
|
||||
public List<IModdedOption> Options { get; } = [];
|
||||
internal List<IModdedOption> Options { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// The name of the group. Visible in options menu.
|
||||
/// Gets the name of the group. Visible in options menu.
|
||||
/// </summary>
|
||||
public abstract string GroupName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A function that returns whether the group should be visible or not.
|
||||
/// Gets the function that determines whether the group should be visible or not.
|
||||
/// </summary>
|
||||
public virtual Func<bool> GroupVisible => () => true;
|
||||
|
||||
/// <summary>
|
||||
/// The group color. This is used to color the group in the options menu.
|
||||
/// Gets the group color. This is used to color the group in the options menu.
|
||||
/// </summary>
|
||||
public virtual Color GroupColor => Color.clear;
|
||||
|
||||
/// <summary>
|
||||
/// The role the group is associated with. This is used for the advanced role options menu.
|
||||
/// Gets the role the group is associated with. This is used for the advanced role options menu.
|
||||
/// </summary>
|
||||
public virtual Type AdvancedRole => null;
|
||||
public virtual Type? AdvancedRole => null;
|
||||
|
||||
internal bool AllOptionsHidden { get; set; }
|
||||
|
||||
internal CategoryHeaderMasked Header { get; set; }
|
||||
}
|
||||
internal CategoryHeaderMasked? Header { get; set; }
|
||||
}
|
||||
@@ -11,10 +11,7 @@ namespace MiraAPI.GameOptions.Attributes;
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public abstract class ModdedOptionAttribute(string title, Type? roleType = null) : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the IModdedOption object that this attribute is attached to.
|
||||
/// </summary>
|
||||
public IModdedOption? HolderOption { get; internal set; }
|
||||
internal IModdedOption? HolderOption { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the title of the option.
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
using MiraAPI.PluginLoading;
|
||||
using System;
|
||||
using System;
|
||||
using BepInEx.Configuration;
|
||||
using MiraAPI.Networking;
|
||||
using MiraAPI.PluginLoading;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.GameOptions;
|
||||
|
||||
public interface IModdedOption
|
||||
internal interface IModdedOption
|
||||
{
|
||||
public uint Id { get; }
|
||||
public BaseGameSetting Data { get; }
|
||||
public IMiraPlugin? ParentMod { get; set; }
|
||||
public Type AdvancedRole { get; set; }
|
||||
public OptionBehaviour OptionBehaviour { get; }
|
||||
public string Title { get; }
|
||||
public StringNames StringName { get; }
|
||||
public Func<bool> Visible { get; set; }
|
||||
public ConfigDefinition? ConfigDefinition { get; set; }
|
||||
public void ValueChanged(OptionBehaviour optionBehaviour);
|
||||
public OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container);
|
||||
public abstract float GetFloatData();
|
||||
public NetData GetNetData();
|
||||
public void HandleNetData(byte[] data);
|
||||
}
|
||||
uint Id { get; }
|
||||
string Title { get; }
|
||||
StringNames StringName { get; }
|
||||
IMiraPlugin? ParentMod { get; set; }
|
||||
BaseGameSetting? Data { get; }
|
||||
Type? AdvancedRole { get; set; }
|
||||
OptionBehaviour? OptionBehaviour { get; }
|
||||
Func<bool> Visible { get; set; }
|
||||
ConfigDefinition? ConfigDefinition { get; set; }
|
||||
OptionBehaviour? CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container);
|
||||
float GetFloatData();
|
||||
NetData GetNetData();
|
||||
void HandleNetData(byte[] data);
|
||||
}
|
||||
@@ -1,18 +1,21 @@
|
||||
using HarmonyLib;
|
||||
using MiraAPI.GameOptions.Attributes;
|
||||
using Reactor.Utilities;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using BepInEx.Configuration;
|
||||
using HarmonyLib;
|
||||
using MiraAPI.GameOptions.Attributes;
|
||||
using MiraAPI.Networking;
|
||||
using MiraAPI.PluginLoading;
|
||||
using MiraAPI.Utilities;
|
||||
using Reactor.Networking.Rpc;
|
||||
using Reactor.Utilities;
|
||||
|
||||
namespace MiraAPI.GameOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Handles modded options.
|
||||
/// </summary>
|
||||
public static class ModdedOptionsManager
|
||||
{
|
||||
private static readonly Dictionary<PropertyInfo, ModdedOptionAttribute> OptionAttributes = new();
|
||||
@@ -20,10 +23,10 @@ public static class ModdedOptionsManager
|
||||
|
||||
internal static readonly Dictionary<uint, IModdedOption> ModdedOptions = new();
|
||||
internal static readonly List<AbstractOptionGroup> Groups = [];
|
||||
|
||||
|
||||
internal static uint NextId => _nextId++;
|
||||
private static uint _nextId = 1;
|
||||
|
||||
|
||||
internal static bool RegisterGroup(Type type, MiraPluginInfo pluginInfo)
|
||||
{
|
||||
if (Activator.CreateInstance(type) is not AbstractOptionGroup group)
|
||||
@@ -41,10 +44,10 @@ public static class ModdedOptionsManager
|
||||
Groups.Add(group);
|
||||
TypeToGroup.Add(type, group);
|
||||
pluginInfo.OptionGroups.Add(group);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
internal static void RegisterPropertyOption(Type type, PropertyInfo property, MiraPluginInfo pluginInfo)
|
||||
{
|
||||
if (!TypeToGroup.TryGetValue(type, out var group))
|
||||
@@ -62,7 +65,11 @@ public static class ModdedOptionsManager
|
||||
RegisterOption(option, group, property.Name, pluginInfo);
|
||||
}
|
||||
|
||||
internal static void RegisterAttributeOption(Type type, ModdedOptionAttribute attribute, PropertyInfo property, MiraPluginInfo pluginInfo)
|
||||
internal static void RegisterAttributeOption(
|
||||
Type type,
|
||||
ModdedOptionAttribute attribute,
|
||||
PropertyInfo property,
|
||||
MiraPluginInfo pluginInfo)
|
||||
{
|
||||
if (OptionAttributes.ContainsKey(property))
|
||||
{
|
||||
@@ -98,7 +105,11 @@ public static class ModdedOptionsManager
|
||||
RegisterOption(option, group, property.Name, pluginInfo);
|
||||
}
|
||||
|
||||
private static void RegisterOption(IModdedOption option, AbstractOptionGroup group, string propertyName, MiraPluginInfo pluginInfo)
|
||||
private static void RegisterOption(
|
||||
IModdedOption option,
|
||||
AbstractOptionGroup group,
|
||||
string propertyName,
|
||||
MiraPluginInfo pluginInfo)
|
||||
{
|
||||
var groupName = group.GetType().FullName;
|
||||
|
||||
@@ -128,51 +139,56 @@ public static class ModdedOptionsManager
|
||||
// necessary to disable then re-enable this setting
|
||||
// we dont know how other plugins handle their configs
|
||||
// this way, all the options are saved at once, instead of one by one
|
||||
foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins.Values)
|
||||
var oldConfigSetting = new Dictionary<MiraPluginInfo, bool>();
|
||||
foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins())
|
||||
{
|
||||
oldConfigSetting.Add(plugin, plugin.PluginConfig.SaveOnConfigSet);
|
||||
plugin.PluginConfig.SaveOnConfigSet = false;
|
||||
}
|
||||
|
||||
|
||||
foreach (var netData in data)
|
||||
{
|
||||
if (!ModdedOptions.TryGetValue(netData.Id, out var option))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
option.HandleNetData(netData.Data);
|
||||
}
|
||||
|
||||
foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins.Values)
|
||||
|
||||
foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins())
|
||||
{
|
||||
plugin.PluginConfig.Save();
|
||||
plugin.PluginConfig.SaveOnConfigSet = true;
|
||||
plugin.PluginConfig.SaveOnConfigSet = oldConfigSetting[plugin];
|
||||
}
|
||||
|
||||
|
||||
if (LobbyInfoPane.Instance)
|
||||
{
|
||||
LobbyInfoPane.Instance.RefreshPane();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Patches the setter of a property to update the value of the option.
|
||||
/// </summary>
|
||||
/// <param name="__originalMethod">The original setter method.</param>
|
||||
/// <param name="value">The new object value.</param>
|
||||
public static void PropertySetterPatch(MethodBase __originalMethod, object value)
|
||||
{
|
||||
var attribute = OptionAttributes.First(pair => pair.Key.GetSetMethod().Equals(__originalMethod)).Value;
|
||||
|
||||
attribute?.SetValue(value);
|
||||
var attribute = OptionAttributes.First(pair => pair.Key.GetSetMethod() == __originalMethod).Value;
|
||||
attribute.SetValue(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Patches the getter of a property to return the value of the option.
|
||||
/// </summary>
|
||||
/// <param name="__originalMethod">The original getter method.</param>
|
||||
/// <param name="__result">The result of the property getter.</param>
|
||||
/// <returns>False so the original getter gets skipped.</returns>
|
||||
public static bool PropertyGetterPatch(MethodBase __originalMethod, ref object __result)
|
||||
{
|
||||
var attribute = OptionAttributes.First(pair => pair.Key.GetGetMethod().Equals(__originalMethod)).Value;
|
||||
|
||||
if (attribute == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var attribute = OptionAttributes.First(pair => pair.Key.GetGetMethod() == __originalMethod).Value;
|
||||
__result = attribute.GetValue();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,17 +58,12 @@ public class ModdedEnumOption : ModdedOption<int>
|
||||
SetValue(BitConverter.ToInt32(data));
|
||||
}
|
||||
|
||||
public override string GetHudStringText()
|
||||
{
|
||||
return Title + ": " + Values[Value];
|
||||
}
|
||||
|
||||
public override int GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour)
|
||||
{
|
||||
return optionBehaviour.GetInt();
|
||||
}
|
||||
|
||||
public override void OnValueChanged(int newValue)
|
||||
protected override void OnValueChanged(int newValue)
|
||||
{
|
||||
DestroyableSingleton<HudManager>.Instance.Notifier.AddSettingsChangeMessage(StringName, Data.GetValueString(newValue), false);
|
||||
if (!OptionBehaviour)
|
||||
|
||||
@@ -80,17 +80,12 @@ public class ModdedNumberOption : ModdedOption<float>
|
||||
SetValue(BitConverter.ToSingle(data));
|
||||
}
|
||||
|
||||
public override string GetHudStringText()
|
||||
{
|
||||
return Title + ": " + Value + Helpers.GetSuffix(SuffixType);
|
||||
}
|
||||
|
||||
public override float GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour)
|
||||
{
|
||||
return Mathf.Clamp(optionBehaviour.GetFloat(), Min, Max);
|
||||
}
|
||||
|
||||
public override void OnValueChanged(float newValue)
|
||||
protected override void OnValueChanged(float newValue)
|
||||
{
|
||||
Value = Mathf.Clamp(newValue, Min, Max);
|
||||
DestroyableSingleton<HudManager>.Instance.Notifier.AddSettingsChangeMessage(StringName, Data.GetValueString(Value), false);
|
||||
|
||||
@@ -9,6 +9,10 @@ using UnityEngine;
|
||||
|
||||
namespace MiraAPI.GameOptions.OptionTypes;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a modded option.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The value type.</typeparam>
|
||||
public abstract class ModdedOption<T> : IModdedOption
|
||||
{
|
||||
private IMiraPlugin? _parentMod;
|
||||
@@ -18,10 +22,20 @@ public abstract class ModdedOption<T> : IModdedOption
|
||||
/// </summary>
|
||||
public uint Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the title of the option.
|
||||
/// </summary>
|
||||
public string Title { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the StringName object of the option.
|
||||
/// </summary>
|
||||
public StringNames StringName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the BaseGameSetting data of the option.
|
||||
/// </summary>
|
||||
public BaseGameSetting Data { get; protected init; }
|
||||
public BaseGameSetting? Data { get; protected init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the parent mod of the option.
|
||||
@@ -51,26 +65,22 @@ public abstract class ModdedOption<T> : IModdedOption
|
||||
/// <summary>
|
||||
/// Gets or sets the event that is invoked when the value of the option changes.
|
||||
/// </summary>
|
||||
public Action<T> ChangedEvent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the title of the option.
|
||||
/// </summary>
|
||||
public string Title { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the StringName object of the option.
|
||||
/// </summary>
|
||||
public StringNames StringName { get; }
|
||||
public Action<T>? ChangedEvent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the visibility of the option.
|
||||
/// </summary>
|
||||
public Func<bool> Visible { get; set; }
|
||||
|
||||
public Type AdvancedRole { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the advanced role of the option.
|
||||
/// </summary>
|
||||
public Type? AdvancedRole { get; set; }
|
||||
|
||||
public OptionBehaviour OptionBehaviour { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the option behaviour of the option.
|
||||
/// </summary>
|
||||
public OptionBehaviour? OptionBehaviour { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the config definition of the option.
|
||||
@@ -87,6 +97,12 @@ public abstract class ModdedOption<T> : IModdedOption
|
||||
|
||||
private ConfigDefinition? _configDefinition;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ModdedOption{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="title">The option title.</param>
|
||||
/// <param name="defaultValue">The default value.</param>
|
||||
/// <param name="roleType">The Role Type or null if it doesn't belong to a role.</param>
|
||||
protected ModdedOption(string title, T defaultValue, Type? roleType)
|
||||
{
|
||||
Id = ModdedOptionsManager.NextId;
|
||||
@@ -102,18 +118,23 @@ public abstract class ModdedOption<T> : IModdedOption
|
||||
}
|
||||
}
|
||||
|
||||
public void ValueChanged(OptionBehaviour optionBehaviour)
|
||||
internal void ValueChanged(OptionBehaviour optionBehaviour)
|
||||
{
|
||||
SetValue(GetValueFromOptionBehaviour(optionBehaviour));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value of the option.
|
||||
/// </summary>
|
||||
/// <param name="newValue">The new value.</param>
|
||||
public void SetValue(T newValue)
|
||||
{
|
||||
var oldVal = Value;
|
||||
Value = newValue;
|
||||
if (!Value.Equals(oldVal) && ChangedEvent != null)
|
||||
|
||||
if (Value?.Equals(oldVal) == false)
|
||||
{
|
||||
ChangedEvent.Invoke(Value);
|
||||
ChangedEvent?.Invoke(Value);
|
||||
}
|
||||
|
||||
if (AmongUsClient.Instance.AmHost)
|
||||
@@ -122,24 +143,55 @@ public abstract class ModdedOption<T> : IModdedOption
|
||||
{
|
||||
entry.Value = Value;
|
||||
}
|
||||
|
||||
|
||||
Rpc<SyncOptionsRpc>.Instance.Send(PlayerControl.LocalPlayer, [GetNetData()], true);
|
||||
}
|
||||
|
||||
OnValueChanged(newValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the float data of the option.
|
||||
/// </summary>
|
||||
/// <returns>A float object representing the option's value.</returns>
|
||||
public abstract float GetFloatData();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the net data of the option.
|
||||
/// </summary>
|
||||
/// <returns>A NetData object representing this option's data.</returns>
|
||||
public abstract NetData GetNetData();
|
||||
|
||||
/// <summary>
|
||||
/// Handles incoming net data.
|
||||
/// </summary>
|
||||
/// <param name="data">The NetData's byte array.</param>
|
||||
public abstract void HandleNetData(byte[] data);
|
||||
|
||||
public abstract void OnValueChanged(T newValue);
|
||||
/// <summary>
|
||||
/// Handles the value changed event.
|
||||
/// </summary>
|
||||
/// <param name="newValue">The new value.</param>
|
||||
protected abstract void OnValueChanged(T newValue);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value from the option behaviour.
|
||||
/// </summary>
|
||||
/// <param name="optionBehaviour">The OptionBehaviour.</param>
|
||||
/// <returns>The value.</returns>
|
||||
public abstract T GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour);
|
||||
|
||||
public abstract OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container);
|
||||
|
||||
public abstract string GetHudStringText();
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates the option behaviour.
|
||||
/// </summary>
|
||||
/// <param name="toggleOpt">The ToggleOption prefab.</param>
|
||||
/// <param name="numberOpt">The NumberOption prefab.</param>
|
||||
/// <param name="stringOpt">The StringOption prefab.</param>
|
||||
/// <param name="container">The options container.</param>
|
||||
/// <returns>A new OptionBehaviour for this modded option.</returns>
|
||||
public abstract OptionBehaviour CreateOption(
|
||||
ToggleOption toggleOpt,
|
||||
NumberOption numberOpt,
|
||||
StringOption stringOpt,
|
||||
Transform container);
|
||||
}
|
||||
@@ -55,17 +55,12 @@ public class ModdedStringOption : ModdedOption<int>
|
||||
SetValue(BitConverter.ToInt32(data));
|
||||
}
|
||||
|
||||
public override string GetHudStringText()
|
||||
{
|
||||
return Title + ": " + Values[Value];
|
||||
}
|
||||
|
||||
public override int GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour)
|
||||
{
|
||||
return optionBehaviour.GetInt();
|
||||
}
|
||||
|
||||
public override void OnValueChanged(int newValue)
|
||||
protected override void OnValueChanged(int newValue)
|
||||
{
|
||||
DestroyableSingleton<HudManager>.Instance.Notifier.AddSettingsChangeMessage(StringName, Data.GetValueString(newValue), false);
|
||||
if (!OptionBehaviour)
|
||||
|
||||
@@ -47,17 +47,12 @@ public class ModdedToggleOption : ModdedOption<bool>
|
||||
SetValue(BitConverter.ToBoolean(data));
|
||||
}
|
||||
|
||||
public override string GetHudStringText()
|
||||
{
|
||||
return Title + ": " + (Value ? "On" : "Off");
|
||||
}
|
||||
|
||||
public override bool GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour)
|
||||
{
|
||||
return optionBehaviour.GetBool();
|
||||
}
|
||||
|
||||
public override void OnValueChanged(bool newValue)
|
||||
protected override void OnValueChanged(bool newValue)
|
||||
{
|
||||
DestroyableSingleton<HudManager>.Instance.Notifier.AddSettingsChangeMessage(StringName, newValue ? "On" : "Off", false);
|
||||
if (!OptionBehaviour)
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
namespace MiraAPI.Hud;
|
||||
|
||||
/// <summary>
|
||||
/// The location of the custom button on the screen.
|
||||
/// </summary>
|
||||
public enum ButtonLocation
|
||||
{
|
||||
/// <summary>
|
||||
/// Placed in the bottom left.
|
||||
/// </summary>
|
||||
BottomLeft,
|
||||
BottomRight
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Placed in the bottom right.
|
||||
/// </summary>
|
||||
BottomRight,
|
||||
}
|
||||
@@ -22,10 +22,10 @@ internal static class CustomButtonManager
|
||||
Logger<MiraApiPlugin>.Error($"Failed to create button from {buttonType.Name}");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
CustomButtons.Add(button);
|
||||
typeof(CustomButtonSingleton<>).MakeGenericType(buttonType)
|
||||
.GetField("_instance", BindingFlags.Static | BindingFlags.NonPublic)!
|
||||
.SetValue(null, button);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,13 @@ namespace MiraAPI.Hud;
|
||||
/// A utility class to get the instance of a custom action button.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the button you are trying to access.</typeparam>
|
||||
public class CustomButtonSingleton<T> where T : CustomActionButton
|
||||
public static class CustomButtonSingleton<T> where T : CustomActionButton
|
||||
{
|
||||
private static T? _instance;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets instance of the button.
|
||||
/// </summary>
|
||||
public static T Instance
|
||||
{
|
||||
get => _instance ??= CustomButtonManager.CustomButtons.OfType<T>().Single();
|
||||
|
||||
@@ -1,31 +1,51 @@
|
||||
using MiraAPI.Modifiers.Types;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using MiraAPI.Modifiers.Types;
|
||||
using MiraAPI.Utilities;
|
||||
using Reactor.Utilities;
|
||||
using Reactor.Utilities.Attributes;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Modifiers;
|
||||
|
||||
/// <summary>
|
||||
/// The component for handling modifiers.
|
||||
/// </summary>
|
||||
[RegisterInIl2Cpp]
|
||||
public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr)
|
||||
{
|
||||
public List<BaseModifier> ActiveModifiers { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets the active modifiers on the player.
|
||||
/// </summary>
|
||||
public ImmutableList<BaseModifier> ActiveModifiers => Modifiers.ToImmutableList();
|
||||
|
||||
public PlayerControl player;
|
||||
private List<BaseModifier> Modifiers { get; set; } = [];
|
||||
|
||||
private TextMeshPro _modifierText;
|
||||
private PlayerControl _player = null!;
|
||||
|
||||
public void Start()
|
||||
private TextMeshPro _modifierText = null!;
|
||||
|
||||
internal void ClearModifiers()
|
||||
{
|
||||
player = GetComponent<PlayerControl>();
|
||||
ActiveModifiers = [];
|
||||
foreach (var modifier in Modifiers)
|
||||
{
|
||||
modifier.OnDeactivate();
|
||||
}
|
||||
|
||||
if (!player.AmOwner)
|
||||
Modifiers.Clear();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_player = GetComponent<PlayerControl>();
|
||||
Modifiers = [];
|
||||
|
||||
if (!_player.AmOwner)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -34,55 +54,56 @@ public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr)
|
||||
_modifierText.verticalAlignment = VerticalAlignmentOptions.Top;
|
||||
}
|
||||
|
||||
public void FixedUpdate()
|
||||
private void FixedUpdate()
|
||||
{
|
||||
if (ActiveModifiers is null) return;
|
||||
|
||||
foreach (var modifier in ActiveModifiers)
|
||||
foreach (var modifier in Modifiers)
|
||||
{
|
||||
if (modifier is null) continue;
|
||||
|
||||
modifier.FixedUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
private void Update()
|
||||
{
|
||||
if (ActiveModifiers is null) return;
|
||||
|
||||
foreach (var modifier in ActiveModifiers)
|
||||
foreach (var modifier in Modifiers)
|
||||
{
|
||||
if (modifier is null) continue;
|
||||
|
||||
modifier.Update();
|
||||
}
|
||||
|
||||
var filteredModifiers = ActiveModifiers.Where(mod => !mod.HideOnUi);
|
||||
if (!_modifierText || !_player.AmOwner)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var filteredModifiers = Modifiers.Where(mod => !mod.HideOnUi);
|
||||
|
||||
var baseModifiers = filteredModifiers as BaseModifier[] ?? filteredModifiers.ToArray();
|
||||
|
||||
if (player.AmOwner && baseModifiers.Any())
|
||||
if (baseModifiers.Length != 0)
|
||||
{
|
||||
var stringBuild = new StringBuilder();
|
||||
foreach (var mod in baseModifiers)
|
||||
{
|
||||
stringBuild.Append($"\n{mod.ModifierName}");
|
||||
stringBuild.Append(CultureInfo.InvariantCulture, $"\n{mod.ModifierName}");
|
||||
if (mod is TimedModifier timer)
|
||||
{
|
||||
stringBuild.Append($" <size=70%>({Math.Round(timer.Duration - timer.TimeRemaining, 0)}s/{timer.Duration}s)</size>");
|
||||
stringBuild.Append(CultureInfo.InvariantCulture, $" <size=70%>({Math.Round(timer.Duration - timer.TimeRemaining, 0)}s/{timer.Duration}s)</size>");
|
||||
}
|
||||
}
|
||||
_modifierText.text = $"<b><size=130%>Modifiers:</b></size>{stringBuild}";
|
||||
}
|
||||
else if (player.AmOwner && !baseModifiers.Any() && _modifierText.text != string.Empty)
|
||||
else if (_modifierText.text != string.Empty)
|
||||
{
|
||||
_modifierText.text = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a modifier from the player.
|
||||
/// </summary>
|
||||
/// <param name="type">The modifier type.</param>
|
||||
public void RemoveModifier(Type type)
|
||||
{
|
||||
var modifier = ActiveModifiers.Find(x => x.GetType() == type);
|
||||
var modifier = Modifiers.Find(x => x.GetType() == type);
|
||||
|
||||
if (modifier is null)
|
||||
{
|
||||
@@ -93,14 +114,22 @@ public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr)
|
||||
RemoveModifier(modifier);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a modifier from the player.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The modifier type.</typeparam>
|
||||
public void RemoveModifier<T>() where T : BaseModifier
|
||||
{
|
||||
RemoveModifier(typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a modifier from the player.
|
||||
/// </summary>
|
||||
/// <param name="modifierId">The modifier ID.</param>
|
||||
public void RemoveModifier(uint modifierId)
|
||||
{
|
||||
var modifier = ActiveModifiers.Find(x => x.ModifierId == modifierId);
|
||||
var modifier = Modifiers.Find(x => x.ModifierId == modifierId);
|
||||
|
||||
if (modifier is null)
|
||||
{
|
||||
@@ -111,37 +140,54 @@ public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr)
|
||||
RemoveModifier(modifier);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a modifier from the player.
|
||||
/// </summary>
|
||||
/// <param name="modifier">The modifier object.</param>
|
||||
public void RemoveModifier(BaseModifier modifier)
|
||||
{
|
||||
if (!ActiveModifiers.Contains(modifier))
|
||||
if (!Modifiers.Contains(modifier))
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Cannot remove modifier {modifier.ModifierName} because it is not active on this player.");
|
||||
return;
|
||||
}
|
||||
|
||||
modifier.OnDeactivate();
|
||||
ActiveModifiers.Remove(modifier);
|
||||
Modifiers.Remove(modifier);
|
||||
|
||||
if (player.AmOwner)
|
||||
if (_player.AmOwner)
|
||||
{
|
||||
HudManager.Instance.SetHudActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
public BaseModifier AddModifier(BaseModifier modifier)
|
||||
/// <summary>
|
||||
/// Adds a modifier to the player.
|
||||
/// </summary>
|
||||
/// <param name="modifier">The modifier to add.</param>
|
||||
/// <returns>The modifier that was added.</returns>
|
||||
public BaseModifier? AddModifier(BaseModifier modifier)
|
||||
{
|
||||
if (ActiveModifiers.Contains(modifier))
|
||||
if (Modifiers.Contains(modifier))
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Player already has modifier with id {modifier.ModifierId}!");
|
||||
return null;
|
||||
}
|
||||
|
||||
ActiveModifiers.Add(modifier);
|
||||
modifier.Player = player;
|
||||
modifier.ModifierId = ModifierManager.TypeToIdModifiers[modifier.GetType()];
|
||||
var modifierId = ModifierManager.GetModifierId(modifier.GetType());
|
||||
|
||||
if (modifierId == null)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Cannot add modifier {modifier.GetType().Name} because it has no ID!");
|
||||
return null;
|
||||
}
|
||||
|
||||
Modifiers.Add(modifier);
|
||||
modifier.Player = _player;
|
||||
modifier.ModifierId = modifierId.Value;
|
||||
modifier.OnActivate();
|
||||
|
||||
if (!player.AmOwner)
|
||||
if (!_player.AmOwner)
|
||||
{
|
||||
return modifier;
|
||||
}
|
||||
@@ -156,23 +202,27 @@ public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr)
|
||||
return modifier;
|
||||
}
|
||||
|
||||
public BaseModifier AddModifier(Type type)
|
||||
/// <summary>
|
||||
/// Adds a modifier to the player.
|
||||
/// </summary>
|
||||
/// <param name="type">The modifier type.</param>
|
||||
/// <returns>The modifier that was added.</returns>
|
||||
public BaseModifier? AddModifier(Type type)
|
||||
{
|
||||
if (!ModifierManager.TypeToIdModifiers.TryGetValue(type, out var modifierId))
|
||||
var modifierId = ModifierManager.GetModifierId(type);
|
||||
if (modifierId == null)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Cannot add modifier {type.Name} because it is not registered.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (ActiveModifiers.Find(x => x.ModifierId == modifierId) != null)
|
||||
if (Modifiers.Find(x => x.ModifierId == modifierId) != null)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Player already has modifier with id {modifierId}!");
|
||||
return null;
|
||||
}
|
||||
|
||||
var modifier = (BaseModifier)Activator.CreateInstance(type);
|
||||
|
||||
if (modifier is null)
|
||||
if (Activator.CreateInstance(type) is not BaseModifier modifier)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Cannot add modifier {type.Name} because it is null.");
|
||||
return null;
|
||||
@@ -183,8 +233,13 @@ public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr)
|
||||
return modifier;
|
||||
}
|
||||
|
||||
public T AddModifier<T>() where T : BaseModifier
|
||||
/// <summary>
|
||||
/// Adds a modifier to the player.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The Type of the modifier.</typeparam>
|
||||
/// <returns>The new modifier.</returns>
|
||||
public T? AddModifier<T>() where T : BaseModifier
|
||||
{
|
||||
return AddModifier(typeof(T)) as T;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,24 @@
|
||||
using MiraAPI.Modifiers.Types;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MiraAPI.Modifiers.Types;
|
||||
using MiraAPI.Networking;
|
||||
using MiraAPI.Roles;
|
||||
using MiraAPI.Utilities;
|
||||
using Reactor.Networking.Rpc;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Reactor.Utilities;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using Random = System.Random;
|
||||
|
||||
namespace MiraAPI.Modifiers;
|
||||
|
||||
/// <summary>
|
||||
/// The manager for handling modifiers.
|
||||
/// </summary>
|
||||
public static class ModifierManager
|
||||
{
|
||||
public static readonly Dictionary<uint, Type> IdToTypeModifiers = [];
|
||||
public static readonly Dictionary<Type, uint> TypeToIdModifiers = [];
|
||||
private static readonly Dictionary<uint, Type> IdToTypeModifierMap = [];
|
||||
private static readonly Dictionary<Type, uint> TypeToIdModifierMap = [];
|
||||
|
||||
private static uint _nextId;
|
||||
|
||||
@@ -25,6 +28,26 @@ public static class ModifierManager
|
||||
return _nextId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the modifier type from the id.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID.</param>
|
||||
/// <returns>The Type of the modifier.</returns>
|
||||
public static Type? GetModifierType(uint id)
|
||||
{
|
||||
return IdToTypeModifierMap.GetValueOrDefault(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the modifier id from the type.
|
||||
/// </summary>
|
||||
/// <param name="type">The Type.</param>
|
||||
/// <returns>The ID of the modifier.</returns>
|
||||
public static uint? GetModifierId(Type type)
|
||||
{
|
||||
return TypeToIdModifierMap.GetValueOrDefault(type);
|
||||
}
|
||||
|
||||
internal static void RegisterModifier(Type modifierType)
|
||||
{
|
||||
if (!typeof(BaseModifier).IsAssignableFrom(modifierType))
|
||||
@@ -32,8 +55,8 @@ public static class ModifierManager
|
||||
return;
|
||||
}
|
||||
|
||||
IdToTypeModifiers.Add(GetNextId(), modifierType);
|
||||
TypeToIdModifiers.Add(modifierType, _nextId);
|
||||
IdToTypeModifierMap.Add(GetNextId(), modifierType);
|
||||
TypeToIdModifierMap.Add(modifierType, _nextId);
|
||||
}
|
||||
|
||||
internal static void AssignModifiers(List<PlayerControl> plrs)
|
||||
@@ -42,34 +65,32 @@ public static class ModifierManager
|
||||
|
||||
List<uint> filteredModifiers = [];
|
||||
|
||||
foreach (var modifier in IdToTypeModifiers.Where(pair => pair.Value.IsAssignableTo(typeof(GameModifier))))
|
||||
foreach (var modifier in IdToTypeModifierMap.Where(pair => pair.Value.IsAssignableTo(typeof(GameModifier))))
|
||||
{
|
||||
var mod = (GameModifier)Activator.CreateInstance(modifier.Value);
|
||||
|
||||
if (mod is null)
|
||||
if (Activator.CreateInstance(modifier.Value) is not GameModifier mod)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Failed to create instance of {modifier.Value.Name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!plrs.Exists(x=>IsGameModifierValid(x, mod, modifier.Key)))
|
||||
|
||||
if (!plrs.Exists(x => IsGameModifierValid(x, mod, modifier.Key)))
|
||||
{
|
||||
Logger<MiraApiPlugin>.Warning("No players are valid for modifier: " + mod.ModifierName);
|
||||
continue;
|
||||
}
|
||||
|
||||
var maxCount = plrs.Count(x=>IsGameModifierValid(x, mod, modifier.Key));
|
||||
|
||||
var maxCount = plrs.Count(x => IsGameModifierValid(x, mod, modifier.Key));
|
||||
|
||||
var num = Math.Clamp(mod.GetAmountPerGame(), 0, maxCount);
|
||||
var chance = mod.GetAssignmentChance();
|
||||
|
||||
|
||||
for (var i = 0; i < num; i++)
|
||||
{
|
||||
var randomNum = rand.Next(100);
|
||||
|
||||
if (randomNum < Math.Clamp(chance, 0, 100))
|
||||
{
|
||||
filteredModifiers.Add(TypeToIdModifiers[modifier.Value]);
|
||||
filteredModifiers.Add(TypeToIdModifierMap[modifier.Value]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -84,15 +105,13 @@ public static class ModifierManager
|
||||
{
|
||||
var id = shuffledModifiers[0];
|
||||
|
||||
var mod = (GameModifier)Activator.CreateInstance(IdToTypeModifiers[id]);
|
||||
|
||||
if (mod is null)
|
||||
if (Activator.CreateInstance(IdToTypeModifierMap[id]) is not GameModifier mod)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Failed to create instance of {IdToTypeModifiers[id].Name}");
|
||||
Logger<MiraApiPlugin>.Error($"Failed to create instance of {IdToTypeModifierMap[id].Name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!plrs.Exists(x=>IsGameModifierValid(x, mod, id)))
|
||||
if (!plrs.Exists(x => IsGameModifierValid(x, mod, id)))
|
||||
{
|
||||
shuffledModifiers.RemoveAt(0);
|
||||
continue;
|
||||
@@ -100,7 +119,7 @@ public static class ModifierManager
|
||||
|
||||
var plr = plrs.Random();
|
||||
|
||||
if (!IsGameModifierValid(plr, mod, id))
|
||||
if (plr == null || !IsGameModifierValid(plr, mod, id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -116,7 +135,7 @@ public static class ModifierManager
|
||||
modifier.IsModifierValidOn(player.Data.Role) &&
|
||||
!player.HasModifier(modifierId);
|
||||
}
|
||||
|
||||
|
||||
internal static void SyncAllModifiers(int targetId = -1)
|
||||
{
|
||||
var data = new List<NetData>();
|
||||
@@ -126,7 +145,7 @@ public static class ModifierManager
|
||||
data.Add(GetPlayerModifiers(player.Object));
|
||||
}
|
||||
|
||||
Rpc<SyncModifiersRpc>.Instance.SendTo(PlayerControl.LocalPlayer, targetId, data.ToArray());
|
||||
Rpc<SyncModifiersRpc>.Instance.SendTo(PlayerControl.LocalPlayer, targetId, [.. data]);
|
||||
}
|
||||
|
||||
internal static void HandleSyncModifiers(NetData[] data)
|
||||
@@ -144,20 +163,16 @@ public static class ModifierManager
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var modifier in modifierComponent.ActiveModifiers)
|
||||
{
|
||||
modifier.OnDeactivate();
|
||||
}
|
||||
modifierComponent.ActiveModifiers.Clear();
|
||||
modifierComponent.ClearModifiers();
|
||||
|
||||
foreach (var id in ids)
|
||||
{
|
||||
if (!IdToTypeModifiers.TryGetValue(id, out var type))
|
||||
if (!IdToTypeModifierMap.TryGetValue(id, out var type))
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Cannot add modifier with id {id} because it is not registered.");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
modifierComponent.AddModifier(type);
|
||||
}
|
||||
}
|
||||
@@ -167,16 +182,16 @@ public static class ModifierManager
|
||||
{
|
||||
var bytes = new List<byte>();
|
||||
var modifierComponent = player.GetComponent<ModifierComponent>();
|
||||
if (!modifierComponent)
|
||||
if (modifierComponent == null)
|
||||
{
|
||||
return new NetData(player.PlayerId, []);
|
||||
}
|
||||
|
||||
foreach (var modifier in modifierComponent.ActiveModifiers)
|
||||
{
|
||||
bytes.AddRange(BitConverter.GetBytes(TypeToIdModifiers[modifier.GetType()]));
|
||||
bytes.AddRange(BitConverter.GetBytes(TypeToIdModifierMap[modifier.GetType()]));
|
||||
}
|
||||
|
||||
return new NetData(player.PlayerId, bytes.ToArray());
|
||||
return new NetData(player.PlayerId, [.. bytes]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,15 +10,21 @@ using UnityEngine.Events;
|
||||
|
||||
namespace MiraAPI.Patches.Options;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Patches the GameOptionsMenu to add custom options.
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(GameOptionsMenu))]
|
||||
public static class GameOptionsMenuPatch
|
||||
{
|
||||
/// <summary>
|
||||
/// Update patch for the GameOptionsMenu.
|
||||
/// </summary>
|
||||
/// <param name="__instance">The GameOptionsMenu instance.</param>
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(GameOptionsMenu.Update))]
|
||||
public static void UpdatePatch(GameOptionsMenu __instance)
|
||||
{
|
||||
if (GameSettingMenuPatches.CurrentSelectedMod == 0)
|
||||
if (GameSettingMenuPatches.SelectedModIdx == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -71,7 +77,7 @@ public static class GameOptionsMenuPatch
|
||||
[HarmonyPatch(nameof(GameOptionsMenu.CreateSettings))]
|
||||
public static bool SettingsPatch(GameOptionsMenu __instance)
|
||||
{
|
||||
if (GameSettingMenuPatches.CurrentSelectedMod == 0)
|
||||
if (GameSettingMenuPatches.SelectedModIdx == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -12,17 +12,30 @@ using Object = UnityEngine.Object;
|
||||
|
||||
namespace MiraAPI.Patches.Options;
|
||||
|
||||
/// <summary>
|
||||
/// Patches for the <see cref="GameSettingMenu"/> to add support for custom options.
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(GameSettingMenu))]
|
||||
public static class GameSettingMenuPatches
|
||||
{
|
||||
public static int CurrentSelectedMod { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets the currently selected mod index.
|
||||
/// </summary>
|
||||
public static int SelectedModIdx { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the currently selected mod.
|
||||
/// </summary>
|
||||
public static MiraPluginInfo? SelectedMod { get; private set; }
|
||||
|
||||
private static TextMeshPro? _text;
|
||||
|
||||
private static Vector3 _roleBtnOgPos;
|
||||
|
||||
/// <summary>
|
||||
/// Prefix for the <see cref="GameSettingMenu.Start"/> method. Sets up the custom options.
|
||||
/// </summary>
|
||||
/// <param name="__instance">The GameSettingMenu instance.</param>
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GameSettingMenu.Start))]
|
||||
public static void StartPrefix(GameSettingMenu __instance)
|
||||
@@ -56,11 +69,12 @@ public static class GameSettingMenuPatches
|
||||
passiveButton.OnClick = new ButtonClickedEvent();
|
||||
passiveButton.OnClick.AddListener((UnityAction)(() =>
|
||||
{
|
||||
if (CurrentSelectedMod != MiraPluginManager.Instance.RegisteredPlugins.Count)
|
||||
SelectedModIdx += 1;
|
||||
if (SelectedModIdx > MiraPluginManager.Instance.RegisteredPlugins().Length)
|
||||
{
|
||||
CurrentSelectedMod += 1;
|
||||
UpdateText(__instance, __instance.GameSettingsTab, __instance.RoleSettingsTab);
|
||||
SelectedModIdx = 0;
|
||||
}
|
||||
UpdateText(__instance, __instance.GameSettingsTab, __instance.RoleSettingsTab);
|
||||
}));
|
||||
|
||||
var backButton = Object.Instantiate(nextButton, __instance.BackButton.transform.parent).gameObject;
|
||||
@@ -70,17 +84,18 @@ public static class GameSettingMenuPatches
|
||||
backButton.transform.FindChild("Active").gameObject.GetComponent<SpriteRenderer>().flipX = backButton.transform.FindChild("Inactive").gameObject.GetComponent<SpriteRenderer>().flipX = true;
|
||||
backButton.gameObject.GetComponent<PassiveButton>().OnClick.AddListener((UnityAction)(() =>
|
||||
{
|
||||
if (CurrentSelectedMod != 0)
|
||||
SelectedModIdx -= 1;
|
||||
if (SelectedModIdx < 0)
|
||||
{
|
||||
CurrentSelectedMod -= 1;
|
||||
UpdateText(__instance, __instance.GameSettingsTab, __instance.RoleSettingsTab);
|
||||
SelectedModIdx = MiraPluginManager.Instance.RegisteredPlugins().Length;
|
||||
}
|
||||
UpdateText(__instance, __instance.GameSettingsTab, __instance.RoleSettingsTab);
|
||||
}));
|
||||
}
|
||||
|
||||
private static void UpdateText(GameSettingMenu menu, GameOptionsMenu settings, RolesSettingsMenu roles)
|
||||
{
|
||||
if (_text is not null && CurrentSelectedMod == 0)
|
||||
if (_text is not null && SelectedModIdx == 0)
|
||||
{
|
||||
_text.text = "Default";
|
||||
_text.fontSizeMax = 3.2f;
|
||||
@@ -88,22 +103,17 @@ public static class GameSettingMenuPatches
|
||||
else if (_text is not null)
|
||||
{
|
||||
_text.fontSizeMax = 2.3f;
|
||||
SelectedMod = MiraPluginManager.Instance.RegisteredPlugins.ElementAt(CurrentSelectedMod - 1).Value;
|
||||
if (SelectedMod == null)
|
||||
{
|
||||
CurrentSelectedMod = 0;
|
||||
UpdateText(menu, settings, roles);
|
||||
}
|
||||
SelectedMod = MiraPluginManager.Instance.RegisteredPlugins()[SelectedModIdx - 1];
|
||||
|
||||
var name = SelectedMod?.MiraPlugin.OptionsTitleText;
|
||||
_text.text = name?[..Math.Min(name.Length, 25)];
|
||||
var name = SelectedMod.MiraPlugin.OptionsTitleText;
|
||||
_text.text = name[..Math.Min(name.Length, 25)];
|
||||
}
|
||||
|
||||
menu.RoleSettingsButton.transform.localPosition = _roleBtnOgPos;
|
||||
menu.GameSettingsButton.gameObject.SetActive(true);
|
||||
menu.RoleSettingsButton.gameObject.SetActive(true);
|
||||
|
||||
if (CurrentSelectedMod != 0)
|
||||
if (SelectedModIdx != 0)
|
||||
{
|
||||
if (SelectedMod?.Options.Where(x=>x.AdvancedRole==null).ToList().Count == 0)
|
||||
{
|
||||
@@ -121,7 +131,7 @@ public static class GameSettingMenuPatches
|
||||
}
|
||||
}
|
||||
|
||||
if (roles?.roleChances != null && SelectedMod != null && SelectedMod.CustomRoles.Count != 0)
|
||||
if (roles.roleChances != null && SelectedMod != null && SelectedMod.CustomRoles.Count != 0)
|
||||
{
|
||||
if (roles.advancedSettingChildren is not null)
|
||||
{
|
||||
|
||||
@@ -16,10 +16,9 @@ namespace MiraAPI.Patches.Options;
|
||||
[HarmonyPatch(typeof(LobbyViewSettingsPane))]
|
||||
public static class LobbyViewPanePatches
|
||||
{
|
||||
|
||||
public static int CurrentSelectedMod { get; private set; }
|
||||
|
||||
public static MiraPluginInfo? SelectedMod => CurrentSelectedMod == 0 ? null : MiraPluginManager.Instance.RegisteredPlugins.ElementAt(CurrentSelectedMod-1).Value;
|
||||
public static MiraPluginInfo? SelectedMod => CurrentSelectedMod == 0 ? null : MiraPluginManager.Instance.RegisteredPlugins()[CurrentSelectedMod-1];
|
||||
|
||||
|
||||
[HarmonyPostfix]
|
||||
@@ -48,7 +47,7 @@ public static class LobbyViewPanePatches
|
||||
passiveButton.OnClick.AddListener((UnityAction)(() =>
|
||||
{
|
||||
CurrentSelectedMod += 1;
|
||||
if (CurrentSelectedMod > MiraPluginManager.Instance.RegisteredPlugins.Count)
|
||||
if (CurrentSelectedMod > MiraPluginManager.Instance.RegisteredPlugins().Length)
|
||||
{
|
||||
CurrentSelectedMod = 0;
|
||||
}
|
||||
@@ -71,7 +70,7 @@ public static class LobbyViewPanePatches
|
||||
CurrentSelectedMod -= 1;
|
||||
if (CurrentSelectedMod < 0)
|
||||
{
|
||||
CurrentSelectedMod = MiraPluginManager.Instance.RegisteredPlugins.Count;
|
||||
CurrentSelectedMod = MiraPluginManager.Instance.RegisteredPlugins().Length;
|
||||
}
|
||||
__instance.RefreshTab();
|
||||
__instance.scrollBar.ScrollToTop();
|
||||
@@ -185,146 +184,145 @@ public static class LobbyViewPanePatches
|
||||
|
||||
private static void DrawRolesTab(LobbyViewSettingsPane instance)
|
||||
{
|
||||
if (SelectedMod == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (SelectedMod == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var num = 0.95f;
|
||||
var num2 = -6.53f;
|
||||
var categoryHeaderMasked =
|
||||
Object.Instantiate(instance.categoryHeaderOrigin, instance.settingsContainer, true);
|
||||
categoryHeaderMasked.SetHeader(StringNames.RoleQuotaLabel, 61);
|
||||
categoryHeaderMasked.transform.localScale = Vector3.one;
|
||||
categoryHeaderMasked.transform.localPosition = new Vector3(-9.77f, 1.26f, -2f);
|
||||
instance.settingsInfo.Add(categoryHeaderMasked.gameObject);
|
||||
var num = 0.95f;
|
||||
var num2 = -6.53f;
|
||||
var categoryHeaderMasked =
|
||||
Object.Instantiate(instance.categoryHeaderOrigin, instance.settingsContainer, true);
|
||||
categoryHeaderMasked.SetHeader(StringNames.RoleQuotaLabel, 61);
|
||||
categoryHeaderMasked.transform.localScale = Vector3.one;
|
||||
categoryHeaderMasked.transform.localPosition = new Vector3(-9.77f, 1.26f, -2f);
|
||||
instance.settingsInfo.Add(categoryHeaderMasked.gameObject);
|
||||
|
||||
var list = new List<Type>();
|
||||
var list = new List<Type>();
|
||||
|
||||
foreach (var team in Enum.GetValues<ModdedRoleTeams>())
|
||||
{
|
||||
var filteredRoles = SelectedMod.CustomRoles.Values
|
||||
.OfType<ICustomRole>()
|
||||
.Where(x => !x.HideSettings && x.Team == team).ToList();
|
||||
foreach (var team in Enum.GetValues<ModdedRoleTeams>())
|
||||
{
|
||||
var filteredRoles = SelectedMod.CustomRoles.Values
|
||||
.OfType<ICustomRole>()
|
||||
.Where(x => !x.HideSettings && x.Team == team).ToList();
|
||||
|
||||
if (filteredRoles.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (filteredRoles.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var categoryHeaderRoleVariant =
|
||||
Object.Instantiate(instance.categoryHeaderRoleOrigin, instance.settingsContainer, true);
|
||||
var categoryHeaderRoleVariant =
|
||||
Object.Instantiate(instance.categoryHeaderRoleOrigin, instance.settingsContainer, true);
|
||||
|
||||
switch (team)
|
||||
{
|
||||
case ModdedRoleTeams.Crewmate:
|
||||
categoryHeaderRoleVariant.SetHeader(StringNames.CrewmateRolesHeader, 61);
|
||||
break;
|
||||
case ModdedRoleTeams.Impostor:
|
||||
categoryHeaderRoleVariant.SetHeader(StringNames.ImpostorRolesHeader, 61);
|
||||
break;
|
||||
case ModdedRoleTeams.Neutral:
|
||||
default:
|
||||
categoryHeaderRoleVariant.SetHeader(StringNames.CrewmateRolesHeader, 61);
|
||||
categoryHeaderRoleVariant.Title.text = team + " Roles";
|
||||
categoryHeaderRoleVariant.Background.color = Color.gray;
|
||||
categoryHeaderRoleVariant.Title.color = Color.white;
|
||||
break;
|
||||
}
|
||||
switch (team)
|
||||
{
|
||||
case ModdedRoleTeams.Crewmate:
|
||||
categoryHeaderRoleVariant.SetHeader(StringNames.CrewmateRolesHeader, 61);
|
||||
break;
|
||||
case ModdedRoleTeams.Impostor:
|
||||
categoryHeaderRoleVariant.SetHeader(StringNames.ImpostorRolesHeader, 61);
|
||||
break;
|
||||
default:
|
||||
categoryHeaderRoleVariant.SetHeader(StringNames.CrewmateRolesHeader, 61);
|
||||
categoryHeaderRoleVariant.Title.text = team + " Roles";
|
||||
categoryHeaderRoleVariant.Background.color = Color.gray;
|
||||
categoryHeaderRoleVariant.Title.color = Color.white;
|
||||
break;
|
||||
}
|
||||
|
||||
categoryHeaderRoleVariant.transform.localScale = Vector3.one;
|
||||
categoryHeaderRoleVariant.transform.localPosition = new Vector3(0.09f, num, -2f);
|
||||
instance.settingsInfo.Add(categoryHeaderRoleVariant.gameObject);
|
||||
num -= 0.696f;
|
||||
categoryHeaderRoleVariant.transform.localScale = Vector3.one;
|
||||
categoryHeaderRoleVariant.transform.localPosition = new Vector3(0.09f, num, -2f);
|
||||
instance.settingsInfo.Add(categoryHeaderRoleVariant.gameObject);
|
||||
num -= 0.696f;
|
||||
|
||||
foreach (var customRole in filteredRoles)
|
||||
{
|
||||
var roleBehaviour = customRole as RoleBehaviour;
|
||||
if (roleBehaviour == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
foreach (var customRole in filteredRoles)
|
||||
{
|
||||
var roleBehaviour = customRole as RoleBehaviour;
|
||||
if (roleBehaviour == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var chancePerGame =
|
||||
GameOptionsManager.Instance.CurrentGameOptions.RoleOptions.GetChancePerGame(roleBehaviour.Role);
|
||||
var numPerGame =
|
||||
GameOptionsManager.Instance.CurrentGameOptions.RoleOptions.GetNumPerGame(roleBehaviour.Role);
|
||||
var chancePerGame =
|
||||
GameOptionsManager.Instance.CurrentGameOptions.RoleOptions.GetChancePerGame(roleBehaviour.Role);
|
||||
var numPerGame =
|
||||
GameOptionsManager.Instance.CurrentGameOptions.RoleOptions.GetNumPerGame(roleBehaviour.Role);
|
||||
|
||||
var flag = numPerGame == 0;
|
||||
var flag = numPerGame == 0;
|
||||
|
||||
var viewSettingsInfoPanelRoleVariant =
|
||||
Object.Instantiate(
|
||||
instance.infoPanelRoleOrigin, instance.settingsContainer, true);
|
||||
viewSettingsInfoPanelRoleVariant.transform.localScale = Vector3.one;
|
||||
viewSettingsInfoPanelRoleVariant.transform.localPosition = new Vector3(num2, num, -2f);
|
||||
var viewSettingsInfoPanelRoleVariant =
|
||||
Object.Instantiate(
|
||||
instance.infoPanelRoleOrigin, instance.settingsContainer, true);
|
||||
viewSettingsInfoPanelRoleVariant.transform.localScale = Vector3.one;
|
||||
viewSettingsInfoPanelRoleVariant.transform.localPosition = new Vector3(num2, num, -2f);
|
||||
|
||||
var advancedRoleOptions = SelectedMod.Options
|
||||
.Where(x => x.AdvancedRole == customRole.GetType())
|
||||
.ToList();
|
||||
var advancedRoleOptions = SelectedMod.Options
|
||||
.Where(x => x.AdvancedRole == customRole.GetType())
|
||||
.ToList();
|
||||
|
||||
if (!flag && advancedRoleOptions.Count > 0)
|
||||
{
|
||||
list.Add(customRole.GetType());
|
||||
}
|
||||
if (!flag && advancedRoleOptions.Count > 0)
|
||||
{
|
||||
list.Add(customRole.GetType());
|
||||
}
|
||||
|
||||
viewSettingsInfoPanelRoleVariant.SetInfo(roleBehaviour.NiceName, numPerGame, chancePerGame,
|
||||
61, customRole.RoleColor, customRole.Icon.LoadAsset(), true);
|
||||
viewSettingsInfoPanelRoleVariant.SetInfo(roleBehaviour.NiceName, numPerGame, chancePerGame,
|
||||
61, customRole.RoleColor, customRole.Icon.LoadAsset(), true);
|
||||
|
||||
viewSettingsInfoPanelRoleVariant.titleText.color =
|
||||
viewSettingsInfoPanelRoleVariant.chanceTitle.color =
|
||||
viewSettingsInfoPanelRoleVariant.chanceBackground.color =
|
||||
viewSettingsInfoPanelRoleVariant.background.color =
|
||||
customRole.RoleColor.GetAlternateColor();
|
||||
instance.settingsInfo.Add(viewSettingsInfoPanelRoleVariant.gameObject);
|
||||
num -= 0.664f;
|
||||
}
|
||||
viewSettingsInfoPanelRoleVariant.titleText.color =
|
||||
viewSettingsInfoPanelRoleVariant.chanceTitle.color =
|
||||
viewSettingsInfoPanelRoleVariant.chanceBackground.color =
|
||||
viewSettingsInfoPanelRoleVariant.background.color =
|
||||
customRole.RoleColor.GetAlternateColor();
|
||||
instance.settingsInfo.Add(viewSettingsInfoPanelRoleVariant.gameObject);
|
||||
num -= 0.664f;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (list.Count > 0)
|
||||
{
|
||||
var categoryHeaderMasked2 =
|
||||
Object.Instantiate(instance.categoryHeaderOrigin, instance.settingsContainer, true);
|
||||
categoryHeaderMasked2.SetHeader(StringNames.RoleSettingsLabel, 61);
|
||||
categoryHeaderMasked2.transform.localScale = Vector3.one;
|
||||
categoryHeaderMasked2.transform.localPosition = new Vector3(-9.77f, num, -2f);
|
||||
instance.settingsInfo.Add(categoryHeaderMasked2.gameObject);
|
||||
num -= 1.7f;
|
||||
var num3 = 0f;
|
||||
for (var k = 0; k < list.Count; k++)
|
||||
{
|
||||
float num4;
|
||||
if (k % 2 == 0)
|
||||
{
|
||||
num4 = -5.8f;
|
||||
if (k > 0)
|
||||
{
|
||||
num -= num3 + 0.59f;
|
||||
num3 = 0f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
num4 = 0.14999962f;
|
||||
}
|
||||
if (list.Count > 0)
|
||||
{
|
||||
var categoryHeaderMasked2 =
|
||||
Object.Instantiate(instance.categoryHeaderOrigin, instance.settingsContainer, true);
|
||||
categoryHeaderMasked2.SetHeader(StringNames.RoleSettingsLabel, 61);
|
||||
categoryHeaderMasked2.transform.localScale = Vector3.one;
|
||||
categoryHeaderMasked2.transform.localPosition = new Vector3(-9.77f, num, -2f);
|
||||
instance.settingsInfo.Add(categoryHeaderMasked2.gameObject);
|
||||
num -= 1.7f;
|
||||
var num3 = 0f;
|
||||
for (var k = 0; k < list.Count; k++)
|
||||
{
|
||||
float num4;
|
||||
if (k % 2 == 0)
|
||||
{
|
||||
num4 = -5.8f;
|
||||
if (k > 0)
|
||||
{
|
||||
num -= num3 + 0.59f;
|
||||
num3 = 0f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
num4 = 0.14999962f;
|
||||
}
|
||||
|
||||
var advancedRoleViewPanel =
|
||||
Object.Instantiate(instance.advancedRolePanelOrigin, instance.settingsContainer, true);
|
||||
advancedRoleViewPanel.transform.localScale = Vector3.one;
|
||||
advancedRoleViewPanel.transform.localPosition = new Vector3(num4, num, -2f);
|
||||
var num5 = SetUpAdvancedRoleViewPanel(advancedRoleViewPanel, list[k], 0.59f, 61);
|
||||
var advancedRoleViewPanel =
|
||||
Object.Instantiate(instance.advancedRolePanelOrigin, instance.settingsContainer, true);
|
||||
advancedRoleViewPanel.transform.localScale = Vector3.one;
|
||||
advancedRoleViewPanel.transform.localPosition = new Vector3(num4, num, -2f);
|
||||
var num5 = SetUpAdvancedRoleViewPanel(advancedRoleViewPanel, list[k], 0.59f, 61);
|
||||
|
||||
if (num5 > num3)
|
||||
{
|
||||
num3 = num5;
|
||||
}
|
||||
if (num5 > num3)
|
||||
{
|
||||
num3 = num5;
|
||||
}
|
||||
|
||||
instance.settingsInfo.Add(advancedRoleViewPanel.gameObject);
|
||||
}
|
||||
}
|
||||
instance.settingsInfo.Add(advancedRoleViewPanel.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
instance.scrollBar.SetYBoundsMax(-num);
|
||||
instance.scrollBar.SetYBoundsMax(-num);
|
||||
}
|
||||
|
||||
private static float SetUpAdvancedRoleViewPanel(AdvancedRoleViewPanel viewPanel, Type roleType, float spacingY, int maskLayer)
|
||||
|
||||
@@ -30,7 +30,7 @@ public static class RoleSettingMenuPatches
|
||||
var maskBg = __instance.scrollBar.transform.FindChild("MaskBg");
|
||||
var hitbox = __instance.scrollBar.transform.FindChild("Hitbox");
|
||||
|
||||
if (GameSettingMenuPatches.CurrentSelectedMod == 0)
|
||||
if (GameSettingMenuPatches.SelectedModIdx == 0)
|
||||
{
|
||||
__instance.AllButton.transform.parent.gameObject.SetActive(true);
|
||||
__instance.AllButton.gameObject.SetActive(true);
|
||||
@@ -127,7 +127,7 @@ public static class RoleSettingMenuPatches
|
||||
[HarmonyPatch(nameof(RolesSettingsMenu.Update))]
|
||||
public static bool UpdatePatch()
|
||||
{
|
||||
return GameSettingMenuPatches.CurrentSelectedMod == 0;
|
||||
return GameSettingMenuPatches.SelectedModIdx == 0;
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
|
||||
@@ -1,14 +1,29 @@
|
||||
using HarmonyLib;
|
||||
using System.Globalization;
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Utilities;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Patches.Options;
|
||||
|
||||
/// <summary>
|
||||
/// Patches for the various game settings.
|
||||
/// </summary>
|
||||
[HarmonyPatch]
|
||||
public class SettingPatches
|
||||
{
|
||||
/// <summary>
|
||||
/// Prefix for the <see cref="FloatGameSetting.GetValueString"/> method. Adds support for custom number suffixes.
|
||||
/// </summary>
|
||||
/// <param name="__instance">The FloatGameSetting instance.</param>
|
||||
/// <param name="__result">The result of the GetValueString method.</param>
|
||||
/// <param name="value">The float value.</param>
|
||||
/// <returns>False to skip original method.</returns>
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(FloatGameSetting), nameof(FloatGameSetting.GetValueString))]
|
||||
public static bool ValueStringPatch(FloatGameSetting __instance, ref string __result, [HarmonyArgument(0)] float value)
|
||||
public static bool ValueStringPatch(
|
||||
FloatGameSetting __instance,
|
||||
ref string __result,
|
||||
[HarmonyArgument(0)] float value)
|
||||
{
|
||||
var result = string.Empty;
|
||||
var suffix = (MiraNumberSuffixes)__instance.SuffixType;
|
||||
@@ -31,12 +46,15 @@ public class SettingPatches
|
||||
}
|
||||
else
|
||||
{
|
||||
result = DestroyableSingleton<TranslationController>.Instance.GetString(StringNames.GameSecondsAbbrev, (Il2CppSystem.Object[])(
|
||||
[
|
||||
value.ToString(__instance.FormatString)
|
||||
]));
|
||||
result = DestroyableSingleton<TranslationController>.Instance.GetString(
|
||||
StringNames.GameSecondsAbbrev,
|
||||
(Il2CppSystem.Object[])
|
||||
[
|
||||
value.ToString(__instance.FormatString, CultureInfo.InvariantCulture)
|
||||
]);
|
||||
}
|
||||
|
||||
__result = result;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
using HarmonyLib;
|
||||
using Il2CppSystem;
|
||||
using Il2CppSystem.Collections.Generic;
|
||||
using Il2CppSystem.Text;
|
||||
using MiraAPI.PluginLoading;
|
||||
using MiraAPI.Roles;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
@@ -63,7 +62,7 @@ public static class TaskAdderPatch
|
||||
_rolesFolder.FolderName = "Roles";
|
||||
_rolesFolder.name = "RolesFolder";
|
||||
|
||||
foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins.Values)
|
||||
foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins())
|
||||
{
|
||||
var newFolder = Object.Instantiate(__instance.RootFolderPrefab, _scroller.Inner);
|
||||
newFolder.FolderName = newFolder.name = plugin.PluginInfo.Metadata.Name;
|
||||
|
||||
@@ -6,16 +6,44 @@ using System.Collections.Generic;
|
||||
|
||||
namespace MiraAPI.PluginLoading;
|
||||
|
||||
public class MiraPluginInfo(IMiraPlugin miraPlugin, PluginInfo info)
|
||||
/// <summary>
|
||||
/// Represents a Mira plugin.
|
||||
/// </summary>
|
||||
public class MiraPluginInfo
|
||||
{
|
||||
public string PluginId { get; set; } = info.Metadata.GUID;
|
||||
public IMiraPlugin MiraPlugin { get; set; } = miraPlugin;
|
||||
public PluginInfo PluginInfo { get; set; } = info;
|
||||
public ConfigFile PluginConfig { get; set; } = miraPlugin.GetConfigFile();
|
||||
internal MiraPluginInfo(IMiraPlugin miraPlugin, PluginInfo info)
|
||||
{
|
||||
MiraPlugin = miraPlugin;
|
||||
PluginConfig = miraPlugin.GetConfigFile();
|
||||
PluginInfo = info;
|
||||
PluginId = info.Metadata.GUID;
|
||||
}
|
||||
|
||||
public readonly List<AbstractOptionGroup> OptionGroups = [];
|
||||
public readonly List<IModdedOption> Options = [];
|
||||
internal List<AbstractOptionGroup> OptionGroups { get; } = [];
|
||||
|
||||
public readonly Dictionary<ushort, RoleBehaviour> CustomRoles = [];
|
||||
internal readonly Dictionary<int, CustomGameMode> GameModes = [];
|
||||
}
|
||||
internal List<IModdedOption> Options { get; } = [];
|
||||
|
||||
internal Dictionary<ushort, RoleBehaviour> CustomRoles { get; } = [];
|
||||
|
||||
internal Dictionary<int, CustomGameMode> GameModes { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin's ID, as defined in the plugin's BepInEx metadata.
|
||||
/// </summary>
|
||||
public string PluginId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin's instance as an <see cref="IMiraPlugin"/>.
|
||||
/// </summary>
|
||||
public IMiraPlugin MiraPlugin { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin's BepInEx metadata.
|
||||
/// </summary>
|
||||
public PluginInfo PluginInfo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin's configuration file.
|
||||
/// </summary>
|
||||
public ConfigFile PluginConfig { get; }
|
||||
}
|
||||
@@ -1,53 +1,49 @@
|
||||
using BepInEx.Unity.IL2CPP;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using BepInEx.Unity.IL2CPP;
|
||||
using MiraAPI.Colors;
|
||||
using MiraAPI.GameOptions;
|
||||
using MiraAPI.GameOptions.Attributes;
|
||||
using MiraAPI.Hud;
|
||||
using MiraAPI.Modifiers;
|
||||
using MiraAPI.Roles;
|
||||
using Reactor.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using MiraAPI.Colors;
|
||||
using MiraAPI.Utilities;
|
||||
using Reactor.Networking;
|
||||
using Reactor.Utilities;
|
||||
|
||||
namespace MiraAPI.PluginLoading;
|
||||
|
||||
internal class MiraPluginManager
|
||||
{
|
||||
public readonly Dictionary<Assembly, MiraPluginInfo> RegisteredPlugins = [];
|
||||
private readonly Dictionary<Assembly, MiraPluginInfo> _registeredPlugins = [];
|
||||
|
||||
private static MiraPluginManager _instance;
|
||||
internal MiraPluginInfo[] RegisteredPlugins() => [.. _registeredPlugins.Values];
|
||||
|
||||
public static MiraPluginManager Instance
|
||||
{
|
||||
get => _instance ??= new MiraPluginManager();
|
||||
private set => _instance = value;
|
||||
}
|
||||
public static MiraPluginManager Instance { get; private set; } = new();
|
||||
|
||||
internal void Initialize()
|
||||
{
|
||||
Instance = this;
|
||||
IL2CPPChainloader.Instance.PluginLoad += (pluginInfo, assembly, plugin) =>
|
||||
{
|
||||
if (!plugin.GetType().GetInterfaces().Contains(typeof(IMiraPlugin)))
|
||||
if (plugin is not IMiraPlugin miraPlugin)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var info = new MiraPluginInfo(plugin as IMiraPlugin, pluginInfo);
|
||||
var info = new MiraPluginInfo(miraPlugin, pluginInfo);
|
||||
|
||||
RegisterModifierAttribute(assembly);
|
||||
RegisterAllOptions(assembly, info);
|
||||
|
||||
|
||||
RegisterRoleAttribute(assembly, info);
|
||||
RegisterButtonAttribute(assembly);
|
||||
|
||||
RegisterColorClasses(assembly);
|
||||
|
||||
RegisteredPlugins.Add(assembly, info);
|
||||
_registeredPlugins.Add(assembly, info);
|
||||
|
||||
Logger<MiraApiPlugin>.Info($"Registering mod {pluginInfo.Metadata.GUID} with Mira API.");
|
||||
};
|
||||
@@ -55,7 +51,7 @@ internal class MiraPluginManager
|
||||
|
||||
public MiraPluginInfo GetPluginByGuid(string guid)
|
||||
{
|
||||
return RegisteredPlugins.Values.First(plugin => plugin.PluginId == guid);
|
||||
return _registeredPlugins.Values.First(plugin => plugin.PluginId == guid);
|
||||
}
|
||||
|
||||
private static void RegisterAllOptions(Assembly assembly, MiraPluginInfo pluginInfo)
|
||||
@@ -68,7 +64,7 @@ internal class MiraPluginManager
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
foreach (var property in type.GetProperties())
|
||||
{
|
||||
if (property.PropertyType.IsAssignableTo(typeof(IModdedOption)))
|
||||
@@ -76,7 +72,7 @@ internal class MiraPluginManager
|
||||
ModdedOptionsManager.RegisterPropertyOption(type, property, pluginInfo);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
var attribute = property.GetCustomAttribute<ModdedOptionAttribute>();
|
||||
if (attribute == null)
|
||||
{
|
||||
@@ -98,22 +94,22 @@ internal class MiraPluginManager
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (!(typeof(RoleBehaviour).IsAssignableFrom(type) && typeof(ICustomRole).IsAssignableFrom(type)))
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"{type.Name} does not inherit from RoleBehaviour or ICustomRole.");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (!ModList.GetById(pluginInfo.PluginId).IsRequiredOnAllClients)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error("Custom roles are only supported on all clients.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
roles.Add(type);
|
||||
}
|
||||
|
||||
|
||||
CustomRoleManager.RegisterRoleTypes(roles, pluginInfo);
|
||||
}
|
||||
|
||||
@@ -138,14 +134,13 @@ internal class MiraPluginManager
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var color = (CustomColor)property.GetValue(null);
|
||||
if (color == null)
|
||||
|
||||
if (property.GetValue(null) is not CustomColor color)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Color property {property.Name} in {type.Name} is null.");
|
||||
Logger<MiraApiPlugin>.Error($"Color property {property.Name} in {type.Name} is not a CustomColor.");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
PaletteManager.CustomColors.Add(color);
|
||||
}
|
||||
}
|
||||
@@ -163,7 +158,6 @@ internal class MiraPluginManager
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void RegisterButtonAttribute(Assembly assembly)
|
||||
{
|
||||
foreach (var type in assembly.GetTypes())
|
||||
@@ -175,4 +169,4 @@ internal class MiraPluginManager
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,7 +122,7 @@ public static class CustomRoleManager
|
||||
/// <returns>A MiraPluginInfo object representing the parent mod of the role.</returns>
|
||||
public static MiraPluginInfo FindParentMod(ICustomRole role)
|
||||
{
|
||||
return MiraPluginManager.Instance.RegisteredPlugins.First(plugin => plugin.Value.CustomRoles.ContainsValue(role as RoleBehaviour ?? throw new InvalidOperationException())).Value;
|
||||
return MiraPluginManager.Instance.RegisteredPlugins().First(plugin => plugin.CustomRoles.ContainsValue(role as RoleBehaviour ?? throw new InvalidOperationException()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -197,7 +197,7 @@ public static class CustomRoleManager
|
||||
// we dont know how other plugins handle their configs
|
||||
// this way, all the options are saved at once, instead of one by one
|
||||
var oldConfigSetting = new Dictionary<MiraPluginInfo, bool>();
|
||||
foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins.Values)
|
||||
foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins())
|
||||
{
|
||||
oldConfigSetting.Add(plugin, plugin.PluginConfig.SaveOnConfigSet);
|
||||
plugin.PluginConfig.SaveOnConfigSet = false;
|
||||
@@ -239,7 +239,7 @@ public static class CustomRoleManager
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins.Values)
|
||||
foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins())
|
||||
{
|
||||
plugin.PluginConfig.Save();
|
||||
plugin.PluginConfig.SaveOnConfigSet = oldConfigSetting[plugin];
|
||||
|
||||
@@ -99,7 +99,7 @@ public static class Extensions
|
||||
|
||||
if (current.Count > 0)
|
||||
{
|
||||
chunks.Enqueue(current.ToArray());
|
||||
chunks.Enqueue([.. current]);
|
||||
}
|
||||
|
||||
return chunks;
|
||||
@@ -112,7 +112,7 @@ public static class Extensions
|
||||
/// <returns>True if the OptionBehaviour is for a custom options, false otherwise.</returns>
|
||||
public static bool IsCustom(this OptionBehaviour optionBehaviour)
|
||||
{
|
||||
return ModdedOptionsManager.ModdedOptions.Values.Any(opt => opt.OptionBehaviour && opt.OptionBehaviour.Equals(optionBehaviour));
|
||||
return ModdedOptionsManager.ModdedOptions.Values.Any(opt => opt.OptionBehaviour && opt.OptionBehaviour == optionBehaviour);
|
||||
}
|
||||
|
||||
private static readonly Dictionary<PlayerControl, ModifierComponent> ModifierComponents = [];
|
||||
@@ -208,13 +208,15 @@ public static class Extensions
|
||||
/// <typeparam name="T">The Type of the Modifier.</typeparam>
|
||||
public static void RpcRemoveModifier<T>(this PlayerControl player) where T : BaseModifier
|
||||
{
|
||||
if (!ModifierManager.TypeToIdModifiers.TryGetValue(typeof(T), out var id))
|
||||
var id = ModifierManager.GetModifierId(typeof(T));
|
||||
|
||||
if (id == null)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Cannot add modifier {typeof(T).Name} because it is not registered.");
|
||||
return;
|
||||
}
|
||||
|
||||
player.RpcRemoveModifier(id);
|
||||
player.RpcRemoveModifier(id.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -225,13 +227,13 @@ public static class Extensions
|
||||
[MethodRpc((uint)MiraRpc.AddModifier)]
|
||||
public static void RpcAddModifier(this PlayerControl target, uint modifierId)
|
||||
{
|
||||
if (ModifierManager.IdToTypeModifiers.TryGetValue(modifierId, out var type))
|
||||
var type = ModifierManager.GetModifierType(modifierId);
|
||||
if (type == null)
|
||||
{
|
||||
target.GetModifierComponent()?.AddModifier(type);
|
||||
Logger<MiraApiPlugin>.Error($"Cannot add modifier with id {modifierId} because it is not registered.");
|
||||
return;
|
||||
}
|
||||
|
||||
Logger<MiraApiPlugin>.Error($"Cannot add modifier with id {modifierId} because it is not registered.");
|
||||
target.GetModifierComponent()?.AddModifier(type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -241,13 +243,14 @@ public static class Extensions
|
||||
/// <typeparam name="T">The modifier Type.</typeparam>
|
||||
public static void RpcAddModifier<T>(this PlayerControl player) where T : BaseModifier
|
||||
{
|
||||
if (!ModifierManager.TypeToIdModifiers.TryGetValue(typeof(T), out var id))
|
||||
var id = ModifierManager.GetModifierId(typeof(T));
|
||||
if (id == null)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Cannot add modifier {typeof(T).Name} because it is not registered.");
|
||||
return;
|
||||
}
|
||||
|
||||
player.RpcAddModifier(id);
|
||||
player.RpcAddModifier(id.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
+140
-55
@@ -1,5 +1,6 @@
|
||||
using MiraAPI.Roles;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using TMPro;
|
||||
@@ -8,14 +9,35 @@ using Object = UnityEngine.Object;
|
||||
|
||||
namespace MiraAPI.Utilities;
|
||||
|
||||
/// <summary>
|
||||
/// A class that contains helper methods.
|
||||
/// </summary>
|
||||
public static class Helpers
|
||||
{
|
||||
public static readonly ContactFilter2D Filter = ContactFilter2D.CreateLegacyFilter(Constants.NotShipMask, float.MinValue, float.MaxValue);
|
||||
public static PlainShipRoom GetRoom(Vector3 pos)
|
||||
/// <summary>
|
||||
/// NotShipMask filter for collisions.
|
||||
/// </summary>
|
||||
public static readonly ContactFilter2D Filter = ContactFilter2D.CreateLegacyFilter(
|
||||
Constants.NotShipMask,
|
||||
float.MinValue,
|
||||
float.MaxValue);
|
||||
|
||||
/// <summary>
|
||||
/// Get the room at a specific position.
|
||||
/// </summary>
|
||||
/// <param name="pos">The position.</param>
|
||||
/// <returns>The ship room if its found.</returns>
|
||||
public static PlainShipRoom? GetRoom(Vector3 pos)
|
||||
{
|
||||
return ShipStatus.Instance.AllRooms.ToList().Find(room => room.roomArea.OverlapPoint(pos));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of dead bodies within a radius.
|
||||
/// </summary>
|
||||
/// <param name="source">The source location.</param>
|
||||
/// <param name="radius">The radius to search in.</param>
|
||||
/// <returns>A list of dead bodies.</returns>
|
||||
public static List<DeadBody> GetNearestDeadBodies(Vector2 source, float radius)
|
||||
{
|
||||
var results = new Il2CppSystem.Collections.Generic.List<Collider2D>();
|
||||
@@ -25,7 +47,16 @@ public static class Helpers
|
||||
.Select(collider2D => collider2D.GetComponent<DeadBody>()).ToList();
|
||||
}
|
||||
|
||||
public static List<T> GetNearestObjectsOfType<T>(Vector2 source, float radius, string? colliderTag = null) where T : Component
|
||||
/// <summary>
|
||||
/// Gets a list of objects within a radius.
|
||||
/// </summary>
|
||||
/// <param name="source">The source point.</param>
|
||||
/// <param name="radius">The radius to search in.</param>
|
||||
/// <param name="colliderTag">An optional collider tag.</param>
|
||||
/// <typeparam name="T">The type of the object.</typeparam>
|
||||
/// <returns>A list of objects of type T.</returns>
|
||||
public static List<T> GetNearestObjectsOfType<T>(Vector2 source, float radius, string? colliderTag = null)
|
||||
where T : Component
|
||||
{
|
||||
var results = new Il2CppSystem.Collections.Generic.List<Collider2D>();
|
||||
Physics2D.OverlapCircle(source, radius, Filter, results);
|
||||
@@ -34,29 +65,49 @@ public static class Helpers
|
||||
.Select(collider2D => collider2D.GetComponent<T>()).ToList();
|
||||
}
|
||||
|
||||
public static List<PlayerControl> GetClosestPlayersInCircle(Vector2 source, float radius, bool ignoreColliders = true)
|
||||
/// <summary>
|
||||
/// Gets the closest players to a specific point.
|
||||
/// </summary>
|
||||
/// <param name="source">The source point.</param>
|
||||
/// <param name="radius">The radius to search in.</param>
|
||||
/// <param name="ignoreColliders">Whether colliders should be ignored.</param>
|
||||
/// <returns>A list of Player Controls in the radius.</returns>
|
||||
public static List<PlayerControl> GetClosestPlayersInCircle(
|
||||
Vector2 source,
|
||||
float radius,
|
||||
bool ignoreColliders = true)
|
||||
{
|
||||
var newList = GetNearestObjectsOfType<PlayerControl>(source, radius);
|
||||
|
||||
if (ignoreColliders)
|
||||
if (!ignoreColliders)
|
||||
{
|
||||
var filteredList = new List<PlayerControl>();
|
||||
foreach (var player in newList)
|
||||
{
|
||||
var vector = player.GetTruePosition() - source;
|
||||
var magnitude = vector.magnitude;
|
||||
if (!PhysicsHelpers.AnyNonTriggersBetween(source, vector.normalized, magnitude, Constants.ShipAndObjectsMask))
|
||||
{
|
||||
filteredList.Add(player);
|
||||
}
|
||||
}
|
||||
|
||||
return filteredList;
|
||||
return newList;
|
||||
}
|
||||
|
||||
return newList;
|
||||
return (from player in newList
|
||||
let vector = player.GetTruePosition() - source
|
||||
let magnitude = vector.magnitude
|
||||
where !PhysicsHelpers.AnyNonTriggersBetween(
|
||||
source,
|
||||
vector.normalized,
|
||||
magnitude,
|
||||
Constants.ShipAndObjectsMask)
|
||||
select player).ToList();
|
||||
}
|
||||
public static List<PlayerControl> GetClosestPlayers(PlayerControl source, float distance = 2f, bool ignoreColliders = true, bool ignoreSource = true)
|
||||
|
||||
/// <summary>
|
||||
/// Gets the closest players to a specific player.
|
||||
/// </summary>
|
||||
/// <param name="source">The source player.</param>
|
||||
/// <param name="distance">Distance to search in.</param>
|
||||
/// <param name="ignoreColliders">Whether to ignore colliders.</param>
|
||||
/// <param name="ignoreSource">Whether to ignore the source player.</param>
|
||||
/// <returns>A list of PlayerControls.</returns>
|
||||
public static List<PlayerControl> GetClosestPlayers(
|
||||
PlayerControl source,
|
||||
float distance = 2f,
|
||||
bool ignoreColliders = true,
|
||||
bool ignoreSource = true)
|
||||
{
|
||||
if (!ShipStatus.Instance)
|
||||
{
|
||||
@@ -69,7 +120,17 @@ public static class Helpers
|
||||
return ignoreSource ? players.Where(plr => plr.PlayerId != source.PlayerId).ToList() : players;
|
||||
}
|
||||
|
||||
public static List<PlayerControl> GetClosestPlayers(Vector2 source, float distance = 2f, bool ignoreColliders = true)
|
||||
/// <summary>
|
||||
/// Gets the closest players to a specific point.
|
||||
/// </summary>
|
||||
/// <param name="source">The source point.</param>
|
||||
/// <param name="distance">The distance to search in.</param>
|
||||
/// <param name="ignoreColliders">Whether to ignore colliders.</param>
|
||||
/// <returns>A list of Player Controls.</returns>
|
||||
public static List<PlayerControl> GetClosestPlayers(
|
||||
Vector2 source,
|
||||
float distance = 2f,
|
||||
bool ignoreColliders = true)
|
||||
{
|
||||
if (!ShipStatus.Instance)
|
||||
{
|
||||
@@ -78,51 +139,65 @@ public static class Helpers
|
||||
|
||||
List<PlayerControl> outputList = [];
|
||||
outputList.Clear();
|
||||
var allPlayers = GameData.Instance.AllPlayers.ToArray().Select(x=>x.Object);
|
||||
var allPlayers = GameData.Instance.AllPlayers.ToArray().Select(x => x.Object);
|
||||
|
||||
foreach (var playerControl in allPlayers)
|
||||
{
|
||||
if (!playerControl || !playerControl.Collider.enabled)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
outputList.AddRange(
|
||||
from playerControl in allPlayers
|
||||
where playerControl && playerControl.Collider.enabled
|
||||
let vector = playerControl.GetTruePosition() - source
|
||||
let magnitude = vector.magnitude
|
||||
where magnitude <= distance && (ignoreColliders || !PhysicsHelpers.AnyNonTriggersBetween(
|
||||
source,
|
||||
vector.normalized,
|
||||
magnitude,
|
||||
Constants.ShipAndObjectsMask))
|
||||
select playerControl);
|
||||
|
||||
var vector = playerControl.GetTruePosition() - source;
|
||||
var magnitude = vector.magnitude;
|
||||
if (magnitude <= distance && (ignoreColliders || !PhysicsHelpers.AnyNonTriggersBetween(source, vector.normalized, magnitude, Constants.ShipAndObjectsMask)))
|
||||
outputList.Sort(
|
||||
delegate(PlayerControl a, PlayerControl b)
|
||||
{
|
||||
outputList.Add(playerControl);
|
||||
}
|
||||
}
|
||||
var magnitude2 = (a.GetTruePosition() - source).magnitude;
|
||||
var magnitude3 = (b.GetTruePosition() - source).magnitude;
|
||||
if (magnitude2 > magnitude3)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
outputList.Sort(delegate (PlayerControl a, PlayerControl b)
|
||||
{
|
||||
var magnitude2 = (a.GetTruePosition() - source).magnitude;
|
||||
var magnitude3 = (b.GetTruePosition() - source).magnitude;
|
||||
if (magnitude2 > magnitude3)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (magnitude2 < magnitude3)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
if (magnitude2 < magnitude3)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
return outputList;
|
||||
}
|
||||
|
||||
public static TextMeshPro CreateTextLabel(string name, Transform parent,
|
||||
AspectPosition.EdgeAlignments alignment, Vector3 distance, float fontSize = 2f,
|
||||
TextAlignmentOptions textAlignment = TextAlignmentOptions.Center)
|
||||
/// <summary>
|
||||
/// Creates a TextMeshPro object with the specified parameters.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the object.</param>
|
||||
/// <param name="parent">The object parent.</param>
|
||||
/// <param name="alignment">The alignment of the TMP object.</param>
|
||||
/// <param name="distance">The distance from the edge.</param>
|
||||
/// <param name="fontSize">The font size.</param>
|
||||
/// <param name="textAlignment">The text alignment.</param>
|
||||
/// <returns>A new TMP object.</returns>
|
||||
public static TextMeshPro CreateTextLabel(
|
||||
string name,
|
||||
Transform parent,
|
||||
AspectPosition.EdgeAlignments alignment,
|
||||
Vector3 distance,
|
||||
float fontSize = 2f,
|
||||
TextAlignmentOptions textAlignment = TextAlignmentOptions.Center)
|
||||
{
|
||||
var textObj = new GameObject(name)
|
||||
{
|
||||
transform =
|
||||
{
|
||||
parent = parent
|
||||
parent = parent,
|
||||
},
|
||||
layer = LayerMask.NameToLayer("UI")
|
||||
layer = LayerMask.NameToLayer("UI"),
|
||||
};
|
||||
|
||||
var textMeshPro = textObj.AddComponent<TextMeshPro>();
|
||||
@@ -139,11 +214,21 @@ TextAlignmentOptions textAlignment = TextAlignmentOptions.Center)
|
||||
return textMeshPro;
|
||||
}
|
||||
|
||||
public static DeadBody GetBodyById(byte id)
|
||||
/// <summary>
|
||||
/// Gets a DeadBody by its parent ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The player ID.</param>
|
||||
/// <returns>A dead body or null if its not found.</returns>
|
||||
public static DeadBody? GetBodyById(byte id)
|
||||
{
|
||||
return Object.FindObjectsOfType<DeadBody>().FirstOrDefault(body => body.ParentId == id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the suffix for a MiraNumberSuffixes enum.
|
||||
/// </summary>
|
||||
/// <param name="suffix">The MiraNumberSuffixes enum.</param>
|
||||
/// <returns>A suffix based on the enum.</returns>
|
||||
public static string GetSuffix(MiraNumberSuffixes suffix)
|
||||
{
|
||||
return suffix switch
|
||||
@@ -164,9 +249,9 @@ TextAlignmentOptions textAlignment = TextAlignmentOptions.Center)
|
||||
public static StringBuilder CreateForRole(ICustomRole role)
|
||||
{
|
||||
var taskStringBuilder = new StringBuilder();
|
||||
taskStringBuilder.AppendLine($"{role.RoleColor.ToTextColor()}You are a <b>{role.RoleName}.</b></color>");
|
||||
taskStringBuilder.AppendLine(CultureInfo.InvariantCulture, $"{role.RoleColor.ToTextColor()}You are a <b>{role.RoleName}.</b></color>");
|
||||
taskStringBuilder.Append("<size=70%>");
|
||||
taskStringBuilder.AppendLine($"{role.RoleLongDescription}");
|
||||
taskStringBuilder.AppendLine(CultureInfo.InvariantCulture, $"{role.RoleLongDescription}");
|
||||
return taskStringBuilder;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user