mirror of
https://github.com/All-Of-Us-Mods/MiraAPI.git
synced 2026-10-20 11:39:45 +00:00
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:
64 files changed
+1493
-1097
No files matched your search
+6
-3
@@ -1,3 +1,6 @@
|
||||
.vs
|
||||
obj
|
||||
bin
|
||||
.idea/
|
||||
.vs/
|
||||
obj/
|
||||
bin/
|
||||
|
||||
*.user
|
||||
Generated
+20
@@ -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
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,4 +1,4 @@
|
||||
using MiraAPI.Utilities;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
|
||||
@@ -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
@@ -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">
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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)];
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)(() =>
|
||||
{
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
using BepInEx.Configuration;
|
||||
|
||||
namespace MiraAPI.PluginLoading
|
||||
{
|
||||
public interface IMiraPlugin
|
||||
{
|
||||
public ConfigFile GetConfigFile();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 |
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
@@ -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,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
|
||||
{
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
@@ -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,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
|
||||
|
||||
@@ -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");
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user