Merge pull request #2 from All-Of-Us-Mods/6.18

6.18 support (plus tons of extra stuff) (we should really switch to trunk based development)
This commit is contained in:
XtraCube authored and GitHub committed 2024-08-18 12:57:48 -04:00
commit 888a8b3a1d
64 files changed
+1493 -1097

No files matched your search

+6 -3
View File
@@ -1,3 +1,6 @@
.vs
obj
bin
.idea/
.vs/
obj/
bin/
*.user
+20
View File
@@ -0,0 +1,20 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/.idea.MiraAPI.iml
/contentModel.xml
/projectSettingsUpdater.xml
/modules.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
bin/
obj/
.git/
.idea/
.vs/
*.user
+13 -7
View File
@@ -1,15 +1,21 @@
using MiraAPI.Roles;
using MiraAPI.GameOptions.Attributes;
using MiraAPI.Roles;
using MiraAPI.Utilities.Assets;
using UnityEngine;
namespace MiraAPI.Example
{
[RegisterCustomRole(ExamplePlugin.Id)]
public class CustomRole : CrewmateRole, ICustomRole
[RegisterCustomRole]
public class CustomRole : ImpostorRole, ICustomRole
{
public string RoleName => "Test Role";
public string RoleDescription => "Role decs?";
public string RoleLongDescription => "Extra role decs?";
public string RoleName => "fortnite killer";
public string RoleLongDescription => "ok so your objective is to eliminate everyone with your fortnite tactical shotgun, good luck";
public string RoleDescription => RoleLongDescription;
public Color RoleColor => Palette.Orange;
public ModdedRoleTeams Team => ModdedRoleTeams.Neutral;
public ModdedRoleTeams Team => ModdedRoleTeams.Impostor;
public LoadableAsset<Sprite> OptionsScreenshot => MiraAssets.Banner;
[ModdedNumberOption("Testing level", min: 3, max: 9, roleType: typeof(CustomRole))] public float testingLevel { get; set; } = 4;
}
}
+21
View File
@@ -0,0 +1,21 @@
using MiraAPI.GameOptions.Attributes;
using MiraAPI.Roles;
using MiraAPI.Utilities.Assets;
using UnityEngine;
namespace MiraAPI.Example
{
[RegisterCustomRole]
public class CustomRole2 : CrewmateRole, ICustomRole
{
public string RoleName => "fortnite defender";
public string RoleLongDescription => "ok so your objective is to save everyone from the fortnite killer";
public string RoleDescription => RoleLongDescription;
public Color RoleColor => Palette.AcceptedGreen;
public ModdedRoleTeams Team => ModdedRoleTeams.Crewmate;
public LoadableAsset<Sprite> OptionsScreenshot => MiraAssets.Banner;
[ModdedToggleOption("Teleportation ability", roleType: typeof(CustomRole2))] public bool teleport { get; set; } = true;
}
}
+18
View File
@@ -0,0 +1,18 @@
using MiraAPI.Roles;
using MiraAPI.Utilities.Assets;
using UnityEngine;
namespace MiraAPI.Example
{
[RegisterCustomRole]
public class CustomRole3 : CrewmateRole, ICustomRole
{
public string RoleName => "fortnite helper";
public string RoleLongDescription => "ok so your objective is to help";
public string RoleDescription => RoleLongDescription;
public Color RoleColor => Palette.Brown;
public ModdedRoleTeams Team => ModdedRoleTeams.Crewmate;
public LoadableAsset<Sprite> OptionsScreenshot => MiraAssets.Banner;
}
}
+14 -5
View File
@@ -9,11 +9,20 @@ namespace MiraAPI.Example
public string GroupName => "General Group";
public Color GroupColor => Color.red;
[ModdedToggleOption("Use Thing")] public bool useThing { get; set; } = true;
[ModdedToggleOption("Use another thing")] public bool useAnotherThing { get; set; } = false;
[ModdedNumberOption("Sussy level", min: 0, max: 10)] public float sussyLevel { get; set; } = 4f;
[ModdedStringOption("Sus choices", ["hello", "hello 2", "hello 3"])] public int susChoices { get; set; } = 0;
[ModdedEnumOption("Enum test", typeof(TestEnum))] public TestEnum enumTest { get; set; } = TestEnum.Hello;
[ModdedToggleOption("Use Thing")]
public bool useThing { get; set; } = false;
[ModdedToggleOption("Use another thing")]
public bool useAnotherThing { get; set; } = true;
[ModdedNumberOption("Sussy level", min: 0, max: 10)]
public float sussyLevel { get; set; } = 4f;
[ModdedStringOption("Sus choices", ["hello", "hello 2", "hello 3"])]
public int susChoices { get; set; } = 2;
[ModdedEnumOption("Enum test", typeof(TestEnum))]
public TestEnum enumTest { get; set; } = TestEnum.Bye;
}
public enum TestEnum
+4 -10
View File
@@ -1,7 +1,8 @@
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using MiraAPI.Utilities.Assets;
using MiraAPI.PluginLoading;
using Reactor;
using Reactor.Networking;
using Reactor.Networking.Attributes;
@@ -12,19 +13,12 @@ namespace MiraAPI.Example;
[BepInProcess("Among Us.exe")]
[BepInDependency(ReactorPlugin.Id)]
[ReactorModFlags(ModFlags.RequireOnAllClients)]
public partial class ExamplePlugin : BasePlugin, IMiraConfig
public partial class ExamplePlugin : BasePlugin, IMiraPlugin
{
public Harmony Harmony { get; } = new(Id);
public static ExamplePlugin Instance { get; private set; }
public ModdedOptionTabSettings TabSettings => new ModdedOptionTabSettings()
{
Title = "MiraAPI Example Mod",
TabIcon = MiraAssets.Empty
};
public ConfigFile GetConfigFile() => Config;
public override void Load()
{
Instance = this;
Harmony.PatchAll();
}
}
+2 -1
View File
@@ -4,6 +4,7 @@ namespace MiraAPI.Example
{
public class SomeRandomNonGroupClass
{
[ModdedStringOption("Yeah, idk", ["Idk 1", "idk 2", "idk 3", "idk 4"])] public static int yeaIdk { get; set; } = 2;
[ModdedStringOption("Yeah, idk", ["Idk 1", "idk 2", "idk 3", "idk 4"])] public static int yeaIdk { get; set; } = 3;
[ModdedNumberOption("Aw man", min: 45, max: 95)] public float sussyLevel { get; set; } = 63;
}
}
+2 -2
View File
@@ -91,7 +91,7 @@ public abstract class CustomGameMode
/// Custom winner selection
/// </summary>
/// <returns>List of winners</returns>
public virtual List<GameData.PlayerInfo> CalculateWinners() => null;
public virtual List<NetworkedPlayerInfo> CalculateWinners() => null;
/// <summary>
/// Show gamemode in Intro Cutscene
@@ -133,6 +133,6 @@ public abstract class CustomGameMode
/// </summary>
/// <param name="vent">Target vent</param>
/// <param name="playerInfo">Player attempting to vent</param>
public virtual bool CanVent(Vent vent, GameData.PlayerInfo playerInfo) => true;
public virtual bool CanVent(Vent vent, NetworkedPlayerInfo playerInfo) => true;
}
+4 -4
View File
@@ -37,7 +37,7 @@ public static class CustomGameModeManager
return;
}
Logger<MiraAPIPlugin>.Error($"No gamemode with id {id} found!");
Logger<MiraApiPlugin>.Error($"No gamemode with id {id} found!");
}
/// <summary>
@@ -48,7 +48,7 @@ public static class CustomGameModeManager
{
if (!typeof(CustomGameMode).IsAssignableFrom(gameModeType))
{
Logger<MiraAPIPlugin>.Warning($"{gameModeType?.Name} does not inherit CustomGameMode!");
Logger<MiraApiPlugin>.Warning($"{gameModeType?.Name} does not inherit CustomGameMode!");
return;
}
@@ -56,13 +56,13 @@ public static class CustomGameModeManager
if (GameModes.Any(x => x.Key == gameMode?.Id))
{
Logger<MiraAPIPlugin>.Error($"ID for gamemode {gameMode?.Name} already exists!");
Logger<MiraApiPlugin>.Error($"ID for gamemode {gameMode?.Name} already exists!");
return;
}
if (gameMode == null)
{
Logger<MiraAPIPlugin>.Error($"GAMEMODE WITH TYPE {gameModeType.Name} IS NULL");
Logger<MiraApiPlugin>.Error($"GAMEMODE WITH TYPE {gameModeType.Name} IS NULL");
return;
}
@@ -1,7 +1,6 @@
using MiraAPI.GameOptions.OptionTypes;
using System;
using System.Reflection;
using UnityEngine;
namespace MiraAPI.GameOptions.Attributes
{
@@ -24,7 +23,6 @@ namespace MiraAPI.GameOptions.Attributes
public override IModdedOption CreateOption(object value, PropertyInfo property)
{
Debug.LogError(value.ToString());
var toggleOpt = new ModdedNumberOption(Title, (float)value, Min, Max, Increment, SuffixType, ZeroInfinity, RoleType);
return toggleOpt;
}
@@ -15,23 +15,6 @@ namespace MiraAPI.GameOptions.Attributes
RoleType = roleType;
}
public static void Register(Assembly assembly)
{
foreach (var type in assembly.GetTypes())
{
foreach (PropertyInfo property in type.GetProperties())
{
foreach (var attribute in property.GetCustomAttributes())
{
if (attribute.GetType().BaseType == typeof(ModdedOptionAttribute))
{
ModdedOptionsManager.RegisterOption(assembly, type, (ModdedOptionAttribute)attribute, property);
}
}
}
}
}
public abstract void SetValue(object val);
public abstract object GetValue();
public abstract IModdedOption CreateOption(object value, PropertyInfo property);
+9 -4
View File
@@ -1,19 +1,24 @@
using System;
using MiraAPI.PluginLoading;
using System;
using MiraAPI.Networking;
using UnityEngine;
namespace MiraAPI.GameOptions
{
public interface IModdedOption
{
public uint Id { get; }
public BaseGameSetting Data { get; }
public ModdedOptionGroup Group { get; set; }
public IMiraPlugin ParentMod { get; set; }
public Type AdvancedRole { get; }
public OptionBehaviour OptionBehaviour { get; protected set; }
public string Title { get; }
public StringNames StringName { get; }
public Func<bool> Visible { get; set; }
public ModdedOptionGroup Group { get; set; }
public IMiraConfig ParentMod { get; set; }
public void ValueChanged(OptionBehaviour optionBehaviour);
public string GetHudStringText();
public OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container);
public NetData GetNetData();
public void HandleNetData(byte[] data);
}
}
+1 -1
View File
@@ -7,7 +7,7 @@ namespace MiraAPI.GameOptions
{
public string GroupName { get; }
public Func<bool> GroupVisible => () => true;
public Color GroupColor => Color.white;
public Color GroupColor => Color.clear;
public Type AdvancedRole => null;
}
}
+3 -2
View File
@@ -1,11 +1,12 @@
using System;
using MiraAPI.PluginLoading;
using System;
using UnityEngine;
namespace MiraAPI.GameOptions
{
public class ModdedOptionGroup
{
public IMiraConfig ParentMod;
public IMiraPlugin ParentMod;
public GameObject Header;
public string GroupName;
public Func<bool> GroupVisible;
+54 -55
View File
@@ -1,65 +1,27 @@
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using HarmonyLib;
using MiraAPI.GameOptions.Attributes;
using Reactor.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using MiraAPI.Networking;
using Reactor.Networking.Rpc;
namespace MiraAPI.GameOptions
{
public class ModdedOptionsManager
{
public static List<IModdedOption> Options = new();
public static List<ModdedOptionGroup> Groups = new();
private static Dictionary<PropertyInfo, ModdedOptionAttribute> OptionAttributes = new();
private static Dictionary<Type, ModdedOptionGroup> OriginalTypes = new();
public static Dictionary<Assembly, IMiraConfig> RegisteredMods = new();
public static readonly Dictionary<uint, IModdedOption> ModdedOptions = new();
public static readonly List<IModdedOption> Options = [];
public static readonly List<ModdedOptionGroup> Groups = [];
private static readonly Dictionary<PropertyInfo, ModdedOptionAttribute> OptionAttributes = new();
public static readonly Dictionary<Type, ModdedOptionGroup> OriginalTypes = new();
public static uint NextId = 1;
public static void Initialize()
public static IModdedOption RegisterOption(Type type, ModdedOptionAttribute attribute, PropertyInfo property)
{
IL2CPPChainloader.Instance.PluginLoad += (_, assembly, plugin) =>
{
if (plugin.GetType().GetInterfaces().Contains(typeof(IMiraConfig)))
{
IMiraConfig config = (IMiraConfig)Activator.CreateInstance(plugin.GetType());
RegisteredMods.Add(assembly, config);
}
RegisterOptionGroups(assembly);
ModdedOptionAttribute.Register(assembly);
};
}
private static void RegisterOptionGroups(Assembly assembly)
{
foreach (var type in assembly.GetTypes())
{
if (typeof(IModdedOptionGroup).IsAssignableFrom(type))
{
IModdedOptionGroup group = (IModdedOptionGroup)Activator.CreateInstance(type);
ModdedOptionGroup newGroup = new ModdedOptionGroup()
{
AdvancedRole = group.AdvancedRole,
GroupColor = group.GroupColor,
GroupName = group.GroupName,
GroupVisible = group.GroupVisible
};
Groups.Add(newGroup);
OriginalTypes.Add(type, newGroup);
newGroup.ParentMod = RegisteredMods[assembly];
}
}
}
public static void RegisterOption(Assembly assembly, Type type, ModdedOptionAttribute attribute, PropertyInfo property)
{
if (OptionAttributes.ContainsKey(property)) return;
if (OptionAttributes.ContainsKey(property)) return null;
object newObj = Activator.CreateInstance(type);
IModdedOption result = attribute.CreateOption(property.GetValue(newObj), property);
@@ -67,26 +29,63 @@ namespace MiraAPI.GameOptions
{
var setterOriginal = property.GetSetMethod();
var setterPatch = typeof(ModdedOptionsManager).GetMethod(nameof(PropertySetterPatch));
PluginSingleton<MiraAPIPlugin>.Instance.Harmony.Patch(setterOriginal, postfix: new HarmonyMethod(setterPatch));
PluginSingleton<MiraApiPlugin>.Instance.Harmony.Patch(setterOriginal, postfix: new HarmonyMethod(setterPatch));
var getterOriginal = property.GetGetMethod();
var getterPatch = typeof(ModdedOptionsManager).GetMethod(nameof(PropertyGetterPatch));
PluginSingleton<MiraAPIPlugin>.Instance.Harmony.Patch(getterOriginal, prefix: new HarmonyMethod(getterPatch));
PluginSingleton<MiraApiPlugin>.Instance.Harmony.Patch(getterOriginal, prefix: new HarmonyMethod(getterPatch));
attribute.HolderOption = result;
result.ParentMod = RegisteredMods[assembly];
Options.Add(result);
OptionAttributes.Add(property, attribute);
if (OriginalTypes.ContainsKey(type))
{
Debug.LogError($"grouping {attribute.Title} with {OriginalTypes[type].GroupName}");
Logger<MiraApiPlugin>.Error($"Grouping {attribute.Title} with {OriginalTypes[type].GroupName}");
result.Group = OriginalTypes[type];
}
}
return result;
}
public static void SyncAllOptions(int targetId=-1)
{
List<NetData> data = [];
int count = 0;
foreach (var option in ModdedOptions.Values)
{
var netData = option.GetNetData();
data.Add(netData);
count += netData.GetLength();
if (count > 1000)
{
Rpc<SyncOptionsRpc>.Instance.SendTo(PlayerControl.LocalPlayer, targetId, data.ToArray());
data.Clear();
count = 0;
}
}
if (data.Count > 0)
{
Rpc<SyncOptionsRpc>.Instance.SendTo(PlayerControl.LocalPlayer, targetId, data.ToArray());
}
}
public static void HandleSyncOptions(NetData[] data)
{
foreach (var netData in data)
{
if (ModdedOptions.TryGetValue(netData.Id, out var option))
{
Logger<MiraApiPlugin>.Error("Handling option " + option.Title);
option.HandleNetData(netData.Data);
}
}
}
public static void PropertySetterPatch(MethodBase __originalMethod, object value)
{
ModdedOptionAttribute attribute = OptionAttributes.First(pair => pair.Key.GetSetMethod().Equals(__originalMethod)).Value;
@@ -1,34 +1,54 @@
using Reactor.Localization.Utilities;
using System;
using System.Linq;
using MiraAPI.Networking;
using UnityEngine;
using Object = UnityEngine.Object;
namespace MiraAPI.GameOptions.OptionTypes
{
public class ModdedEnumOption : ModdedOption<int>
{
public string[] Values;
public ModdedEnumOption(string title, int defaultValue, Type enumType, System.Type roleType) : base(title, defaultValue, roleType)
public ModdedEnumOption(string title, int defaultValue, Type enumType, Type roleType) : base(title, defaultValue, roleType)
{
Values = Enum.GetNames(enumType);
Data = ScriptableObject.CreateInstance<StringGameSetting>();
var data = (StringGameSetting)Data;
data.Title = StringName;
data.Type = global::OptionTypes.String;
data.Values = Values.Select(CustomStringName.CreateAndRegister).ToArray();
data.Index = Value;
}
public string[] Values { get; set; }
public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container)
{
var stringOption = UnityEngine.Object.Instantiate(stringOpt, container);
var stringOption = Object.Instantiate(stringOpt, container);
stringOption.name = Title;
stringOption.Title = StringName;
stringOption.Value = Value;
stringOption.Values = Values.Select(CustomStringName.CreateAndRegister).ToArray();
stringOption.SetUpFromData(Data, 20);
stringOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged;
stringOption.OnEnable();
// SetUpFromData method doesnt work correctly so we must set the values manually
stringOption.Title = StringName;
stringOption.Values = ((StringGameSetting)Data).Values;
stringOption.Value = Value;
OptionBehaviour = stringOption;
return stringOption;
}
public override NetData GetNetData()
{
return new NetData(Id, BitConverter.GetBytes(Value));
}
public override void HandleNetData(byte[] data)
{
SetValue(BitConverter.ToInt32(data));
}
public override string GetHudStringText()
{
@@ -42,10 +62,14 @@ namespace MiraAPI.GameOptions.OptionTypes
public override void OnValueChanged(int newValue)
{
DestroyableSingleton<HudManager>.Instance.Notifier.AddSettingsChangeMessage(StringName, Data.GetValueString(newValue), false);
if (OptionBehaviour is null) return;
StringOption opt = OptionBehaviour as StringOption;
opt.Value = newValue;
var opt = OptionBehaviour as StringOption;
if (opt)
{
opt.Value = newValue;
}
}
}
}
@@ -1,5 +1,9 @@
using MiraAPI.Utilities;
using System;
using AmongUs.GameOptions;
using MiraAPI.Networking;
using MiraAPI.Utilities;
using UnityEngine;
using Object = UnityEngine.Object;
namespace MiraAPI.GameOptions.OptionTypes
{
@@ -19,28 +23,53 @@ namespace MiraAPI.GameOptions.OptionTypes
ZeroInfinity = zeroInfinity;
Value = Mathf.Clamp(defaultValue, min, max);
Data = ScriptableObject.CreateInstance<FloatGameSetting>();
var data = (FloatGameSetting)Data;
data.Type = global::OptionTypes.Float;
data.Title = StringName;
data.Value = Value;
data.Increment = Increment;
data.ValidRange = new FloatRange(Min, Max);
data.FormatString = "0";
data.ZeroIsInfinity = ZeroInfinity;
data.SuffixType = SuffixType;
data.OptionName = FloatOptionNames.Invalid;
}
public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container)
{
var numberOption = Object.Instantiate(numberOpt, container);
var numberOption = Object.Instantiate(numberOpt, Vector3.zero, Quaternion.identity, container);
numberOption.name = Title;
numberOption.SetUpFromData(Data, 20);
numberOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged;
numberOption.Title = StringName;
numberOption.Value = Value;
numberOption.Increment = Increment;
numberOption.SuffixType = SuffixType;
numberOption.FormatString = "0";
numberOption.ValidRange = new FloatRange(Min, Max);
numberOption.FormatString = "0";
numberOption.ZeroIsInfinity = ZeroInfinity;
numberOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged;
numberOption.OnEnable();
numberOption.SuffixType = SuffixType;
numberOption.floatOptionName = FloatOptionNames.Invalid;
OptionBehaviour = numberOption;
return numberOption;
}
public override NetData GetNetData()
{
return new NetData(Id, BitConverter.GetBytes(Value));
}
public override void HandleNetData(byte[] data)
{
SetValue(BitConverter.ToSingle(data));
}
public override string GetHudStringText()
{
return Title + ": " + Value + Helpers.GetSuffix(SuffixType);
@@ -54,11 +83,15 @@ namespace MiraAPI.GameOptions.OptionTypes
public override void OnValueChanged(float newValue)
{
Value = Mathf.Clamp(newValue, Min, Max);
DestroyableSingleton<HudManager>.Instance.Notifier.AddSettingsChangeMessage(StringName, Data.GetValueString(Value), false);
if (OptionBehaviour is null) return;
NumberOption opt = OptionBehaviour as NumberOption;
opt.Value = newValue;
var opt = OptionBehaviour as NumberOption;
if (opt)
{
opt.Value = newValue;
}
}
}
}
@@ -1,24 +1,30 @@
using MiraAPI.Roles;
using MiraAPI.PluginLoading;
using MiraAPI.Roles;
using Reactor.Localization.Utilities;
using System;
using MiraAPI.Networking;
using Reactor.Networking.Rpc;
using Reactor.Utilities;
using UnityEngine;
namespace MiraAPI.GameOptions.OptionTypes
{
public abstract class ModdedOption<T> : IModdedOption
{
public T Value { get; set; }
public T DefaultValue { get; set; }
public Action<T> ChangedEvent { get; private set; }
public uint Id { get; }
public BaseGameSetting Data { get; protected init; }
public T Value { get; protected set; }
public T DefaultValue { get; init; }
public Action<T> ChangedEvent { get; set; }
public string Title { get; }
public StringNames StringName { get; }
public Func<bool> Visible { get; set; }
public Type AdvancedRole { get; }
public OptionBehaviour OptionBehaviour { get; set; }
public ModdedOptionGroup Group { get; set; } = null;
public IMiraConfig ParentMod { get; set; } = null;
public IMiraPlugin ParentMod { get; set; } = null;
public ModdedOption(string title, T defaultValue, Type roleType)
public ModdedOption(string title, T defaultValue, Type roleType) : this()
{
Title = title;
DefaultValue = defaultValue;
@@ -31,6 +37,14 @@ namespace MiraAPI.GameOptions.OptionTypes
AdvancedRole = roleType;
}
}
public ModdedOption()
{
Logger<MiraApiPlugin>.Error("created modded option");
Id = ModdedOptionsManager.NextId++;
ModdedOptionsManager.Options.Add(this);
ModdedOptionsManager.ModdedOptions.Add(Id, this);
}
public void ValueChanged(OptionBehaviour optionBehaviour)
{
@@ -39,16 +53,25 @@ namespace MiraAPI.GameOptions.OptionTypes
public void SetValue(T newValue)
{
T oldVal = Value;
var oldVal = Value;
Value = newValue;
if (!Value.Equals(oldVal) && ChangedEvent != null)
{
ChangedEvent.Invoke(Value);
}
if (AmongUsClient.Instance.AmHost)
{
Rpc<SyncOptionsRpc>.Instance.Send(PlayerControl.LocalPlayer, [GetNetData()], true);
}
OnValueChanged(newValue);
}
public abstract NetData GetNetData();
public abstract void HandleNetData(byte[] data);
public abstract void OnValueChanged(T newValue);
public abstract T GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour);
@@ -1,33 +1,55 @@
using Reactor.Localization.Utilities;
using System;
using System.Linq;
using MiraAPI.Networking;
using UnityEngine;
using Object = UnityEngine.Object;
namespace MiraAPI.GameOptions.OptionTypes
{
public class ModdedStringOption : ModdedOption<int>
{
public string[] Values { get; private set; }
public ModdedStringOption(string title, int defaultValue, string[] values, System.Type roleType) : base(title, defaultValue, roleType)
public ModdedStringOption(string title, int defaultValue, string[] values, Type roleType) : base(title, defaultValue, roleType)
{
Values = values;
Data = ScriptableObject.CreateInstance<StringGameSetting>();
var data = (StringGameSetting)Data;
data.Title = StringName;
data.Type = global::OptionTypes.String;
data.Values = Values.Select(CustomStringName.CreateAndRegister).ToArray();
data.Index = Value;
}
public string[] Values { get; set; }
public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container)
{
var stringOption = Object.Instantiate(stringOpt, container);
stringOption.name = Title;
stringOption.Title = StringName;
stringOption.Value = Value;
stringOption.Values = Values.Select(CustomStringName.CreateAndRegister).ToArray();
stringOption.SetUpFromData(Data, 20);
stringOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged;
stringOption.OnEnable();
// SetUpFromData method doesnt work correctly so we must set the values manually
stringOption.Title = StringName;
stringOption.Values = ((StringGameSetting)Data).Values;
stringOption.Value = Value;
OptionBehaviour = stringOption;
return stringOption;
}
public override NetData GetNetData()
{
return new NetData(Id, BitConverter.GetBytes(Value));
}
public override void HandleNetData(byte[] data)
{
SetValue(BitConverter.ToInt32(data));
}
public override string GetHudStringText()
{
return Title + ": " + Values[Value];
@@ -40,10 +62,14 @@ namespace MiraAPI.GameOptions.OptionTypes
public override void OnValueChanged(int newValue)
{
DestroyableSingleton<HudManager>.Instance.Notifier.AddSettingsChangeMessage(StringName, Data.GetValueString(newValue), false);
if (OptionBehaviour is null) return;
StringOption opt = OptionBehaviour as StringOption;
opt.Value = newValue;
var opt = OptionBehaviour as StringOption;
if (opt)
{
opt.Value = newValue;
}
}
}
}
@@ -1,4 +1,7 @@
using UnityEngine;
using System;
using MiraAPI.Networking;
using UnityEngine;
using Object = UnityEngine.Object;
namespace MiraAPI.GameOptions.OptionTypes
{
@@ -6,21 +9,39 @@ namespace MiraAPI.GameOptions.OptionTypes
{
public ModdedToggleOption(string title, bool defaultValue, System.Type roleType) : base(title, defaultValue, roleType)
{
Data = ScriptableObject.CreateInstance<CheckboxGameSetting>();
var data = (CheckboxGameSetting)Data;
data.Title = StringName;
data.Type = global::OptionTypes.Checkbox;
}
public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container)
{
var toggleOption = UnityEngine.Object.Instantiate(toggleOpt, container);
var toggleOption = Object.Instantiate(toggleOpt, Vector3.zero, Quaternion.identity, container);
toggleOption.SetUpFromData(Data, 20);
toggleOption.name = Title;
toggleOption.Title = StringName;
toggleOption.TitleText.text = Title;
toggleOption.CheckMark.enabled = Value;
toggleOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged;
toggleOption.OnEnable();
OptionBehaviour = toggleOption;
return toggleOption;
}
public override NetData GetNetData()
{
return new NetData(Id, BitConverter.GetBytes(Value));
}
public override void HandleNetData(byte[] data)
{
SetValue(BitConverter.ToBoolean(data));
}
public override string GetHudStringText()
{
return Title + ": " + (Value ? "On" : "Off");
@@ -33,10 +54,14 @@ namespace MiraAPI.GameOptions.OptionTypes
public override void OnValueChanged(bool newValue)
{
DestroyableSingleton<HudManager>.Instance.Notifier.AddSettingsChangeMessage(StringName, newValue ? "On" : "Off", false);
if (OptionBehaviour is null) return;
ToggleOption toggleOpt = OptionBehaviour as ToggleOption;
toggleOpt.CheckMark.enabled = newValue;
var toggleOpt = OptionBehaviour as ToggleOption;
if (toggleOpt)
{
toggleOpt.CheckMark.enabled = newValue;
}
}
}
}
-154
View File
@@ -1,154 +0,0 @@
using Reactor.Utilities.Extensions;
using System;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using Object = UnityEngine.Object;
namespace MiraAPI.API.GameOptions;
public static class ModdedOptionsTab
{
public static List<GameObject> CustomTabs = new List<GameObject>();
public static List<GameObject> CustomScreens = new List<GameObject>();
public static GameObject InitializeForMod(IMiraConfig config, GameSettingMenu __instance)
{
var gameBtn = __instance.transform.FindChild("Header/Tabs/GameTab").gameObject;
var roleBtn = __instance.transform.FindChild("Header/Tabs/RoleTab").gameObject;
var screen = CreateNewMenu(config, __instance);
var tab = CreateCustomTab(config, __instance, screen, gameBtn, roleBtn);
CustomScreens.Add(screen);
CustomTabs.Add(tab);
SpriteRenderer rend = tab.transform.FindChild("Btn/Tab Background").GetComponent<SpriteRenderer>();
rend.enabled = false;
UpdateListeners(__instance, gameBtn.GetComponentInChildren<PassiveButton>(), roleBtn.GetComponentInChildren<PassiveButton>(), rend);
var container = screen.transform.FindChild("GameGroup/SliderInner");
container.DestroyChildren();
//CreateNewResetButton(__instance, container);
return container.gameObject;
}
private static void UpdateListeners(GameSettingMenu __instance, PassiveButton gameB, PassiveButton roleB, SpriteRenderer rend)
{
gameB.OnClick.RemoveAllListeners();
gameB.OnClick.AddListener((UnityAction)(() =>
{
__instance.RegularGameSettings.SetActive(true);
__instance.RolesSettings.gameObject.SetActive(false);
foreach (var screen in CustomScreens) screen.gameObject.SetActive(false);
rend.enabled = false;
__instance.GameSettingsHightlight.enabled = true;
__instance.RolesSettingsHightlight.enabled = false;
}));
roleB.OnClick.RemoveAllListeners();
roleB.OnClick.AddListener((UnityAction)(() =>
{
__instance.RegularGameSettings.SetActive(false);
__instance.RolesSettings.gameObject.SetActive(true);
foreach (var screen in CustomScreens) screen.gameObject.SetActive(false);
rend.enabled = false;
__instance.GameSettingsHightlight.enabled = false;
__instance.RolesSettingsHightlight.enabled = true;
}));
}
public static GameObject CreateHeader(ToggleOption toggleOpt, Transform container, string title, Color color)
{
var header = Object.Instantiate(toggleOpt, container);
header.Title = StringNames.None;
header.TitleText.text = title;
header.TitleText.color = color;
header.name = "Header";
var checkBox = header.transform.FindChild("CheckBox")?.gameObject;
if (checkBox)
{
checkBox.Destroy();
}
var background = header.transform.FindChild("Background")?.gameObject;
if (background)
{
background.Destroy();
}
header.GetComponent<OptionBehaviour>().Destroy();
return header.gameObject;
}
public static GameObject CreateCustomTab(IMiraConfig options, GameSettingMenu __instance, GameObject newSettings,
GameObject gameTab, GameObject roleTab)
{
var newTab = Object.Instantiate(gameTab, gameTab.transform.parent);
newTab.name = "Tab";
newTab.transform.position = roleTab.transform.position;
newTab.transform.position += new Vector3(CustomTabs.Count + 1, 0, 0);
__instance.Tabs.transform.position -= new Vector3(0.5f, 0, 0);
var inside = newTab.transform.FindChild("ColorButton");
inside.name = "Btn";
var btn = inside.GetComponentInChildren<PassiveButton>();
btn.OnClick.RemoveAllListeners();
btn.OnClick.AddListener((Action)TabAction);
var spriteRend = inside.GetComponentInChildren<SpriteRenderer>();
// if (options.TabSettings.TabIcon != null) spriteRend.sprite = options.TabSettings.TabIcon.LoadAsset();
return newTab;
void TabAction()
{
__instance.RegularGameSettings.SetActive(false);
__instance.RolesSettings.gameObject.SetActive(false);
foreach (var screen in CustomScreens)
{
screen.gameObject.SetActive(false);
}
newSettings.gameObject.SetActive(true);
SpriteRenderer rend = newTab.transform.FindChild("Btn/Tab Background").GetComponent<SpriteRenderer>();
rend.enabled = true;
gameTab.transform.FindChild("ColorButton/Tab Background").gameObject.GetComponent<SpriteRenderer>().enabled = false;
roleTab.transform.FindChild("Hat Button/Tab Background").gameObject.GetComponent<SpriteRenderer>().enabled = false;
foreach (var tab in CustomTabs)
{
tab.transform.FindChild("Btn/Tab Background").gameObject.GetComponent<SpriteRenderer>().enabled = false;
}
}
}
public static GameObject CreateNewMenu(IMiraConfig config, GameSettingMenu __instance)
{
var gameSettings = __instance.RegularGameSettings;
var newSettings = Object.Instantiate(gameSettings, gameSettings.transform.parent);
newSettings.name = "Settings";
newSettings.SetActive(false);
var launchpadGroup = newSettings.transform.FindChild("GameGroup").gameObject;
var text = launchpadGroup.transform.FindChild("Text").gameObject.GetComponent<TextMeshPro>();
text.gameObject.GetComponent<TextTranslatorTMP>().Destroy();
text.text = $"{config.TabSettings.Title} Options";
return newSettings;
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets;
using UnityEngine;
using UnityEngine.Events;
-16
View File
@@ -1,16 +0,0 @@
using MiraAPI.Utilities;
using UnityEngine;
namespace MiraAPI
{
public interface IMiraConfig
{
public ModdedOptionTabSettings TabSettings { get; }
}
public struct ModdedOptionTabSettings(string title, LoadableAsset<Sprite> tabIcon)
{
public string Title { get; set; } = title;
public LoadableAsset<Sprite> TabIcon { get; set; } = tabIcon;
}
}
+17 -1
View File
@@ -14,12 +14,28 @@
<GamePlatform Condition="'$(GamePlatform)' == ''">Steam</GamePlatform>
<GameVersion Condition="'$(GamePlatform)' == 'Steam'">2024.3.5</GameVersion>
</PropertyGroup>
<ItemGroup>
<None Remove="Resources\Cog.png" />
<None Remove="Resources\CogActive.png" />
<None Remove="Resources\FortniteBanner.jpeg" />
<None Remove="Resources\NextButton.png" />
<None Remove="Resources\NextButtonActive.png" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\Cog.png" />
<EmbeddedResource Include="Resources\CogActive.png" />
<EmbeddedResource Include="Resources\FortniteBanner.jpeg" />
<EmbeddedResource Include="Resources\NextButton.png" />
<EmbeddedResource Include="Resources\NextButtonActive.png" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Reactor" Version="2.2.0" />
<PackageReference Include="BepInEx.Unity.IL2CPP" Version="6.0.0-be.688" Private="false" ExcludeAssets="runtime;native" />
<PackageReference Include="Iced" Version="1.20.0" />
<PackageReference Include="AmongUs.GameLibs.Steam" Version="2024.3.5" PrivateAssets="all" />
<PackageReference Include="AmongUs.GameLibs.Steam" Version="2024.6.18" PrivateAssets="all" />
<PackageReference Include="BepInEx.AutoPlugin" Version="1.1.0" PrivateAssets="all" />
<PackageReference Include="BepInEx.IL2CPP.MSBuild" Version="2.1.0-rc.1" PrivateAssets="all" ExcludeAssets="runtime">
-35
View File
@@ -1,35 +0,0 @@
using BepInEx;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using MiraAPI.GameModes;
using MiraAPI.GameOptions;
using MiraAPI.Hud;
using MiraAPI.Roles;
using Reactor;
using Reactor.Networking;
using Reactor.Networking.Attributes;
using System.Reflection;
namespace MiraAPI;
[BepInAutoPlugin("mira.api", "MiraAPI")]
[BepInProcess("Among Us.exe")]
[BepInDependency(ReactorPlugin.Id)]
[ReactorModFlags(ModFlags.RequireOnAllClients)]
public partial class MiraAPIPlugin : BasePlugin
{
public Harmony Harmony { get; } = new(Id);
public static MiraAPIPlugin Instance { get; private set; }
public override void Load()
{
Instance = this;
Harmony.PatchAll();
RegisterCustomRoleAttribute.Initialize();
ModdedOptionsManager.Initialize();
RegisterGameModeAttribute.Register(Assembly.GetCallingAssembly());
RegisterButtonAttribute.Register(Assembly.GetExecutingAssembly());
}
}
+48
View File
@@ -0,0 +1,48 @@
using BepInEx;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using MiraAPI.PluginLoading;
using Reactor;
using Reactor.Patches;
using System;
using System.Text;
using Reactor.Networking;
using Reactor.Networking.Attributes;
using UnityEngine;
namespace MiraAPI;
[BepInAutoPlugin("mira.api", "MiraAPI")]
[BepInProcess("Among Us.exe")]
[BepInDependency(ReactorPlugin.Id)]
[ReactorModFlags(ModFlags.RequireOnAllClients)]
public partial class MiraApiPlugin : BasePlugin
{
public Harmony Harmony { get; } = new(Id);
public static MiraPluginManager PluginManager { get; private set; }
public static Color MiraColor = new Color32(238, 154, 112, 255);
public override void Load()
{
Harmony.PatchAll();
PluginManager = new MiraPluginManager();
PluginManager.Initialize();
ReactorVersionShower.TextUpdated += text =>
{
text.text = new StringBuilder($"{MiraColor.ToTextColor()}Mira API</color> ")
.Append(GetShortHashVersion(Version))
.Append('\n')
.Append(text.text)
.ToString();
};
}
private static string GetShortHashVersion(string version)
{
var index = version.IndexOf("+", StringComparison.Ordinal);
return index < 0 ? version : version[..(index + 3)];
}
}
+10
View File
@@ -0,0 +1,10 @@
using System;
namespace MiraAPI.Networking;
public readonly struct NetData(uint id, byte[] data)
{
public uint Id { get; } = id;
public byte[] Data { get; } = data;
public int GetLength() => BitConverter.GetBytes(Id).Length + Data.Length;
}
+19 -53
View File
@@ -1,81 +1,47 @@
using Hazel;
using MiraAPI.GameOptions;
using Reactor.Networking.Attributes;
using Reactor.Networking.Rpc;
namespace MiraAPI.Networking.Options;
namespace MiraAPI.Networking;
// METHOD RPC DOESNT WORK WITH THE ARRAYS AND STUFF SO THIS IS HOW WE WILL DO IT FOR NOW
[RegisterCustomRpc((uint)MiraRpc.SyncGameOptions)]
public class SyncOptionsRpc(MiraAPIPlugin plugin, uint id) : PlayerCustomRpc<MiraAPIPlugin, SyncOptionsRpc.Data>(plugin, id)
public class SyncOptionsRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc<MiraApiPlugin, NetData[]>(plugin, id)
{
public override RpcLocalHandling LocalHandling => RpcLocalHandling.None;
public readonly struct Data
public override void Write(MessageWriter writer, NetData[] data)
{
public readonly bool[] Toggles;
public readonly float[] Numbers;
public readonly int[] StringIDs;
public Data(bool[] toggles, float[] numbers, int[] stringIDs)
writer.WritePacked((uint)data.Length);
foreach (var netData in data)
{
Toggles = toggles;
Numbers = numbers;
StringIDs = stringIDs;
writer.WritePacked(netData.Id);
writer.WriteBytesAndSize(netData.Data);
}
}
public override void Write(MessageWriter writer, Data data)
public override NetData[] Read(MessageReader reader)
{
writer.WritePacked((uint)data.Toggles.Length);
foreach (var t in data.Toggles)
var length = reader.ReadPackedUInt32();
var data = new NetData[length];
for (var i = 0; i < length; i++)
{
writer.Write(t);
var id = reader.ReadPackedUInt32();
var bytes = reader.ReadBytesAndSize();
data[i] = new NetData(id, bytes);
}
writer.WritePacked((uint)data.Numbers.Length);
foreach (var n in data.Numbers)
{
writer.Write(n);
}
writer.WritePacked((uint)data.StringIDs.Length);
foreach (var n in data.StringIDs)
{
writer.WritePacked(n);
}
return data;
}
public override Data Read(MessageReader reader)
{
var toggles = new bool[reader.ReadPackedUInt32()];
for (var i = 0; i < toggles.Length; i++)
{
toggles[i] = reader.ReadBoolean();
}
var numbers = new float[reader.ReadPackedUInt32()];
for (var i = 0; i < numbers.Length; i++)
{
numbers[i] = reader.ReadSingle();
}
var strings = new int[reader.ReadPackedUInt32()];
for (var i = 0; i < strings.Length; i++)
{
strings[i] = reader.ReadPackedInt32();
}
return new Data(toggles, numbers, strings);
}
public override void Handle(PlayerControl playerControl, Data data)
public override void Handle(PlayerControl playerControl, NetData[] data)
{
if (AmongUsClient.Instance.HostId != playerControl.OwnerId)
{
return;
}
//CustomOptionsManager.HandleOptionsSync(data.Toggles, data.Numbers, data.StringIDs);
ModdedOptionsManager.HandleSyncOptions(data);
}
}
+24 -33
View File
@@ -1,58 +1,49 @@
using Hazel;
using System;
using Hazel;
using MiraAPI.Roles;
using Reactor.Networking.Attributes;
using Reactor.Networking.Rpc;
using Reactor.Utilities;
using System;
namespace MiraAPI.Networking.Options;
namespace MiraAPI.Networking;
[RegisterCustomRpc((uint)MiraRpc.SyncRoleOptions)]
public class SyncRoleOptionsRpc(MiraAPIPlugin plugin, uint id) : PlayerCustomRpc<MiraAPIPlugin, SyncRoleOptionsRpc.Data>(plugin, id)
public class SyncRoleOptionsRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc<MiraApiPlugin, NetData[]>(plugin, id)
{
public struct Data(ushort roleId, int number, int chance)
{
public readonly ushort RoleId = roleId;
public readonly int Number = number;
public readonly int Chance = chance;
}
public override RpcLocalHandling LocalHandling => RpcLocalHandling.None;
public override void Write(MessageWriter writer, Data data)
public override void Write(MessageWriter writer, NetData[] data)
{
writer.Write(data.RoleId);
writer.WritePacked(data.Number);
writer.WritePacked(data.Chance);
writer.WritePacked((uint)data.Length);
foreach (var netData in data)
{
writer.WritePacked(netData.Id);
writer.WriteBytesAndSize(netData.Data);
}
}
public override Data Read(MessageReader reader)
public override NetData[] Read(MessageReader reader)
{
return new Data(reader.ReadUInt16(), reader.ReadPackedInt32(), reader.ReadPackedInt32());
var length = reader.ReadPackedUInt32();
var data = new NetData[length];
for (var i = 0; i < length; i++)
{
var id = reader.ReadPackedUInt32();
var bytes = reader.ReadBytesAndSize();
data[i] = new NetData(id, bytes);
}
return data;
}
public override void Handle(PlayerControl playerControl, Data data)
public override void Handle(PlayerControl playerControl, NetData[] data)
{
if (AmongUsClient.Instance.HostId != playerControl.OwnerId)
{
return;
}
if (!CustomRoleManager.CustomRoles.TryGetValue(data.RoleId, out var roleBehaviour) ||
!CustomRoleManager.GetCustomRoleBehaviour(roleBehaviour.Role, out var role)) return;
PluginSingleton<MiraAPIPlugin>.Instance.Config.TryGetEntry<int>(role.NumConfigDefinition, out var numEntry);
PluginSingleton<MiraAPIPlugin>.Instance.Config.TryGetEntry<int>(role.ChanceConfigDefinition, out var chanceEntry);
try
{
numEntry.Value = data.Number;
chanceEntry.Value = data.Chance;
}
catch (Exception e)
{
Logger<MiraAPIPlugin>.Warning(e.ToString());
}
CustomRoleManager.HandleSyncRoleOptions(data);
}
}
+26
View File
@@ -0,0 +1,26 @@
using HarmonyLib;
using InnerNet;
using MiraAPI.GameOptions;
using MiraAPI.Roles;
namespace MiraAPI.Patches;
[HarmonyPatch(typeof(AmongUsClient), nameof(AmongUsClient.CreatePlayer))]
public static class AmongUsClientSyncPatch
{
public static void Postfix(ClientData clientData)
{
if (!AmongUsClient.Instance.AmHost)
{
return;
}
if (clientData.Id == AmongUsClient.Instance.HostId)
{
return;
}
ModdedOptionsManager.SyncAllOptions(clientData.Id);
CustomRoleManager.SyncAllRoleSettings(clientData.Id);
}
}
+9 -63
View File
@@ -1,8 +1,6 @@
using HarmonyLib;
using InnerNet;
using MiraAPI.GameOptions;
using MiraAPI.Hud;
using MiraAPI.Patches.Options;
using MiraAPI.Roles;
using Reactor.Utilities.Extensions;
using UnityEngine;
@@ -18,66 +16,11 @@ namespace MiraAPI.Patches
/// Custom role tab
private static TaskPanelBehaviour _roleTab;
/// Scrolling increment
private const float Increment = 0.3f;
/// Bounds for scrolling
private static FloatRange _bounds = new FloatRange(2.9f, 4.6f);
/// <summary>
/// Add options scrolling (on the hud text)
/// </summary>
public static void OptionsScrollingLogic(HudManager __instance)
{
if (Input.GetKeyDown(KeyCode.Tab))
{
if (ToHudStringPatch.CurrentPage >= ModdedOptionsManager.RegisteredMods.Count)
{
ToHudStringPatch.CurrentPage = 0;
}
else
{
ToHudStringPatch.CurrentPage += 1;
}
}
var numPlayers = GameData.Instance ? GameData.Instance.PlayerCount : 10;
HudManager.Instance.GameSettings.text = GameOptionsManager.Instance.CurrentGameOptions.ToHudString(numPlayers);
var pos = __instance.GameSettings.transform.localPosition;
if (!PlayerControl.LocalPlayer.CanMove)
{
return;
}
if (ToHudStringPatch.CurrentPage == 0)
{
__instance.GameSettings.transform.localPosition = new Vector3(pos.x, _bounds.min, pos.z);
return;
}
_bounds.max = __instance.GameSettings.rectTransform.sizeDelta.y + _bounds.min;
var delta = Input.mouseScrollDelta.y switch
{
> 0f => -Increment,
< 0f => Increment,
_ => 0f
};
pos = new Vector3(pos.x, Mathf.Clamp(pos.y + delta, _bounds.min, _bounds.max), pos.z);
__instance.GameSettings.transform.localPosition = pos;
}
[HarmonyPostfix, HarmonyPatch("Update")]
[HarmonyPostfix]
[HarmonyPatch(nameof(HudManager.Update))]
public static void UpdatePostfix(HudManager __instance)
{
var local = PlayerControl.LocalPlayer;
if (LobbyBehaviour.Instance)
{
OptionsScrollingLogic(__instance);
}
if (AmongUsClient.Instance.GameState != InnerNetClient.GameStates.Started && !ShipStatus.Instance)
{
@@ -85,7 +28,7 @@ namespace MiraAPI.Patches
}
//CustomGameModeManager.ActiveMode.HudUpdate(__instance);
if (local.Data.Role is null) return;
if (local is null || local.Data is null || local.Data.Role is null) return;
if (local.Data.Role is ICustomRole customRole)
{
@@ -116,7 +59,8 @@ namespace MiraAPI.Patches
/* /// <summary>
/// Trigger hudstart on current custom gamemode
/// </summary>
[HarmonyPostfix, HarmonyPatch("OnGameStart")]
[HarmonyPostfix]
[HarmonyPatch("OnGameStart")]
public static void GameStartPatch(HudManager __instance)
{
CustomGameModeManager.ActiveMode.HudStart(__instance);
@@ -125,7 +69,8 @@ namespace MiraAPI.Patches
/// <summary>
/// Create custom buttons parent
/// </summary>
[HarmonyPostfix, HarmonyPatch("Start")]
[HarmonyPostfix]
[HarmonyPatch(nameof(HudManager.Start))]
public static void StartPostfix(HudManager __instance)
{
if (!_bottomLeft)
@@ -160,7 +105,8 @@ namespace MiraAPI.Patches
/// <summary>
/// Make sure all launchpad hud elements are inactive/active when appropriate
/// </summary>
[HarmonyPostfix, HarmonyPatch("SetHudActive", typeof(PlayerControl), typeof(RoleBehaviour), typeof(bool))]
[HarmonyPostfix]
[HarmonyPatch(nameof(HudManager.SetHudActive), typeof(PlayerControl), typeof(RoleBehaviour), typeof(bool))]
public static void SetHudActivePostfix(HudManager __instance, [HarmonyArgument(0)] PlayerControl player, [HarmonyArgument(1)] RoleBehaviour roleBehaviour, [HarmonyArgument(2)] bool isActive)
{
if (player.Data == null)
+102 -80
View File
@@ -1,109 +1,131 @@
using HarmonyLib;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using MiraAPI.GameOptions;
using MiraAPI.Utilities;
using Reactor.Localization.Utilities;
using System.Linq;
using TMPro;
using UnityEngine;
using Object = UnityEngine.Object;
namespace MiraAPI.Patches.Options;
[HarmonyPatch(typeof(GameOptionsMenu))]
public static class GameOptionsMenuPatch
{
/// <summary>
/// Change the OnValueChanged action for all custom game options
/// </summary>
[HarmonyPostfix, HarmonyPatch("Start")]
public static void StartPostfix(GameOptionsMenu __instance)
[HarmonyPrefix]
[HarmonyPatch(nameof(GameOptionsMenu.CreateSettings))]
public static bool SettingsPatch(GameOptionsMenu __instance)
{
foreach (var customOption in ModdedOptionsManager.Options)
if (GameSettingMenuPatches.currentSelectedMod == 0) return true;
__instance.MapPicker.gameObject.SetActive(false);
float num = 2.1f;
foreach (IModdedOptionGroup group in GameSettingMenuPatches.selectedMod.OptionGroups)
{
if (customOption.AdvancedRole is not null || !customOption.OptionBehaviour)
CategoryHeaderMasked categoryHeaderMasked = UnityEngine.Object.Instantiate(__instance.categoryHeaderOrigin, Vector3.zero, Quaternion.identity, __instance.settingsContainer);
categoryHeaderMasked.SetHeader(CustomStringName.CreateAndRegister(group.GroupName), 20);
if (group.GroupColor != Color.clear)
{
continue;
categoryHeaderMasked.Background.color = group.GroupColor;
categoryHeaderMasked.Title.color = group.GroupColor.DarkenColor();
}
categoryHeaderMasked.transform.localScale = Vector3.one * 0.63f;
categoryHeaderMasked.transform.localPosition = new Vector3(-0.903f, num, -2f);
num -= 0.63f;
foreach (var opt in ModdedOptionsManager.Options.Where(opt => opt.Group is not null && opt.AdvancedRole is null && opt.Group.GroupName == group.GroupName))
{
OptionBehaviour newOpt = opt.CreateOption(__instance.checkboxOrigin, __instance.numberOptionOrigin, __instance.stringOptionOrigin, __instance.settingsContainer);
newOpt.transform.localPosition = new Vector3(0.952f, num, -2f);
newOpt.SetClickMask(__instance.ButtonClickMask);
SpriteRenderer[] componentsInChildren = newOpt.GetComponentsInChildren<SpriteRenderer>(true);
for (int i = 0; i < componentsInChildren.Length; i++)
{
if (group.GroupColor != Color.clear) componentsInChildren[i].color = group.GroupColor;
componentsInChildren[i].material.SetInt(PlayerMaterial.MaskLayer, 20);
}
foreach (TextMeshPro textMeshPro in newOpt.GetComponentsInChildren<TextMeshPro>(true))
{
if (group.GroupColor != Color.clear) textMeshPro.color = group.GroupColor.DarkenColor();
textMeshPro.fontMaterial.SetFloat("_StencilComp", 3f);
textMeshPro.fontMaterial.SetFloat("_Stencil", 20);
}
__instance.Children.Add(newOpt);
num -= 0.45f;
newOpt.Initialize();
}
}
CategoryHeaderMasked ungroupedHeader = UnityEngine.Object.Instantiate(__instance.categoryHeaderOrigin, Vector3.zero, Quaternion.identity, __instance.settingsContainer);
ungroupedHeader.SetHeader(CustomStringName.CreateAndRegister("Ungrouped Options"), 20);
ungroupedHeader.transform.localScale = Vector3.one * 0.63f;
ungroupedHeader.transform.localPosition = new Vector3(-0.903f, num, -2f);
num -= 0.63f;
foreach (var opt in ModdedOptionsManager.Options.Where(opt => opt.Group is null && opt.AdvancedRole is null))
{
OptionBehaviour newOpt = opt.CreateOption(__instance.checkboxOrigin, __instance.numberOptionOrigin, __instance.stringOptionOrigin, __instance.settingsContainer);
newOpt.transform.localPosition = new Vector3(0.952f, num, -2f);
newOpt.SetClickMask(__instance.ButtonClickMask);
newOpt.SetUpFromData(newOpt.data, 20);
SpriteRenderer[] componentsInChildren = newOpt.GetComponentsInChildren<SpriteRenderer>(true);
for (int i = 0; i < componentsInChildren.Length; i++)
{
componentsInChildren[i].material.SetInt(PlayerMaterial.MaskLayer, 20);
}
foreach (TextMeshPro textMeshPro in newOpt.GetComponentsInChildren<TextMeshPro>(true))
{
textMeshPro.fontMaterial.SetFloat("_StencilComp", 3f);
textMeshPro.fontMaterial.SetFloat("_Stencil", 20);
}
customOption.OptionBehaviour.OnValueChanged = (Action<OptionBehaviour>)customOption.ValueChanged;
__instance.Children.Add(newOpt);
num -= 0.45f;
newOpt.Initialize();
}
__instance.scrollBar.SetYBoundsMax(-num - 1.65f);
return false;
}
private static GameSettingMenu menu;
/// <summary>
/// Set the position and offset of all custom game options
/// </summary>
[HarmonyPostfix, HarmonyPatch("Update")]
public static void UpdatePostfix(GameOptionsMenu __instance)
[HarmonyPrefix]
[HarmonyPatch(nameof(GameOptionsMenu.Initialize))]
public static bool InitPatch(GameOptionsMenu __instance)
{
if (!menu)
if (__instance.Children == null || __instance.Children.Count == 0)
{
menu = Object.FindObjectsOfType<GameSettingMenu>().First();
}
if (menu.RegularGameSettings.active || menu.RolesSettings.gameObject.active || menu.HideNSeekSettings.active)
{
return;
}
var startOffset = 2.5f;
__instance.GetComponentInParent<Scroller>().ContentYBounds.max = startOffset + __instance.Children.Count * 0.5f;
foreach (var option in ModdedOptionsManager.Options.Where(option => option.Group == null))
{
if (!option.OptionBehaviour) continue;
option.OptionBehaviour.enabled = option.Visible();
option.OptionBehaviour.gameObject.SetActive(option.Visible());
if (option.Visible())
__instance.MapPicker.gameObject.SetActive(true);
__instance.MapPicker.Initialize(20);
BaseGameSetting mapNameSetting = GameManager.Instance.GameSettingsList.MapNameSetting;
__instance.MapPicker.SetUpFromData(mapNameSetting, 20);
__instance.Children = new List<OptionBehaviour>();
__instance.Children.Add(__instance.MapPicker);
__instance.CreateSettings();
__instance.cachedData = GameOptionsManager.Instance.CurrentGameOptions;
foreach (var optionBehaviour in __instance.Children)
{
startOffset -= 0.55f;
}
var transform = option.OptionBehaviour.transform;
var optionPosition = transform.localPosition;
transform.localPosition = new Vector3(optionPosition.x, startOffset, optionPosition.z);
}
foreach (var group in ModdedOptionsManager.Groups.Where(group => group.AdvancedRole == null))
{
if (group.Header == null)
{
continue;
}
group.Header.SetActive(group.GroupVisible());
if (group.GroupVisible())
{
startOffset -= 0.5f;
}
var position = group.Header.transform.localPosition;
group.Header.transform.localPosition = new Vector3(position.x, startOffset, position.z);
foreach (var option in ModdedOptionsManager.Options.Where(x => x.Group == group))
{
if (!option.OptionBehaviour)
if (AmongUsClient.Instance && !AmongUsClient.Instance.AmHost)
{
continue;
optionBehaviour.SetAsPlayer();
}
var enabled = group.GroupVisible() && option.Visible();
if (optionBehaviour.IsCustom()) continue;
option.OptionBehaviour.enabled = enabled;
option.OptionBehaviour.gameObject.SetActive(enabled);
if (group.GroupVisible() && option.Visible())
{
startOffset -= 0.5f;
}
var transform = option.OptionBehaviour.transform;
var optionPosition = transform.localPosition;
transform.localPosition = new Vector3(optionPosition.x, startOffset, optionPosition.z);
optionBehaviour.OnValueChanged = new System.Action<OptionBehaviour>(__instance.ValueChanged);
}
}
return false;
}
}
@@ -0,0 +1,146 @@
using HarmonyLib;
using MiraAPI.PluginLoading;
using MiraAPI.Utilities.Assets;
using Reactor.Utilities.Extensions;
using System;
using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using static UnityEngine.UI.Button;
using Object = UnityEngine.Object;
namespace MiraAPI.Patches.Options;
[HarmonyPatch(typeof(GameSettingMenu))]
public static class GameSettingMenuPatches
{
public static int currentSelectedMod = 0;
public static MiraPluginInfo selectedMod = null;
public static TextMeshPro text;
[HarmonyPrefix]
[HarmonyPatch(nameof(GameSettingMenu.Start))]
public static void StartPrefix(GameSettingMenu __instance)
{
__instance.transform.FindChild("GameSettingsLabel").gameObject.SetActive(false);
Transform helpThing = __instance.transform.FindChild("What Is This?");
GameObject tmpText = Object.Instantiate(helpThing.transform.FindChild("InfoText"), helpThing.parent).gameObject;
tmpText.GetComponent<TextTranslatorTMP>().Destroy();
tmpText.name = "SelectedMod";
tmpText.transform.localPosition = new Vector3(-3.3382f, 1.5399f, -2);
text = tmpText.GetComponent<TextMeshPro>();
text.fontSizeMax = 3.2f;
UpdateText(__instance.GameSettingsTab, __instance.RoleSettingsTab);
text.alignment = TextAlignmentOptions.Center;
GameObject nextButton = Object.Instantiate(__instance.BackButton, __instance.BackButton.transform.parent).gameObject;
nextButton.transform.localPosition = new Vector3(-2.2663f, 1.5272f, -25f);
nextButton.name = "RightArrowButton";
nextButton.transform.FindChild("Inactive").gameObject.GetComponent<SpriteRenderer>().sprite = MiraAssets.NextButton.LoadAsset();
nextButton.transform.FindChild("Active").gameObject.GetComponent<SpriteRenderer>().sprite = MiraAssets.NextButtonActive.LoadAsset();
nextButton.gameObject.GetComponent<CloseButtonConsoleBehaviour>().DestroyImmediate();
PassiveButton passiveButton = nextButton.gameObject.GetComponent<PassiveButton>();
passiveButton.OnClick = new ButtonClickedEvent();
passiveButton.OnClick.AddListener((UnityAction)(() =>
{
if (currentSelectedMod != MiraPluginManager.Instance.RegisteredPlugins.Count)
{
currentSelectedMod += 1;
UpdateText(__instance.GameSettingsTab, __instance.RoleSettingsTab);
}
}));
GameObject backButton = Object.Instantiate(nextButton, __instance.BackButton.transform.parent).gameObject;
backButton.transform.localPosition = new Vector3(-4.4209f, 1.5272f, -25f);
backButton.name = "LeftArrowButton";
backButton.gameObject.GetComponent<CloseButtonConsoleBehaviour>().Destroy();
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)
{
currentSelectedMod -= 1;
UpdateText(__instance.GameSettingsTab, __instance.RoleSettingsTab);
}
}));
}
public static void UpdateText(GameOptionsMenu settings, RolesSettingsMenu roles)
{
if (currentSelectedMod == 0)
{
text.text = "Default";
text.fontSizeMax = 3.2f;
}
else
{
text.fontSizeMax = 2f;
selectedMod = MiraPluginManager.Instance.RegisteredPlugins.ElementAt(currentSelectedMod - 1).Value;
if (selectedMod == null)
{
currentSelectedMod = 0;
UpdateText(settings, roles);
}
string name = selectedMod.PluginInfo.Metadata.Name;
text.text = name.Substring(0, Math.Min(name.Length, 15));
}
if (roles is not null && roles.roleChances is not null)
{
if (roles.advancedSettingChildren is not null)
{
foreach (var child in roles.advancedSettingChildren)
{
child.gameObject.DestroyImmediate();
}
roles.advancedSettingChildren.Clear();
roles.advancedSettingChildren = null;
}
foreach (var header in roles.RoleChancesSettings.transform.GetComponentsInChildren<CategoryHeaderEditRole>())
{
header.gameObject.DestroyImmediate();
}
foreach (var roleChance in roles.roleChances)
{
roleChance.gameObject.DestroyImmediate();
}
roles.roleChances.Clear();
roles.roleChances = null;
roles.AdvancedRolesSettings.gameObject.SetActive(false);
roles.RoleChancesSettings.gameObject.SetActive(true);
roles.SetQuotaTab();
}
if (settings is not null && settings.Children is not null)
{
foreach (var child in settings.Children)
{
if (child.TryCast<GameOptionsMapPicker>()) continue;
if (child.gameObject is null) continue;
child.gameObject.DestroyImmediate();
}
foreach (var header in settings.settingsContainer.GetComponentsInChildren<CategoryHeaderMasked>())
{
header.gameObject.DestroyImmediate();
}
settings.Children.Clear();
settings.Children = null;
settings.Initialize();
}
}
}
@@ -1,68 +0,0 @@
using HarmonyLib;
using MiraAPI.API.GameOptions;
using MiraAPI.GameOptions;
using Reactor.Utilities;
using Reactor.Utilities.Extensions;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Object = UnityEngine.Object;
namespace MiraAPI.Patches.Options;
[HarmonyPatch(typeof(GameSettingMenu))]
public static class GameSettingsMenuPatches
{
private static GameObject _gameBtn;
private static GameObject _roleBtn;
[HarmonyPrefix, HarmonyPatch("Start")]
public static void StartPrefix(GameSettingMenu __instance)
{
foreach (var tab in ModdedOptionsTab.CustomTabs) tab.gameObject.Destroy();
foreach (var screen in ModdedOptionsTab.CustomScreens) screen.gameObject.Destroy();
ModdedOptionsTab.CustomScreens.Clear();
ModdedOptionsTab.CustomTabs.Clear();
_gameBtn = __instance.transform.FindChild("Header/Tabs/GameTab").gameObject;
_roleBtn = __instance.transform.FindChild("Header/Tabs/RoleTab").gameObject;
var numberOpt = __instance.RegularGameSettings.GetComponentInChildren<NumberOption>();
var toggleOpt = Object.FindObjectOfType<ToggleOption>();
var stringOpt = __instance.RegularGameSettings.GetComponentInChildren<StringOption>();
foreach (var pair in ModdedOptionsManager.RegisteredMods)
{
var container = ModdedOptionsTab.InitializeForMod(pair.Value, __instance).transform;
foreach (var group in ModdedOptionsManager.Groups.Where(group => group.AdvancedRole == null && group.ParentMod == pair.Value))
{
group.Header = ModdedOptionsTab.CreateHeader(toggleOpt, container, group.GroupName, group.GroupColor);
CreateOptionsFor(__instance, toggleOpt, numberOpt, stringOpt, container, ModdedOptionsManager.Options.Where(x => x.Group == group).ToList());
}
CreateOptionsFor(__instance, toggleOpt, numberOpt, stringOpt, container, ModdedOptionsManager.Options.Where(x => x.Group is null).ToList());
}
if (!numberOpt || !toggleOpt || !stringOpt)
{
Logger<MiraAPIPlugin>.Error("OPTION PREFABS MISSING");
}
}
public static void CreateOptionsFor(GameSettingMenu __instance, ToggleOption togglePrefab, NumberOption numberPrefab, StringOption stringPrefab, Transform container,
List<IModdedOption> options)
{
foreach (IModdedOption option in options)
{
if (option.AdvancedRole is not null || option.OptionBehaviour)
{
continue;
}
OptionBehaviour newOption = option.CreateOption(togglePrefab, numberPrefab, stringPrefab, container);
__instance.AllItems = __instance.AllItems.AddItem(newOption.transform).ToArray();
}
}
}
-118
View File
@@ -1,118 +0,0 @@
using AmongUs.GameOptions;
using HarmonyLib;
using MiraAPI.GameOptions;
using MiraAPI.Roles;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MiraAPI.Patches.Options;
[HarmonyPatch(typeof(IGameOptionsExtensions), "ToHudString")]
public static class ToHudStringPatch
{
public static int CurrentPage = 0;
/// <summary>
/// Generic method to add options
/// </summary>
public static void AddOptions(StringBuilder sb,
List<IModdedOption> options)
{
foreach (var opt in options)
{
sb.AppendLine(opt.GetHudStringText());
}
}
public static void Prefix()
{
if (GameManager.Instance is null || GameManager.Instance.IsHideAndSeek())
{
return;
}
foreach (var role in CustomRoleManager.CustomRoles.Values)
{
var customRole = role as ICustomRole;
if (customRole.IsGhostRole)
{
role.Role = RoleTypes.CrewmateGhost;
}
}
}
/// <summary>
/// Update the HudOptions on the left of the screen if player is using Launchpad options
/// </summary>
public static void Postfix(IGameOptions __instance, ref string __result)
{
if (GameManager.Instance is null)
{
return;
}
foreach (var pair in CustomRoleManager.CustomRoles)
{
var customRole = pair.Value as ICustomRole;
if (customRole.IsGhostRole)
{
pair.Value.Role = (RoleTypes)pair.Key;
}
}
if (CurrentPage != 0)
{
IMiraConfig currentPlugin = ModdedOptionsManager.RegisteredMods.Values.ToArray()[CurrentPage - 1];
if (currentPlugin == null)
{
CurrentPage = 0;
return;
}
var sb = new StringBuilder($"<size=180%><b>{currentPlugin.TabSettings.Title} Options:</b></size>\n<size=130%>");
var groupsWithRoles = ModdedOptionsManager.Groups.Where(group => group.AdvancedRole != null && group.ParentMod == currentPlugin);
var groupsWithoutRoles = ModdedOptionsManager.Groups.Where(group => group.AdvancedRole == null && group.ParentMod == currentPlugin);
AddOptions(sb,
ModdedOptionsManager.Options.Where(option => option.Group == null && option.ParentMod == currentPlugin && option.Visible()).ToList()
);
foreach (var group in groupsWithoutRoles)
{
if (!group.GroupVisible())
{
continue;
}
sb.AppendLine($"\n<size=160%><b>{group.GroupColor.ToTextColor()}{group.GroupName}</color></b></size>");
AddOptions(sb, ModdedOptionsManager.Options.Where(x => x.Group == group).ToList());
}
var customOptionGroups = groupsWithRoles as ModdedOptionGroup[] ?? groupsWithRoles.ToArray();
if (customOptionGroups.Any())
{
sb.AppendLine("\n<size=160%><b>Roles</b></size>");
foreach (var group in customOptionGroups)
{
if (!group.GroupVisible())
{
continue;
}
sb.AppendLine($"<size=140%><b>{group.GroupColor.ToTextColor()}{group.GroupName}</color></b></size><size=120%>");
AddOptions(sb, ModdedOptionsManager.Options.Where(x => x.Group == group).ToList());
sb.Append("</size>\n");
}
}
__result = sb.ToString();
}
if (CurrentPage == 0)
{
__result = "<size=160%><b>Normal Options:</b></size>\n<size=130%>" + __result;
}
__result += $"\nUse <b>TAB</b> to switch pages (Page {CurrentPage}/{ModdedOptionsManager.RegisteredMods.Count})</size>";
}
}
@@ -0,0 +1,58 @@
using HarmonyLib;
using MiraAPI.PluginLoading;
using MiraAPI.Utilities.Assets;
using Reactor.Utilities.Extensions;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
namespace MiraAPI.Patches.Options;
[HarmonyPatch(typeof(LobbyViewSettingsPane))]
public static class LobbyViewPanePatches
{
[HarmonyPostfix]
[HarmonyPatch(nameof(LobbyViewSettingsPane.Awake))]
public static void AwakePatch(LobbyViewSettingsPane __instance)
{
__instance.gameModeText.transform.localPosition = new Vector3(-4.3f, 2.4f, -2);
__instance.gameModeText.horizontalAlignment = HorizontalAlignmentOptions.Center;
__instance.gameModeText.fontSizeMax = 10f;
__instance.gameModeText.margin = new Vector4(1,0,1,0);
// Create the next button
GameObject nextButton = Object.Instantiate(__instance.BackButton, __instance.BackButton.transform.parent).gameObject;
nextButton.transform.localPosition = new Vector3(-2.3f, 2.4f, -2f);
nextButton.transform.localScale = new Vector3(3, 3, 2);
nextButton.name = "RightArrowButton";
var normal = nextButton.transform.FindChild("Normal").GetComponentInChildren<SpriteRenderer>();
normal.transform.localPosition = new Vector3(0, 0f, 0.3f);
normal.sprite = MiraAssets.NextButton.LoadAsset();
var hover = nextButton.transform.FindChild("Hover").GetComponentInChildren<SpriteRenderer>();
hover.transform.localPosition = new Vector3(0, 0f, 0.3f);
hover.sprite = MiraAssets.NextButtonActive.LoadAsset();
PassiveButton passiveButton = nextButton.gameObject.GetComponent<PassiveButton>();
passiveButton.OnClick = new Button.ButtonClickedEvent();
passiveButton.OnClick.AddListener((UnityAction)(() =>
{
}));
// Create the back button
GameObject backButton = Object.Instantiate(nextButton, __instance.BackButton.transform.parent).gameObject;
backButton.transform.localPosition = new Vector3(-6.3f, 2.4f, -2f);
backButton.name = "LeftArrowButton";
backButton.transform.FindChild("Normal").gameObject.GetComponentInChildren<SpriteRenderer>().flipX
= backButton.transform.FindChild("Hover").gameObject.GetComponentInChildren<SpriteRenderer>().flipX
= true;
PassiveButton passiveButton2 = backButton.gameObject.GetComponent<PassiveButton>();
passiveButton2.OnClick = new Button.ButtonClickedEvent();
passiveButton2.OnClick.AddListener((UnityAction)(() =>
{
}));
}
}
+62
View File
@@ -0,0 +1,62 @@
using HarmonyLib;
using Il2CppSystem;
using MiraAPI.Utilities;
namespace MiraAPI.Patches.Options
{
[HarmonyPatch]
public static class OptionsPatches
{
[HarmonyPrefix]
[HarmonyPatch(typeof(ToggleOption), nameof(ToggleOption.Initialize))]
public static bool ToggleInit(ToggleOption __instance)
{
if (!__instance.IsCustom()) return true;
__instance.TitleText.text = DestroyableSingleton<TranslationController>.Instance.GetString(__instance.Title, Array.Empty<Object>());
return false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(ToggleOption), nameof(ToggleOption.UpdateValue))]
public static bool ToggleUpdate(ToggleOption __instance)
{
return !__instance.IsCustom();
}
[HarmonyPrefix]
[HarmonyPatch(typeof(NumberOption), nameof(NumberOption.Initialize))]
public static bool NumberInit(NumberOption __instance)
{
if (!__instance.IsCustom()) return true;
__instance.TitleText.text = DestroyableSingleton<TranslationController>.Instance.GetString(__instance.Title, Array.Empty<Object>());
return false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(NumberOption), nameof(NumberOption.UpdateValue))]
public static bool NumberUpdate(NumberOption __instance)
{
return !__instance.IsCustom();
}
[HarmonyPrefix]
[HarmonyPatch(typeof(StringOption), nameof(StringOption.Initialize))]
public static bool StringInit(StringOption __instance)
{
if (!__instance.IsCustom()) return true;
__instance.TitleText.text = DestroyableSingleton<TranslationController>.Instance.GetString(__instance.Title, Array.Empty<Object>());
__instance.ValueText.text = DestroyableSingleton<TranslationController>.Instance.GetString(__instance.Values[__instance.Value], Array.Empty<Object>());
return false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(StringOption), nameof(StringOption.UpdateValue))]
public static bool StringUpdate(StringOption __instance)
{
return !__instance.IsCustom();
}
}
}
@@ -0,0 +1,213 @@
using HarmonyLib;
using MiraAPI.Roles;
using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets;
using Reactor.Utilities.Extensions;
using System;
using System.Linq;
using MiraAPI.Networking;
using Reactor.Networking.Rpc;
using Reactor.Utilities;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using static UnityEngine.UI.Button;
using Object = UnityEngine.Object;
namespace MiraAPI.Patches.Options
{
[HarmonyPatch(typeof(RolesSettingsMenu))]
public static class RoleSettingMenuPatches
{
[HarmonyPrefix]
[HarmonyPatch(nameof(RolesSettingsMenu.SetQuotaTab))]
public static bool PatchStart(RolesSettingsMenu __instance)
{
__instance.roleChances = new Il2CppSystem.Collections.Generic.List<RoleOptionSetting>();
__instance.advancedSettingChildren = new Il2CppSystem.Collections.Generic.List<OptionBehaviour>();
if (GameSettingMenuPatches.currentSelectedMod == 0)
{
__instance.AllButton.transform.parent.gameObject.SetActive(true);
__instance.AllButton.gameObject.SetActive(true);
__instance.scrollBar.transform.localPosition = new Vector3(-1.4957f, 0.657f, -4);
return true;
}
float num = 0.522f;
__instance.AllButton.transform.parent.gameObject.SetActive(false);
__instance.AllButton.gameObject.SetActive(false);
__instance.scrollBar.transform.localPosition = new Vector3(-1.4957f, 1.5261f, -4);
CategoryHeaderEditRole categoryHeaderEditRole = Object.Instantiate(__instance.categoryHeaderEditRoleOrigin, Vector3.zero, Quaternion.identity, __instance.RoleChancesSettings.transform);
categoryHeaderEditRole.SetHeader(StringNames.CrewmateRolesHeader, 20);
categoryHeaderEditRole.transform.localPosition = new Vector3(4.986f, num, -2f);
num -= 0.522f;
int num3 = 0;
foreach (var role in GameSettingMenuPatches.selectedMod.CustomRoles.Values.OfType<ICustomRole>().Where(role => role.Team == ModdedRoleTeams.Crewmate))
{
CreateQuotaOption(__instance, role as RoleBehaviour, ref num, num3);
num3++;
}
num -= 0.4f;
CategoryHeaderEditRole categoryHeaderEditRole2 = Object.Instantiate(__instance.categoryHeaderEditRoleOrigin, Vector3.zero, Quaternion.identity, __instance.RoleChancesSettings.transform);
categoryHeaderEditRole2.SetHeader(StringNames.ImpostorRolesHeader, 20);
categoryHeaderEditRole2.transform.localPosition = new Vector3(4.986f, num, -2f);
num -= 0.522f;
foreach (var role in GameSettingMenuPatches.selectedMod.CustomRoles.Values.OfType<ICustomRole>().Where(role => role.Team == ModdedRoleTeams.Impostor))
{
CreateQuotaOption(__instance, role as RoleBehaviour, ref num, num3);
num3++;
}
num -= 0.4f;
CategoryHeaderEditRole categoryHeaderEditRole3 = Object.Instantiate(__instance.categoryHeaderEditRoleOrigin, Vector3.zero, Quaternion.identity, __instance.RoleChancesSettings.transform);
categoryHeaderEditRole3.SetHeader(StringNames.None, 20);
categoryHeaderEditRole3.Title.text = "Neutral Roles";
categoryHeaderEditRole3.transform.localPosition = new Vector3(4.986f, num, -2f);
num -= 0.522f;
foreach (var role in GameSettingMenuPatches.selectedMod.CustomRoles.Values.OfType<ICustomRole>().Where(role => role.Team == ModdedRoleTeams.Neutral))
{
CreateQuotaOption(__instance, role as RoleBehaviour, ref num, num3);
num3++;
}
return false;
}
[HarmonyPrefix]
[HarmonyPatch(nameof(RolesSettingsMenu.Update))]
public static bool UpdatePatch()
{
if (GameSettingMenuPatches.currentSelectedMod == 0)
{
return true;
}
return false;
}
private static void ValueChanged(OptionBehaviour obj)
{
var roleSetting = obj.Cast<RoleOptionSetting>();
var role = roleSetting.Role as ICustomRole;
if (role is null or { HideSettings: true }) return;
PluginSingleton<MiraApiPlugin>.Instance.Config.TryGetEntry<int>(role.NumConfigDefinition, out var numEntry);
numEntry.Value = roleSetting.RoleMaxCount;
PluginSingleton<MiraApiPlugin>.Instance.Config.TryGetEntry<int>(role.ChanceConfigDefinition, out var chanceEntry);
chanceEntry.Value = roleSetting.RoleChance;
roleSetting.UpdateValuesAndText(GameOptionsManager.Instance.CurrentGameOptions.RoleOptions);
DestroyableSingleton<HudManager>.Instance.Notifier.AddRoleSettingsChangeMessage(roleSetting.Role.StringName, roleSetting.RoleMaxCount, roleSetting.RoleChance, roleSetting.Role.TeamType, false);
if (AmongUsClient.Instance.AmHost)
{
Rpc<SyncRoleOptionsRpc>.Instance.Send(PlayerControl.LocalPlayer, [role.GetNetData(roleSetting.Role)], true);
}
GameOptionsManager.Instance.GameHostOptions = GameOptionsManager.Instance.CurrentGameOptions;
}
private static void CreateAdvancedSettings(RolesSettingsMenu __instance, RoleBehaviour role)
{
foreach (var option in __instance.advancedSettingChildren)
{
Object.Destroy(option.gameObject);
}
__instance.advancedSettingChildren.Clear();
float num = -0.872f;
foreach (var option in GameSettingMenuPatches.selectedMod.Options.Where(option => option.AdvancedRole == role.GetType()))
{
OptionBehaviour newOpt = option.CreateOption(__instance.checkboxOrigin, __instance.numberOptionOrigin, __instance.stringOptionOrigin, __instance.AdvancedRolesSettings.transform);
newOpt.transform.localPosition = new Vector3(2.17f, num, -2f);
newOpt.SetClickMask(__instance.ButtonClickMask);
SpriteRenderer[] componentsInChildren = newOpt.GetComponentsInChildren<SpriteRenderer>(true);
for (int i = 0; i < componentsInChildren.Length; i++)
{
componentsInChildren[i].material.SetInt(PlayerMaterial.MaskLayer, 20);
}
foreach (TextMeshPro textMeshPro in newOpt.GetComponentsInChildren<TextMeshPro>(true))
{
textMeshPro.fontMaterial.SetFloat("_StencilComp", 3f);
textMeshPro.fontMaterial.SetFloat("_Stencil", 20);
}
newOpt.LabelBackground.enabled = false;
__instance.advancedSettingChildren.Add(newOpt);
num += -0.45f;
newOpt.Initialize();
}
__instance.scrollBar.CalculateAndSetYBounds((float)(__instance.advancedSettingChildren.Count + 3), 1f, 6f, 0.45f);
}
private static void ChangeTab(RoleBehaviour role, RolesSettingsMenu __instance)
{
ICustomRole customRole = role as ICustomRole;
__instance.roleDescriptionText.text = customRole.RoleLongDescription;
__instance.roleTitleText.text = DestroyableSingleton<TranslationController>.Instance.GetString(role.StringName, Array.Empty<Il2CppSystem.Object>());
__instance.roleScreenshot.sprite = Sprite.Create(role.RoleScreenshot.texture, new Rect(0, 0, 370, 230), Vector2.one / 2, 100);
__instance.roleScreenshot.drawMode = SpriteDrawMode.Sliced;
__instance.roleHeaderSprite.color = customRole.RoleColor;
__instance.roleHeaderText.color = customRole.RoleColor.DarkenColor();
CreateAdvancedSettings(__instance, role);
foreach (var optionBehaviour in __instance.advancedSettingChildren)
{
optionBehaviour.OnValueChanged = new Action<OptionBehaviour>(__instance.ValueChanged);
if (AmongUsClient.Instance && !AmongUsClient.Instance.AmHost)
{
optionBehaviour.SetAsPlayer();
}
}
__instance.RoleChancesSettings.SetActive(false);
__instance.AdvancedRolesSettings.SetActive(true);
__instance.RefreshChildren();
}
public static void CreateQuotaOption(RolesSettingsMenu __instance, RoleBehaviour role, ref float yPos, int index)
{
ICustomRole customRole = role as ICustomRole;
RoleOptionSetting roleOptionSetting = Object.Instantiate(__instance.roleOptionSettingOrigin, Vector3.zero, Quaternion.identity, __instance.RoleChancesSettings.transform);
roleOptionSetting.transform.localPosition = new Vector3(-0.15f, yPos, -2f);
roleOptionSetting.SetRole(GameOptionsManager.Instance.CurrentGameOptions.RoleOptions, role, 20);
roleOptionSetting.labelSprite.color = customRole.RoleColor;
roleOptionSetting.titleText.color = customRole.RoleColor.DarkenColor();
roleOptionSetting.OnValueChanged = new Action<OptionBehaviour>(ValueChanged);
roleOptionSetting.titleText.horizontalAlignment = TMPro.HorizontalAlignmentOptions.Left;
roleOptionSetting.SetClickMask(__instance.ButtonClickMask);
__instance.roleChances.Add(roleOptionSetting);
PassiveButton newButton = Object.Instantiate(roleOptionSetting.buttons[0], roleOptionSetting.transform);
newButton.name = "ConfigButton";
newButton.transform.localPosition = new Vector3(0.2419f, -0.2582f, -2f);
newButton.transform.FindChild("Plus_TMP").gameObject.DestroyImmediate();
newButton.transform.FindChild("InactiveSprite").GetComponent<SpriteRenderer>().sprite = MiraAssets.Cog.LoadAsset();
newButton.transform.FindChild("ActiveSprite").GetComponent<SpriteRenderer>().sprite = MiraAssets.CogActive.LoadAsset();
PassiveButton passiveButton = newButton.GetComponent<PassiveButton>();
passiveButton.OnClick = new ButtonClickedEvent();
passiveButton.OnClick.AddListener((UnityAction)(() =>
{
ChangeTab(role, __instance);
}));
if (index < GameSettingMenuPatches.selectedMod.CustomRoles.Count - 1)
{
yPos += -0.43f;
}
}
}
}
+1 -1
View File
@@ -12,7 +12,7 @@ public static class GameStartupPatch
/// This is used for registering roles when the game opens, might be a janky solution, but it works
/// </summary>
[HarmonyPostfix]
[HarmonyPatch("Start")]
[HarmonyPatch(nameof(MainMenuManager.Start))]
public static void StartPostfix()
{
if (_runOnce)
+2 -1
View File
@@ -9,7 +9,8 @@ namespace MiraAPI.Patches.Roles;
[HarmonyPatch(typeof(PlayerNameColor))]
public static class NameTagPatch
{
[HarmonyPrefix, HarmonyPatch("Get", typeof(RoleBehaviour))]
[HarmonyPrefix]
[HarmonyPatch(nameof(PlayerNameColor.Get), typeof(RoleBehaviour))]
public static bool GetPatch([HarmonyArgument(0)] RoleBehaviour otherPlayerRole, ref Color __result)
{
if (PlayerControl.LocalPlayer.Data.IsDead)
@@ -10,7 +10,8 @@ public static class RoleBehaviourPatches
/// <summary>
/// Update TeamColor text for Launchpad roles
/// </summary>
[HarmonyPrefix, HarmonyPatch("TeamColor", MethodType.Getter)]
[HarmonyPrefix]
[HarmonyPatch(nameof(RoleBehaviour.TeamColor), MethodType.Getter)]
public static bool PrefixTeamColorGetter(RoleBehaviour __instance, ref Color __result)
{
if (__instance is not ICustomRole behaviour)
+2 -1
View File
@@ -8,7 +8,8 @@ namespace MiraAPI.Patches.Roles;
[HarmonyPatch(typeof(RoleManager))]
public static class RoleManagerPatches
{
[HarmonyPrefix, HarmonyPatch("AssignRoleOnDeath")]
[HarmonyPrefix]
[HarmonyPatch(nameof(RoleManager.AssignRoleOnDeath))]
public static bool AssignRoleOnDeath(RoleManager __instance, [HarmonyArgument(0)] PlayerControl plr)
{
if (plr == null || !plr.Data.IsDead)
@@ -1,24 +0,0 @@
using HarmonyLib;
using MiraAPI.Roles;
namespace MiraAPI.Patches.Roles;
[HarmonyPatch(typeof(RoleOptionSetting))]
public static class RoleOptionSettingPatch
{
/// <summary>
/// Update the role details screen in role settings for Launchpad roles
/// </summary>
[HarmonyPrefix, HarmonyPatch("ShowRoleDetails")]
public static bool ShowRoleDetailsPrefix(RoleOptionSetting __instance)
{
if (__instance.Role is ICustomRole customRole)
{
GameSettingMenu.Instance.RoleName.text = __instance.Role.NiceName;
GameSettingMenu.Instance.RoleBlurb.text = __instance.Role.BlurbLong;
GameSettingMenu.Instance.RoleIcon.sprite = customRole.Icon.LoadAsset();
return false;
}
return true;
}
}
@@ -5,13 +5,14 @@ using Reactor.Utilities;
namespace MiraAPI.Patches.Roles;
[HarmonyPatch(typeof(RoleOptionsCollectionV07))]
[HarmonyPatch(typeof(RoleOptionsCollectionV08))]
public static class RoleOptionsCollectionPatch
{
/// <summary>
/// Set the role chance for custom Launchpad roles based on config
/// </summary>
[HarmonyPrefix, HarmonyPatch("GetChancePerGame")]
[HarmonyPrefix]
[HarmonyPatch(nameof(RoleOptionsCollectionV08.GetChancePerGame))]
public static bool GetChancePrefix([HarmonyArgument(0)] RoleTypes roleType, ref int __result)
{
if (CustomRoleManager.GetCustomRoleBehaviour(roleType, out var customRole))
@@ -21,7 +22,7 @@ public static class RoleOptionsCollectionPatch
return false;
}
PluginSingleton<MiraAPIPlugin>.Instance.Config.TryGetEntry<int>(customRole.ChanceConfigDefinition, out var entry);
PluginSingleton<MiraApiPlugin>.Instance.Config.TryGetEntry<int>(customRole.ChanceConfigDefinition, out var entry);
__result = entry.Value;
return false;
}
@@ -32,7 +33,8 @@ public static class RoleOptionsCollectionPatch
/// <summary>
/// Set the amount for custom Launchpad roles based on config
/// </summary>
[HarmonyPrefix, HarmonyPatch("GetNumPerGame")]
[HarmonyPrefix]
[HarmonyPatch(nameof(RoleOptionsCollectionV08.GetNumPerGame))]
public static bool GetNumPrefix([HarmonyArgument(0)] RoleTypes roleType, ref int __result)
{
if (CustomRoleManager.GetCustomRoleBehaviour(roleType, out var customRole))
@@ -42,7 +44,7 @@ public static class RoleOptionsCollectionPatch
return false;
}
PluginSingleton<MiraAPIPlugin>.Instance.Config.TryGetEntry<int>(customRole.NumConfigDefinition, out var entry);
PluginSingleton<MiraApiPlugin>.Instance.Config.TryGetEntry<int>(customRole.NumConfigDefinition, out var entry);
__result = entry.Value;
return false;
}
@@ -1,147 +0,0 @@
using AmongUs.GameOptions;
using HarmonyLib;
using Il2CppInterop.Runtime;
using MiraAPI.Networking.Options;
using MiraAPI.Roles;
using Reactor.Networking.Rpc;
using Reactor.Utilities.Extensions;
using System.Linq;
using UnityEngine;
using Object = UnityEngine.Object;
namespace MiraAPI.Patches.Roles;
[HarmonyPatch(typeof(RolesSettingsMenu))]
public static class RolesSettingsMenuPatches
{
/// <summary>
/// Create role buttons and advanced role settings menu for every custom Launchpad role
/// </summary>
[HarmonyPrefix, HarmonyPatch("OnEnable")]
public static void OnEnablePrefix(RolesSettingsMenu __instance)
{
var tabPrefab = __instance.AllAdvancedSettingTabs.ToArray()[1].Tab;
foreach (var (key, role) in CustomRoleManager.CustomRoles)
{
if (__instance.AllAdvancedSettingTabs.ToArray().Any(x => (ushort)x.Type == key))
{
continue;
}
if (role is ICustomRole { HideSettings: true })
{
continue;
}
var numChanceOption = Object.Instantiate(__instance.SettingPrefab, __instance.ItemParent);
numChanceOption.transform.localPosition = new Vector3(0.044f, 0, 0);
numChanceOption.name = role.NiceName;
numChanceOption.Role = role;
__instance.AllRoleSettings.Add(numChanceOption);
var newTab = Object.Instantiate(tabPrefab, __instance.AdvancedRolesSettings.transform);
newTab.name = role.NiceName + " Settings";
var toggleSet = Object.Instantiate(newTab.GetComponentInChildren<ToggleOption>(true));
var numberSet = Object.Instantiate(newTab.GetComponentInChildren<NumberOption>(true));
foreach (var option in newTab.GetComponentsInChildren<OptionBehaviour>())
{
option.gameObject.Destroy();
}
var startOffset = 0.5f;
/* foreach (var customOption in CustomOptionsManager.CustomOptions)
{
if (customOption.AdvancedRole is not null && customOption.AdvancedRole == role.GetType())
{
startOffset -= 0.5f;
switch (customOption)
{
case CustomNumberOption numberOption:
var numOpt = numberOption.CreateNumberOption(numberSet, newTab.transform);
numOpt.transform.localPosition = new Vector3(-1.25f, startOffset, 0);
break;
case CustomToggleOption toggleOption:
var togOpt = toggleOption.CreateToggleOption(toggleSet, newTab.transform);
togOpt.transform.localPosition = new Vector3(-1.25f, startOffset, 0);
break;
}
}
}*/
var tmp = newTab.GetComponentInChildren<TextTranslatorTMP>();
tmp.defaultStr = role.NiceName;
tmp.TargetText = role.StringName;
var newAdvSet = new AdvancedRoleSettingsButton
{
Tab = newTab,
Type = (RoleTypes)key
};
__instance.AllAdvancedSettingTabs.Add(newAdvSet);
toggleSet.gameObject.Destroy();
numberSet.gameObject.Destroy();
}
}
/// <summary>
/// Set custom role options OnValueChanged and re-enable the role settings menu scrolling
/// </summary>
[HarmonyPostfix, HarmonyPatch("Start")]
public static void StartPostfix(RolesSettingsMenu __instance)
{
/* foreach (var customOption in CustomOptionsManager.CustomOptions)
{
if (customOption.AdvancedRole is not null)
{
customOption.OptionBehaviour.OnValueChanged = (Action<OptionBehaviour>)customOption.ValueChanged;
}
}*/
var scroll = __instance.GetComponentInChildren<Scroller>();
scroll.active = true;
scroll.ContentYBounds.max = 2 * __instance.AllRoleSettings.Count / 10f;
scroll.transform.FindChild("UI_Scrollbar").gameObject.SetActive(true);
}
/// <summary>
/// Update config when value changed
/// </summary>
[HarmonyPrefix, HarmonyPatch("ValueChanged")]
public static bool ValueChangedPrefix(RolesSettingsMenu __instance, [HarmonyArgument(0)] OptionBehaviour obj)
{
if (obj.GetIl2CppType() != Il2CppType.Of<RoleOptionSetting>())
{
return true;
}
var roleSetting = obj.Cast<RoleOptionSetting>();
if (roleSetting.Role is not ICustomRole role || role.HideSettings)
{
return true;
}
MiraAPIPlugin.Instance.Config.TryGetEntry<int>(role.NumConfigDefinition, out var numEntry);
numEntry.Value = roleSetting.RoleMaxCount;
MiraAPIPlugin.Instance.Config.TryGetEntry<int>(role.ChanceConfigDefinition, out var chanceEntry);
chanceEntry.Value = roleSetting.RoleChance;
roleSetting.UpdateValuesAndText(GameOptionsManager.Instance.CurrentGameOptions.RoleOptions);
if (AmongUsClient.Instance.AmHost)
{
Rpc<SyncRoleOptionsRpc>.Instance.Send(new SyncRoleOptionsRpc.Data((ushort)roleSetting.Role.Role, numEntry.Value, chanceEntry.Value));
}
GameOptionsManager.Instance.GameHostOptions = GameOptionsManager.Instance.CurrentGameOptions;
return false;
}
}
+44 -10
View File
@@ -3,7 +3,9 @@ using HarmonyLib;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using Il2CppSystem.Text;
using MiraAPI.PluginLoading;
using MiraAPI.Roles;
using System.Linq;
using UnityEngine;
using Object = UnityEngine.Object;
@@ -15,24 +17,24 @@ public static class TaskAdderPatch
public static TaskFolder RolesFolder;
public static System.Collections.Generic.Dictionary<string, string> ModsFolders = new System.Collections.Generic.Dictionary<string, string>();
[HarmonyPostfix, HarmonyPatch(typeof(TaskAdderGame), nameof(TaskAdderGame.Begin))]
[HarmonyPostfix]
[HarmonyPatch(typeof(TaskAdderGame), nameof(TaskAdderGame.Begin))]
public static void AddRolesFolder(TaskAdderGame __instance)
{
RolesFolder = Object.Instantiate(__instance.RootFolderPrefab, __instance.transform);
RolesFolder.gameObject.SetActive(false);
RolesFolder.FolderName = "Roles";
RolesFolder.name = "RolesFolder";
foreach (var pair in CustomRoleManager.ModRoles)
foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins.Values)
{
var newFolder = Object.Instantiate(__instance.RootFolderPrefab, __instance.transform);
newFolder.FolderName = pair.Key.Metadata.Name;
newFolder.name = pair.Key.Metadata.Name;
newFolder.FolderName = newFolder.name = plugin.PluginInfo.Metadata.Name;
newFolder.gameObject.SetActive(false);
RolesFolder.SubFolders.Add(newFolder);
if (!ModsFolders.ContainsKey(pair.Key.Metadata.Name))
if (!ModsFolders.ContainsKey(plugin.PluginInfo.Metadata.Name))
{
ModsFolders.Add(pair.Key.Metadata.Name, pair.Key.Metadata.GUID);
ModsFolders.Add(plugin.PluginInfo.Metadata.Name, plugin.PluginId);
}
}
@@ -41,7 +43,8 @@ public static class TaskAdderPatch
__instance.GoToRoot();
}
[HarmonyPostfix, HarmonyPatch(typeof(TaskAddButton), "Role", MethodType.Setter)]
[HarmonyPostfix]
[HarmonyPatch(typeof(TaskAddButton), "Role", MethodType.Setter)]
public static void RoleGetterPatch(TaskAddButton __instance)
{
if (__instance.role is ICustomRole lpRole)
@@ -56,7 +59,8 @@ public static class TaskAdderPatch
}
// yes it might be crazy patching the entire method, but i tried so many other methods and only this works :cry:
[HarmonyPrefix, HarmonyPatch(typeof(TaskAdderGame), nameof(TaskAdderGame.ShowFolder))]
[HarmonyPrefix]
[HarmonyPatch(typeof(TaskAdderGame), nameof(TaskAdderGame.ShowFolder))]
public static bool ShowPatch(TaskAdderGame __instance, [HarmonyArgument(0)] TaskFolder taskFolder)
{
var stringBuilder = new StringBuilder(64);
@@ -149,12 +153,42 @@ public static class TaskAdderPatch
}
}
}
if (taskFolder.FolderName == "Roles")
{
for (int m = 0; m < DestroyableSingleton<RoleManager>.Instance.AllRoles.Length; m++)
{
RoleBehaviour roleBehaviour = DestroyableSingleton<RoleManager>.Instance.AllRoles[m];
if (roleBehaviour.Role != RoleTypes.ImpostorGhost && roleBehaviour.Role != RoleTypes.CrewmateGhost && !CustomRoleManager.CustomRoles.ContainsKey((ushort)roleBehaviour.Role))
{
TaskAddButton taskAddButton2 = Object.Instantiate<TaskAddButton>(__instance.RoleButton);
taskAddButton2.SafePositionWorld = __instance.SafePositionWorld;
taskAddButton2.Text.text = "Be_" + roleBehaviour.NiceName + ".exe";
__instance.AddFileAsChild(RolesFolder, taskAddButton2, ref num, ref num2, ref num3);
taskAddButton2.Role = roleBehaviour;
if (taskAddButton2 != null && taskAddButton2.Button != null)
{
ControllerManager.Instance.AddSelectableUiElement(taskAddButton2.Button, false);
if (m == 0 && __instance.restorePreviousSelectionFound != null)
{
ControllerManager.Instance.SetDefaultSelection(__instance.restorePreviousSelectionFound, null);
__instance.restorePreviousSelectionByFolderName = string.Empty;
__instance.restorePreviousSelectionFound = null;
}
else if (m == 0)
{
ControllerManager.Instance.SetDefaultSelection(taskAddButton2.Button, null);
}
}
}
}
}
if (ModsFolders.TryGetValue(taskFolder.FolderName, out string guid))
{
for (var m = 0; m < CustomRoleManager.GetRolesRegistered(guid).Count; m++)
MiraPluginInfo plugin = MiraPluginManager.Instance.GetPluginByGuid(guid);
for (var m = 0; m < plugin.CustomRoles.Count; m++)
{
var roleBehaviour = CustomRoleManager.GetRolesRegistered(guid)[m];
var roleBehaviour = plugin.CustomRoles.ElementAt(m).Value;
if (roleBehaviour.Role != RoleTypes.ImpostorGhost && roleBehaviour.Role != RoleTypes.CrewmateGhost && !roleBehaviour.IsDead)
{
var taskAddButton2 = Object.Instantiate(__instance.RoleButton);
+9
View File
@@ -0,0 +1,9 @@
using BepInEx.Configuration;
namespace MiraAPI.PluginLoading
{
public interface IMiraPlugin
{
public ConfigFile GetConfigFile();
}
}
+30
View File
@@ -0,0 +1,30 @@
using BepInEx;
using BepInEx.Configuration;
using MiraAPI.GameModes;
using MiraAPI.GameOptions;
using System.Collections.Generic;
namespace MiraAPI.PluginLoading;
public class MiraPluginInfo
{
public string PluginId { get; set; }
public IMiraPlugin MiraPlugin { get; set; }
public PluginInfo PluginInfo { get; set; }
public ConfigFile PluginConfig { get; set; }
public readonly List<IModdedOptionGroup> OptionGroups = [];
public readonly List<IModdedOption> Options = [];
public readonly Dictionary<ushort, RoleBehaviour> CustomRoles = [];
public readonly Dictionary<int, CustomGameMode> GameModes = [];
public MiraPluginInfo(string id, IMiraPlugin miraPlugin, PluginInfo info)
{
PluginId = id;
MiraPlugin = miraPlugin;
PluginInfo = info;
PluginConfig = miraPlugin.GetConfigFile();
}
}
+105
View File
@@ -0,0 +1,105 @@
using BepInEx;
using BepInEx.Unity.IL2CPP;
using Il2CppInterop.Runtime.Injection;
using MiraAPI.GameOptions;
using MiraAPI.GameOptions.Attributes;
using MiraAPI.Roles;
using Reactor.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace MiraAPI.PluginLoading;
public class MiraPluginManager
{
public readonly Dictionary<Assembly, MiraPluginInfo> RegisteredPlugins = [];
private static MiraPluginManager _instance;
public static MiraPluginManager Instance {
get => _instance ??= new MiraPluginManager();
private set => _instance = value;
}
public void Initialize()
{
Instance = this;
IL2CPPChainloader.Instance.PluginLoad += (_, assembly, plugin) =>
{
if (!plugin.GetType().GetInterfaces().Contains(typeof(IMiraPlugin))) return;
var id = MetadataHelper.GetMetadata(plugin.GetType()).GUID;
var info = new MiraPluginInfo(id, plugin as IMiraPlugin, IL2CPPChainloader.Instance.Plugins[id]);
RegisterRoleAttribute(assembly, info);
RegisterOptionsGroups(assembly, info);
RegisterOptionsAttributes(assembly, info);
RegisteredPlugins.Add(assembly, info);
Logger<MiraApiPlugin>.Info($"Registering mod {id} with Mira API.");
};
}
public MiraPluginInfo GetPluginByGuid(string guid)
{
return RegisteredPlugins.Values.First(plugin => plugin.PluginId == guid);
}
private static void RegisterOptionsAttributes(Assembly assembly, MiraPluginInfo pluginInfo)
{
foreach (var type in assembly.GetTypes())
{
foreach (PropertyInfo property in type.GetProperties())
{
foreach (var attribute in property.GetCustomAttributes())
{
if (attribute.GetType().BaseType == typeof(ModdedOptionAttribute))
{
pluginInfo.Options.Add(ModdedOptionsManager.RegisterOption(type, (ModdedOptionAttribute)attribute, property));
}
}
}
}
}
private static void RegisterOptionsGroups(Assembly assembly, MiraPluginInfo pluginInfo)
{
foreach (var type in assembly.GetTypes())
{
if (typeof(IModdedOptionGroup).IsAssignableFrom(type))
{
IModdedOptionGroup group = (IModdedOptionGroup)Activator.CreateInstance(type);
ModdedOptionGroup newGroup = new ModdedOptionGroup
{
AdvancedRole = group.AdvancedRole,
GroupColor = group.GroupColor,
GroupName = group.GroupName,
GroupVisible = group.GroupVisible
};
ModdedOptionsManager.Groups.Add(newGroup);
ModdedOptionsManager.OriginalTypes.Add(type, newGroup);
pluginInfo.OptionGroups.Add(group);
}
}
}
private static void RegisterRoleAttribute(Assembly assembly, MiraPluginInfo pluginInfo)
{
foreach (var type in assembly.GetTypes())
{
var attribute = type.GetCustomAttribute<RegisterCustomRoleAttribute>();
if (attribute == null) continue;
ClassInjector.RegisterTypeInIl2Cpp(type);
var role = CustomRoleManager.RegisterRole(type, attribute.RoleId);
pluginInfo.CustomRoles.Add((ushort)role.Role, role);
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

+70 -68
View File
@@ -1,8 +1,7 @@
using AmongUs.GameOptions;
using BepInEx;
using BepInEx.Unity.IL2CPP;
using Il2CppInterop.Runtime;
using MiraAPI.Networking.Options;
using MiraAPI.Networking;
using MiraAPI.PluginLoading;
using Reactor.Localization.Utilities;
using Reactor.Networking.Rpc;
using Reactor.Utilities;
@@ -10,6 +9,7 @@ using Reactor.Utilities.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using HarmonyLib;
using TMPro;
using UnityEngine;
using Object = UnityEngine.Object;
@@ -18,46 +18,40 @@ namespace MiraAPI.Roles;
public static class CustomRoleManager
{
public static readonly Dictionary<ushort, RoleBehaviour> CustomRoles = [];
public static readonly Dictionary<PluginInfo, List<RoleBehaviour>> ModRoles = [];
public static readonly Dictionary<ushort, RoleBehaviour> CustomRoles = new();
private static int GetNextRoleId(ushort requestedId)
{
while (CustomRoles.ContainsKey(requestedId) || requestedId <= Enum.GetNames<RoleTypes>().Length)
{
requestedId++;
}
return requestedId;
}
public static void RegisterInRoleManager()
{
RoleManager.Instance.AllRoles = RoleManager.Instance.AllRoles.Concat(CustomRoles.Values).ToArray();
}
internal static void RegisterRole(Type roleType, ushort roleId, string modId)
internal static RoleBehaviour RegisterRole(Type roleType, ushort roleId)
{
if (!IL2CPPChainloader.Instance.Plugins.TryGetValue(modId, out PluginInfo info))
{
Debug.LogError($"{modId} is not a valid mod!");
return;
}
if (!(typeof(RoleBehaviour).IsAssignableFrom(roleType) && typeof(ICustomRole).IsAssignableFrom(roleType)))
{
return;
}
if (CustomRoles.Any(behaviour => behaviour.GetType() == roleType))
{
return;
Logger<MiraApiPlugin>.Error($"{roleType?.Name} does not inherit from RoleBehaviour or ICustomRole.");
return null;
}
var roleBehaviour = (RoleBehaviour)new GameObject(roleType.Name).DontDestroy().AddComponent(Il2CppType.From(roleType));
if (roleBehaviour is not ICustomRole customRole)
{
return;
roleBehaviour.gameObject.Destroy();
return null;
}
if (CustomRoles.ContainsKey(roleId))
{
Debug.LogError($"Role ID conflict for {customRole.RoleName}. Use a different ID.");
return;
}
roleBehaviour.Role = (RoleTypes)roleId;
roleBehaviour.Role = (RoleTypes)GetNextRoleId(roleId);
roleBehaviour.TeamType = customRole.Team == ModdedRoleTeams.Neutral ? RoleTeamTypes.Crewmate : (RoleTeamTypes)customRole.Team;
roleBehaviour.NameColor = customRole.RoleColor;
roleBehaviour.StringName = CustomStringName.CreateAndRegister(customRole.RoleName);
@@ -70,53 +64,38 @@ public static class CustomRoleManager
roleBehaviour.CanVent = customRole.CanUseVent;
roleBehaviour.DefaultGhostRole = customRole.GhostRole;
roleBehaviour.MaxCount = 15;
roleBehaviour.RoleScreenshot = customRole.OptionsScreenshot.LoadAsset();
if (customRole.IsGhostRole)
{
RoleManager.GhostRoles.Add(roleBehaviour.Role);
}
if (ModRoles.TryGetValue(info, out List<RoleBehaviour> roles))
{
roles.Add(roleBehaviour);
}
else
{
ModRoles.TryAdd(info, new List<RoleBehaviour>() { roleBehaviour });
}
CustomRoles.Add(roleId, roleBehaviour);
if (customRole.HideSettings)
{
return;
return roleBehaviour;
}
var config = PluginSingleton<MiraAPIPlugin>.Instance.Config;
var config = PluginSingleton<MiraApiPlugin>.Instance.Config;
config.Bind(customRole.NumConfigDefinition, 1);
config.Bind(customRole.ChanceConfigDefinition, 100);
return roleBehaviour;
}
public static List<RoleBehaviour> GetRolesRegistered(string modId)
public static MiraPluginInfo FindParentMod(ICustomRole role)
{
PluginInfo info = ModRoles.Keys.First(key => key.Metadata.GUID == modId);
if (ModRoles.TryGetValue(info, out List<RoleBehaviour> roles))
{
return roles;
}
else
{
Debug.LogError($"{modId} has no roles registered.");
return null;
}
return MiraPluginManager.Instance.RegisteredPlugins.First(plugin => plugin.Value.CustomRoles.Values.Contains(role as RoleBehaviour)).Value;
}
public static bool GetCustomRoleBehaviour(RoleTypes roleType, out ICustomRole result)
{
CustomRoles.TryGetValue((ushort)roleType, out var temp);
if (temp is ICustomRole role)
if (temp != null)
{
result = role;
result = temp as ICustomRole;
return true;
}
@@ -157,38 +136,61 @@ public static class CustomRoleManager
panel.SetTaskText(role.SetTabText().ToString());
}
public static void SyncRoleSettings()
public static void SyncAllRoleSettings(int targetId=-1)
{
List<NetData> data = [];
int count = 0;
foreach (var role in CustomRoles.Values)
{
ICustomRole customRole = role as ICustomRole;
if (customRole.HideSettings)
if (customRole is null or { HideSettings: true })
{
continue;
}
PluginSingleton<MiraAPIPlugin>.Instance.Config.TryGetEntry<int>(customRole.NumConfigDefinition, out var numEntry);
PluginSingleton<MiraAPIPlugin>.Instance.Config.TryGetEntry<int>(customRole.ChanceConfigDefinition, out var chanceEntry);
Rpc<SyncRoleOptionsRpc>.Instance.Send(new SyncRoleOptionsRpc.Data((ushort)role.Role, numEntry.Value, chanceEntry.Value));
PluginSingleton<MiraApiPlugin>.Instance.Config.TryGetEntry<int>(customRole.NumConfigDefinition, out var numEntry);
PluginSingleton<MiraApiPlugin>.Instance.Config.TryGetEntry<int>(customRole.ChanceConfigDefinition, out var chanceEntry);
var netData = new NetData((uint)role.Role, BitConverter.GetBytes(numEntry.Value).AddRangeToArray(BitConverter.GetBytes(chanceEntry.Value)));
data.Add(netData);
count += netData.GetLength();
if (count > 1000)
{
Rpc<SyncRoleOptionsRpc>.Instance.SendTo(PlayerControl.LocalPlayer, targetId, data.ToArray());
data.Clear();
count = 0;
}
}
if (data.Count > 0)
{
Rpc<SyncRoleOptionsRpc>.Instance.SendTo(PlayerControl.LocalPlayer, targetId, data.ToArray());
}
}
public static void SyncRoleSettings(int targetId)
public static void HandleSyncRoleOptions(NetData[] data)
{
foreach (var role in CustomRoles.Values)
foreach (var netData in data)
{
ICustomRole customRole = role as ICustomRole;
if (customRole.HideSettings)
if (CustomRoles.TryGetValue((ushort)netData.Id, out var role))
{
continue;
var customRole = role as ICustomRole;
if (customRole is null or { HideSettings: true })
{
continue;
}
var num = BitConverter.ToInt32(netData.Data, 0);
var chance = BitConverter.ToInt32(netData.Data, 4);
PluginSingleton<MiraApiPlugin>.Instance.Config.TryGetEntry<int>(customRole.NumConfigDefinition, out var numEntry);
PluginSingleton<MiraApiPlugin>.Instance.Config.TryGetEntry<int>(customRole.ChanceConfigDefinition, out var chanceEntry);
numEntry.Value = num;
chanceEntry.Value = chance;
}
PluginSingleton<MiraAPIPlugin>.Instance.Config.TryGetEntry<int>(customRole.NumConfigDefinition, out var numEntry);
PluginSingleton<MiraAPIPlugin>.Instance.Config.TryGetEntry<int>(customRole.ChanceConfigDefinition, out var chanceEntry);
Rpc<SyncRoleOptionsRpc>.Instance.SendTo(targetId, new SyncRoleOptionsRpc.Data((ushort)role.Role, numEntry.Value, chanceEntry.Value));
}
}
}
+15 -2
View File
@@ -1,8 +1,12 @@
using AmongUs.GameOptions;
using System;
using AmongUs.GameOptions;
using BepInEx.Configuration;
using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets;
using System.Text;
using HarmonyLib;
using MiraAPI.Networking;
using Reactor.Utilities;
using UnityEngine;
namespace MiraAPI.Roles;
@@ -18,6 +22,7 @@ public interface ICustomRole
Color RoleColor { get; }
ModdedRoleTeams Team { get; }
LoadableAsset<Sprite> OptionsScreenshot { get; }
LoadableAsset<Sprite> Icon => MiraAssets.Empty;
@@ -53,7 +58,7 @@ public interface ICustomRole
void HudUpdate(HudManager hudManager) { }
string GetCustomEjectionMessage(GameData.PlayerInfo player)
string GetCustomEjectionMessage(NetworkedPlayerInfo player)
{
return Team == ModdedRoleTeams.Impostor ? $"{player.PlayerName} was The {RoleName}" : null;
}
@@ -63,4 +68,12 @@ public interface ICustomRole
var taskStringBuilder = Helpers.CreateForRole(this);
return taskStringBuilder;
}
NetData GetNetData(RoleBehaviour roleBehaviour)
{
PluginSingleton<MiraApiPlugin>.Instance.Config.TryGetEntry<int>(NumConfigDefinition, out var numEntry);
PluginSingleton<MiraApiPlugin>.Instance.Config.TryGetEntry<int>(ChanceConfigDefinition, out var chanceEntry);
return new NetData((uint)roleBehaviour.Role, BitConverter.GetBytes(numEntry.Value).AddRangeToArray(BitConverter.GetBytes(chanceEntry.Value)));
}
}
+3 -31
View File
@@ -1,46 +1,18 @@
using BepInEx.Unity.IL2CPP;
using MiraAPI.Utilities;
using System;
using System.Collections.Generic;
using System.Reflection;
using System;
namespace MiraAPI.Roles;
[AttributeUsage(AttributeTargets.Class)]
public class RegisterCustomRoleAttribute : Attribute
{
private static readonly HashSet<Assembly> RegisteredAssemblies = [];
public ushort RoleId { get; }
public string ModId { get; }
public RegisterCustomRoleAttribute(string modId)
public RegisterCustomRoleAttribute()
{
ModId = modId;
RoleId = (ushort)(20 + CustomRoleManager.CustomRoles.Count);
}
public RegisterCustomRoleAttribute(string modId, ushort roleId)
public RegisterCustomRoleAttribute(ushort roleId)
{
ModId = modId;
RoleId = roleId;
}
public static void Initialize()
{
IL2CPPChainloader.Instance.PluginLoad += (_, assembly, plugin) => Register(assembly);
}
public static void Register(Assembly assembly)
{
if (!RegisteredAssemblies.Add(assembly)) return;
foreach (var type in assembly.GetTypes())
{
var attribute = type.GetCustomAttribute<RegisterCustomRoleAttribute>();
if (attribute != null)
{
Helpers.RegisterType(type);
CustomRoleManager.RegisterRole(type, attribute.RoleId, attribute.ModId);
}
}
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
namespace MiraAPI.Utilities
namespace MiraAPI.Utilities.Assets
{
public abstract class LoadableAsset<T> where T : UnityEngine.Object
{
@@ -1,8 +1,8 @@
using Reactor.Utilities.Extensions;
using System;
using System;
using Reactor.Utilities.Extensions;
using UnityEngine;
namespace MiraAPI.Utilities
namespace MiraAPI.Utilities.Assets
{
public class LoadableBundleAsset<T>(string name, AssetBundle bundle) : LoadableAsset<T> where T : UnityEngine.Object
{
+6
View File
@@ -3,5 +3,11 @@
public static class MiraAssets
{
public static readonly LoadableResourceAsset Empty = new("", "");
public static readonly LoadableResourceAsset NextButton = new("NextButton.png", "MiraAPI.Resources.");
public static readonly LoadableResourceAsset NextButtonActive = new("NextButtonActive.png", "MiraAPI.Resources.");
public static readonly LoadableResourceAsset Cog = new("Cog.png", "MiraAPI.Resources.");
public static readonly LoadableResourceAsset CogActive = new("CogActive.png", "MiraAPI.Resources.");
public static readonly LoadableResourceAsset Banner = new("FortniteBanner.jpeg", "MiraAPI.Resources.");
}
}
+11 -1
View File
@@ -1,4 +1,5 @@
using MiraAPI.Roles;
using MiraAPI.GameOptions;
using MiraAPI.Roles;
using Reactor.Utilities.Extensions;
using System.Linq;
using UnityEngine;
@@ -7,6 +8,15 @@ namespace MiraAPI.Utilities
{
public static class Extensions
{
public static bool IsCustom(this OptionBehaviour optionBehaviour)
{
return ModdedOptionsManager.Options.Any(opt => opt.OptionBehaviour && opt.OptionBehaviour.Equals(optionBehaviour));
}
public static Color DarkenColor(this Color color)
{
return new Color(color.r - 0.3f, color.g - 0.3f, color.b - 0.3f);
}
public static void UpdateBodies(this PlayerControl playerControl, Color outlineColor, ref DeadBody target)
{
foreach (var body in Object.FindObjectsOfType<DeadBody>())
+1 -22
View File
@@ -1,7 +1,4 @@
using HarmonyLib;
using Il2CppInterop.Runtime.Injection;
using MiraAPI.Roles;
using System;
using MiraAPI.Roles;
using System.Linq;
using System.Text;
using UnityEngine;
@@ -17,24 +14,6 @@ namespace MiraAPI.Utilities
return ShipStatus.Instance.AllRooms.ToList().Find(room => room.roomArea.OverlapPoint(pos));
}
public static void RegisterType(Type type)
{
if (ClassInjector.IsTypeRegisteredInIl2Cpp(type))
{
return;
}
try
{
ClassInjector.RegisterTypeInIl2Cpp(type);
}
catch (Exception e)
{
Debug.LogError($"Failed to register {type.FullDescription()}: {e}");
}
}
public static string GetSuffix(NumberSuffixes suffix)
{
return suffix switch
+100
View File
@@ -0,0 +1,100 @@
using System.Collections.Generic;
using UnityEngine;
namespace MiraAPI.Utilities;
public static class ShaderID
{
private static readonly Dictionary<string, int> _cache = new();
public static int Get(string name)
{
if (_cache.TryGetValue(name, out var id))
{
return id;
}
id = Shader.PropertyToID(name);
_cache[name] = id;
return id;
}
// For player shader
public static readonly int _BodyColor = Shader.PropertyToID("_BodyColor");
public static readonly int _BackColor = Shader.PropertyToID("_BackColor");
public static readonly int _VisorColor = Shader.PropertyToID("_VisorColor");
// Main texture, very obviously used in any shader with a texture
public static readonly int _MainTex = Shader.PropertyToID("_MainTex");
// Any masking stuff, like MeetingHud bubbles
public static readonly int _Mask = Shader.PropertyToID("_Mask");
public static readonly int _MaskComp = Shader.PropertyToID("_MaskComp");
public static readonly int _MaskLayer = Shader.PropertyToID("_MaskLayer");
public static readonly int _Stencil = Shader.PropertyToID("_Stencil");
public static readonly int _StencilComp = Shader.PropertyToID("_StencilComp");
// Used in many tasks
public static readonly int _Color = Shader.PropertyToID("_Color");
// Has 2 uses in the game, provided for convenience
public static readonly int _Opacity = Shader.PropertyToID("_Opacity");
// Used once in CooldownHelpers, once in PowerBarMining
public static readonly int _NormalizedUvs = Shader.PropertyToID("_NormalizedUvs");
// Used in many consoles
public static readonly int _Outline = Shader.PropertyToID("_Outline");
public static readonly int _OutlineColor = Shader.PropertyToID("_OutlineColor");
// Used in some consoles
public static readonly int _AddColor = Shader.PropertyToID("_AddColor");
// Has some uses in various locations
public static readonly int _Percent = Shader.PropertyToID("_Percent");
public static readonly int _PercentY = Shader.PropertyToID("_PercentY");
public static readonly int _Desat = Shader.PropertyToID("_Desat");
// Used in LightSource
public static readonly int _PlayerRadius = Shader.PropertyToID("_PlayerRadius");
public static readonly int _LightRadius = Shader.PropertyToID("_LightRadius");
public static readonly int _LightOffset = Shader.PropertyToID("_LightOffset");
public static readonly int _FlashlightSize = Shader.PropertyToID("_FlashlightSize");
public static readonly int _FlashlightAngle = Shader.PropertyToID("_FlashlightAngle");
// Used once for LightSourceGpuRenderer
public static readonly int _DepthCompressionValue = Shader.PropertyToID("_DepthCompressionValue");
// Used in ProgressTracker
public static readonly int _Buckets = Shader.PropertyToID("_Buckets");
public static readonly int _FullBuckets = Shader.PropertyToID("_FullBuckets");
// Used once in IntroCutscene and EndGameManager
public static readonly int _Rad = Shader.PropertyToID("_Rad");
// Used only once for Quick Chat
public static readonly int _FaceColor = Shader.PropertyToID("_FaceColor");
// Used for NavigationMinigame
public static readonly int _CrossHair = Shader.PropertyToID("_CrossHair");
public static readonly int _CrossColor = Shader.PropertyToID("_CrossColor");
// Used for both SurveillanceMinigame (Planet and Normal)
public static readonly int _Center = Shader.PropertyToID("_Center");
public static readonly int _Color2 = Shader.PropertyToID("_Color2");
// Used in ReactorShipRoom
public static readonly int _Speed = Shader.PropertyToID("_Speed");
// Used only once in CourseMinigame
public static readonly int _AltTex = Shader.PropertyToID("_AltTex");
public static readonly int _Perc = Shader.PropertyToID("_Perc");
// Used once in TextMarquee
public static readonly int _VertexOffsetX = Shader.PropertyToID("_VertexOffsetX");
public static readonly int _VertexOffsetY = Shader.PropertyToID("_VertexOffsetY");
// Used in VertLineBehaviour
public static readonly int _Fade = Shader.PropertyToID("_Fade");
}