diff --git a/AmongUs.props b/AmongUs.props index e0d7f3c..6f6e0f9 100644 --- a/AmongUs.props +++ b/AmongUs.props @@ -2,12 +2,12 @@ - + - + diff --git a/MiraAPI/MiraApiSettings.cs b/MiraAPI/MiraApiSettings.cs index aa4b29c..574212c 100644 --- a/MiraAPI/MiraApiSettings.cs +++ b/MiraAPI/MiraApiSettings.cs @@ -30,4 +30,10 @@ public class MiraApiSettings(ConfigFile config) : LocalSettingsTab(config) /// Gets whether to show keybinds in the control mapper. /// public ConfigEntry ShowKeybinds { get; private set; } = config.Bind("Keybinds", "Show Keybinds in Control Mapper", true); + + /// + /// Gets whether to apply cosmetic changes to the TaskAdder. + /// + [LocalToggleSetting] + public ConfigEntry PrettyTaskAdder { get; private set; } = config.Bind("Freeplay", "Pretty Task Laptop", true); } diff --git a/MiraAPI/Networking/CustomMurderRpc.cs b/MiraAPI/Networking/CustomMurderRpc.cs index 209afcd..e3005c9 100644 --- a/MiraAPI/Networking/CustomMurderRpc.cs +++ b/MiraAPI/Networking/CustomMurderRpc.cs @@ -222,7 +222,7 @@ public static class CustomMurderRpc if (createDeadBody) { - deadBody = Object.Instantiate(GameManager.Instance.DeadBodyPrefab); + deadBody = Object.Instantiate(GameManager.Instance.deadBodyPrefab[0]); deadBody.enabled = false; deadBody.ParentId = target.PlayerId; deadBody.bodyRenderers.ToList().ForEach(target.SetPlayerMaterialColors); diff --git a/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs b/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs index 7f6a8b5..59d87dd 100644 --- a/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs +++ b/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs @@ -1,15 +1,15 @@ -using System; +using System.Collections.Generic; using System.Globalization; using System.Linq; -using AmongUs.GameOptions; +using System.Text; using HarmonyLib; +using MiraAPI.LocalSettings; using MiraAPI.Modifiers; using MiraAPI.Modifiers.Types; using MiraAPI.PluginLoading; using MiraAPI.Roles; -using MiraAPI.Utilities; using MiraAPI.Utilities.Assets; -using TMPro; +using Reactor.Utilities.Extensions; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; @@ -21,6 +21,8 @@ namespace MiraAPI.Patches.Freeplay; internal static class TaskAdderPatches { private static Scroller _scroller = null!; + private static Dictionary folders = new(); + [HarmonyPostfix] [HarmonyPatch(nameof(TaskAdderGame.Begin))] public static void AddRolesFolder(TaskAdderGame __instance) @@ -60,46 +62,80 @@ internal static class TaskAdderPatches __instance.RoleButton.GetComponent().ClickMask = collider; __instance.RootFolderPrefab.GetComponent().ClickMask = collider; __instance.RootFolderPrefab.gameObject.SetActive(false); - __instance.TaskParent = inner.transform; - var rolesFolder = Object.Instantiate(__instance.RootFolderPrefab, _scroller.Inner); - rolesFolder.gameObject.SetActive(false); - rolesFolder.FolderName = "Roles"; - rolesFolder.name = "RolesFolder"; + var crewmateFolder = __instance.Root.SubFolders.ToArray().FirstOrDefault(x => x.FolderName == "Crewmate")!; + var impostorFolder = __instance.Root.SubFolders.ToArray().FirstOrDefault(x => x.FolderName == "Impostor")!; + var neutralFolder = __instance.CreateFolder("Neutral", __instance.Root, 2, Color.gray); + var modifiersFolder = __instance.CreateFolder("Modifiers", __instance.Root, 0, Color.blue); - var modifiersFolder = Object.Instantiate(__instance.RootFolderPrefab, _scroller.Inner); - modifiersFolder.gameObject.SetActive(false); - modifiersFolder.FolderName = "Modifiers"; - modifiersFolder.name = "ModifiersFolder"; + folders.Clear(); + folders.Add("Crewmate", crewmateFolder); + folders.Add("Impostor", impostorFolder); + folders.Add("Neutral", neutralFolder); + folders.Add("Modifiers", modifiersFolder); + int folderIdx = 3; foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins) { - if (plugin.InternalRoles.Any(x => x.Value is ICustomRole { Configuration.ShowInFreeplay: true })) + var pluginFolders = new Dictionary(); + + foreach (var role in plugin.InternalRoles + .Where(x => x.Value is ICustomRole { Configuration.ShowInFreeplay: true }) + .Select(x => x.Value)) { - var newFolder = Object.Instantiate(__instance.RootFolderPrefab, _scroller.Inner); - newFolder.name = plugin.PluginId+":Roles"; - newFolder.FolderName = plugin.PluginInfo.Metadata.Name; - newFolder.gameObject.SetActive(false); - rolesFolder.SubFolders.Add(newFolder); + var customRole = role as ICustomRole; + var folderName = customRole!.Configuration.FreeplayFolder; + if (!folders.TryGetValue(folderName, out var teamFolder)) + { + teamFolder = __instance.CreateFolder(folderName, __instance.Root, folderIdx++, customRole.IntroConfiguration?.IntroTeamColor ?? Color.gray); + folders.Add(folderName, teamFolder); + } + + if (!pluginFolders.TryGetValue(folderName, out var pluginFolder)) + { + pluginFolder = __instance.CreateFolder(plugin.PluginInfo.Metadata.Name, teamFolder); + pluginFolders.Add(folderName, teamFolder); + } + + pluginFolder.RoleChildren.Add(role); } if (plugin.InternalModifiers.Exists(x => x.ShowInFreeplay)) { - var newFolder = Object.Instantiate(__instance.RootFolderPrefab, _scroller.Inner); - newFolder.name = plugin.PluginId+":Modifiers"; - newFolder.FolderName = plugin.PluginInfo.Metadata.Name; - newFolder.gameObject.SetActive(false); - modifiersFolder.SubFolders.Add(newFolder); + __instance.CreateFolder(plugin.PluginInfo.Metadata.Name, modifiersFolder) + .gameObject.name = plugin.PluginId; } } - __instance.Root.SubFolders.Add(rolesFolder); - __instance.Root.SubFolders.Add(modifiersFolder); + // Modifier folder at the end + __instance.Root.SubFolders.Insert(folderIdx, modifiersFolder); __instance.GoToRoot(); } + private static TaskFolder CreateFolder(this TaskAdderGame instance, string name, TaskFolder parent, int idx = -1, Color? color = null) + { + var folder = Object.Instantiate(instance.RootFolderPrefab, instance.transform); + folder.gameObject.SetActive(false); + folder.FolderName = name; + folder.SetCustomColor(color ?? new Color(0.937f, 0.811f, 0.592f)); + folder.name = name + "Folder"; + if (idx == -1) + parent.SubFolders.Add(folder); + else if (idx > 0) + parent.SubFolders.Insert(idx, folder); + return folder; + } + + private static void SetCustomColor(this TaskFolder folder, Color color) + { + folder.currentFolderColor = color; + folder.folderSpriteRenderer.color = color; + folder.buttonRolloverHandler.OutColor = color; + folder.buttonRolloverHandler.UnselectedColor = color; + } + private static void AddFileAsChildCustom( this TaskAdderGame instance, TaskAddButton item, @@ -122,40 +158,46 @@ internal static class TaskAdderPatches instance.ActiveItems.Add(item.transform); } + private static bool IsChildOf(this TaskFolder child, TaskFolder parent) => parent.SubFolders + .ToArray() + .Any(x => x.FolderName == child.FolderName); + // yes it might be crazy patching the entire method, but i tried so many other methods and only this works :cry: + // true -chip [HarmonyPrefix] [HarmonyPatch(nameof(TaskAdderGame.ShowFolder))] public static bool ShowPatch(TaskAdderGame __instance, TaskFolder taskFolder) { - var stringBuilder = new Il2CppSystem.Text.StringBuilder(64); + StringBuilder stringBuilder = new StringBuilder(64); __instance.Hierarchy.Add(taskFolder); - for (var i = 0; i < __instance.Hierarchy.Count; i++) + foreach (var t in __instance.Hierarchy) { - stringBuilder.Append(__instance.Hierarchy.ToArray()[i].FolderName); - stringBuilder.Append("\\"); + stringBuilder.Append(t.FolderName); + stringBuilder.Append('\\'); } - __instance.PathText.text = stringBuilder.ToString(); - for (var j = 0; j < __instance.ActiveItems.Count; j++) + var prettyEnabled = LocalSettingsTabSingleton.Instance.PrettyTaskAdder.Value; + if (prettyEnabled) { - Object.Destroy(__instance.ActiveItems.ToArray()[j].gameObject); + __instance.PathText.fontSizeMin = 3; + __instance.PathText.fontSizeMax = 3; + __instance.transform.FindChild("TitleText_TMP")?.gameObject.DestroyImmediate(); } + __instance.ActiveItems.ToArray().Do(x => x.gameObject.Destroy()); __instance.ActiveItems.Clear(); - var num = 0f; - var num2 = 0f; - var num3 = 0f; - for (var k = 0; k < taskFolder.SubFolders.Count; k++) - { - var taskFolder2 = Object.Instantiate(taskFolder.SubFolders.ToArray()[k], __instance.TaskParent); - var folderTransform = taskFolder2.transform; + float num = 0f; + float num2 = 0f; + float num3 = 0f; + foreach (var t in taskFolder.SubFolders) + { + TaskFolder taskFolder2 = Object.Instantiate(t, __instance.TaskParent); taskFolder2.gameObject.SetActive(true); taskFolder2.Parent = __instance; - - folderTransform.localPosition = new Vector3(num, num2, 0f); - folderTransform.localScale = Vector3.one; - num3 = Mathf.Max(num3, taskFolder2.Text.bounds.size.y + 1.3f); + taskFolder2.transform.localPosition = new Vector3(num, num2, 0f); + taskFolder2.transform.localScale = Vector3.one; + num3 = Mathf.Max(num3, taskFolder2.Text.bounds.size.y + 1.1f); num += __instance.folderWidth; if (num > __instance.lineWidth) { @@ -164,242 +206,74 @@ internal static class TaskAdderPatches num3 = 0f; } - __instance.ActiveItems.Add(folderTransform); - if (!taskFolder2 || !taskFolder2.Button) + __instance.ActiveItems.Add(taskFolder2.transform); + if (taskFolder2 != null && taskFolder2.Button != null) { - continue; - } - - ControllerManager.Instance.AddSelectableUiElement(taskFolder2.Button); - if (!string.IsNullOrEmpty(__instance.restorePreviousSelectionByFolderName) && - taskFolder2.FolderName.Equals(__instance.restorePreviousSelectionByFolderName, StringComparison.Ordinal)) - { - __instance.restorePreviousSelectionFound = taskFolder2.Button; + ControllerManager.Instance.AddSelectableUiElement(taskFolder2.Button); } } - var flag = false; - var list = taskFolder.Children.ToArray().OrderBy(t => t.TaskType).ToList(); - - for (var l = 0; l < list.Count; l++) + var list = taskFolder.TaskChildren + .ToArray() + .OrderBy(x => x.TaskType) + .ToArray(); + foreach (var task in list) { - var taskAddButton = Object.Instantiate(__instance.TaskPrefab); - taskAddButton.MyTask = list.ToArray()[l]; - switch (taskAddButton.MyTask.TaskType) + TaskAddButton taskAddButton = Object.Instantiate(__instance.TaskPrefab); + taskAddButton.MyTask = task; + switch (task.TaskType) { case TaskTypes.DivertPower: - { - var targetSystem = taskAddButton.MyTask.Cast().TargetSystem; - taskAddButton.Text.text = TranslationController.Instance.GetString( - StringNames.DivertPowerTo, - TranslationController.Instance.GetString(targetSystem)); - break; - } + { + var targetSystem = task.Cast().TargetSystem; + taskAddButton.Text.text = TranslationController.Instance.GetString( + StringNames.DivertPowerTo, + TranslationController.Instance.GetString(targetSystem)); + break; + } case TaskTypes.FixWeatherNode: - { - var nodeId = taskAddButton.MyTask.Cast().NodeId; - taskAddButton.Text.text = - TranslationController.Instance.GetString( - StringNames.FixWeatherNode) + " " + - TranslationController.Instance.GetString( - WeatherSwitchGame.ControlNames[nodeId]); - break; - } + { + var nodeId = task.Cast().NodeId; + taskAddButton.Text.text = + TranslationController.Instance.GetString( + StringNames.FixWeatherNode) + " " + + TranslationController.Instance.GetString( + WeatherSwitchGame.ControlNames[nodeId]); + break; + } default: taskAddButton.Text.text = - TranslationController.Instance.GetString(taskAddButton.MyTask.TaskType); + TranslationController.Instance.GetString(task.TaskType); break; } __instance.AddFileAsChildCustom(taskAddButton, ref num, ref num2, ref num3); - if (taskAddButton.Button == null) + if (taskAddButton != null && taskAddButton.Button != null) { - continue; - } - - ControllerManager.Instance.AddSelectableUiElement(taskAddButton.Button); - if (__instance.Hierarchy.Count == 1 || flag) - { - continue; - } - - var component = - ControllerManager.Instance.CurrentUiState.CurrentSelection.GetComponent(); - if (component != null) - { - __instance.restorePreviousSelectionByFolderName = component.FolderName; - } - - ControllerManager.Instance.SetDefaultSelection(taskAddButton.Button); - flag = true; - } - - if (taskFolder.FolderName == "Roles") - { - for (var m = 0; m < RoleManager.Instance.AllRoles.Length; m++) - { - var roleBehaviour = RoleManager.Instance.AllRoles[m]; - if (roleBehaviour.Role == RoleTypes.ImpostorGhost || roleBehaviour.Role == RoleTypes.CrewmateGhost || - CustomRoleManager.CustomRoles.ContainsKey((ushort)roleBehaviour.Role)) - { - continue; - } - - var taskAddButton2 = Object.Instantiate(__instance.RoleButton); - taskAddButton2.SafePositionWorld = __instance.SafePositionWorld; - taskAddButton2.Text.text = "Be_" + roleBehaviour.NiceName + ".exe"; - __instance.AddFileAsChildCustom(taskAddButton2, ref num, ref num2, ref num3); - taskAddButton2.Role = roleBehaviour; - - if (taskAddButton2.Button == null) - { - continue; - } - - ControllerManager.Instance.AddSelectableUiElement(taskAddButton2.Button); - switch (m) - { - case 0 when __instance.restorePreviousSelectionFound != null: - ControllerManager.Instance.SetDefaultSelection(__instance.restorePreviousSelectionFound); - __instance.restorePreviousSelectionByFolderName = string.Empty; - __instance.restorePreviousSelectionFound = null; - break; - case 0: - ControllerManager.Instance.SetDefaultSelection(taskAddButton2.Button); - break; - } + ControllerManager.Instance.AddSelectableUiElement(taskAddButton.Button, false); } } - if (taskFolder) + var roleChildren = taskFolder.RoleChildren + .ToArray() + .Where(x => !(taskFolder.IsChildOf(__instance.Root) && x is ICustomRole)) + .ToArray(); + foreach (var role in roleChildren) { - var split = taskFolder.name.Split(':'); - if (split.Length == 2) + TaskAddButton roleAddButton = Object.Instantiate(__instance.RoleButton); + roleAddButton.SafePositionWorld = __instance.SafePositionWorld; + roleAddButton.Text.text = prettyEnabled ? role.NiceName : "Be_" + role.NiceName + ".exe"; + roleAddButton.Role = role; + if (prettyEnabled) { - var pluginId = split[0]; - var folderName = split[1]; - - if (MiraPluginManager.GetPluginByGuid(pluginId) is { } plugin) - { - if (folderName == "Roles(Clone)") - { - PluginRoles(__instance, plugin, ref num, ref num2, ref num3); - } - else if (folderName == "Modifiers(Clone)") - { - PluginModifiers(__instance, plugin, ref num, ref num2, ref num3); - } - } + roleAddButton.Text.fontSizeMin = 1; + roleAddButton.FileImage.sprite = role.IsImpostor + ? MiraAssets.ImpostorFile.LoadAsset() + : MiraAssets.CrewmateFile.LoadAsset(); } - } - - foreach (var chip in __instance.ActiveItems) - { - chip.GetComponentInChildren().EnableStencilMasking(); - chip.GetComponent().maskInteraction = SpriteMaskInteraction.VisibleInsideMask; - } - - if (_scroller) - { - _scroller.CalculateAndSetYBounds(__instance.ActiveItems.Count, 6, 4.5f, 1.65f); - _scroller.SetYBoundsMin(0.0f); - _scroller.ScrollToTop(); - } - - if (__instance.Hierarchy.Count == 1) - { - ControllerManager.Instance.SetBackButton(__instance.BackButton); - return false; - } - - ControllerManager.Instance.SetBackButton(__instance.FolderBackButton); - return false; - } - - private static void PluginModifiers( - TaskAdderGame instance, - MiraPluginInfo plugin, - ref float num, - ref float num2, - ref float num3) - { - for (var m = 0; m < plugin.InternalModifiers.Count; m++) - { - var modifier = plugin.InternalModifiers[m]; - if (!modifier.ShowInFreeplay) - { - continue; - } - - var taskAddButton = Object.Instantiate(instance.RoleButton); - taskAddButton.name = modifier.TypeId.ToString(NumberFormatInfo.InvariantInfo); - taskAddButton.role = null; - taskAddButton.MyTask = null; - taskAddButton.SafePositionWorld = instance.SafePositionWorld; - taskAddButton.Text.text = modifier.ModifierName; - taskAddButton.Text.fontSizeMin = 1; - taskAddButton.Text.EnableMasking(); - taskAddButton.FileImage.color = modifier.FreeplayFileColor; - taskAddButton.RolloverHandler.OutColor = modifier.FreeplayFileColor; - taskAddButton.Button.OnClick = new Button.ButtonClickedEvent(); - taskAddButton.Button.OnClick.AddListener((UnityEngine.Events.UnityAction)(() => - { - var id = modifier.TypeId; - if (PlayerControl.LocalPlayer.HasModifier(id)) - { - PlayerControl.LocalPlayer.RemoveModifier(id); - taskAddButton.Overlay.enabled = false; - } - else - { - PlayerControl.LocalPlayer.AddModifier(id); - taskAddButton.Overlay.enabled = true; - } - })); - - if (modifier is TimedModifier timed) - { - taskAddButton.FileImage.sprite = MiraAssets.TimedModifierFile.LoadAsset(); - taskAddButton.Text.text += $"({timed.Duration}s)"; - } - - instance.AddFileAsChildCustom(taskAddButton, ref num, ref num2, ref num3); - - ControllerManager.Instance.AddSelectableUiElement(taskAddButton.Button); - switch (m) - { - case 0 when instance.restorePreviousSelectionFound != null: - ControllerManager.Instance.SetDefaultSelection(instance.restorePreviousSelectionFound); - instance.restorePreviousSelectionByFolderName = string.Empty; - instance.restorePreviousSelectionFound = null; - break; - case 0: - ControllerManager.Instance.SetDefaultSelection(taskAddButton.Button); - break; - } - } - } - - private static void PluginRoles(TaskAdderGame instance, MiraPluginInfo plugin, ref float num, ref float num2, ref float num3) - { - for (var m = 0; m < plugin.InternalRoles.Count; m++) - { - var roleBehaviour = plugin.InternalRoles.ElementAt(m).Value; - if (roleBehaviour.Role == RoleTypes.ImpostorGhost || roleBehaviour.Role == RoleTypes.CrewmateGhost || - roleBehaviour is ICustomRole { Configuration.ShowInFreeplay: false }) - { - continue; - } - - var taskAddButton2 = Object.Instantiate(instance.RoleButton); - taskAddButton2.SafePositionWorld = instance.SafePositionWorld; - taskAddButton2.Text.text = "Be_" + roleBehaviour.NiceName + ".exe"; - taskAddButton2.Text.EnableMasking(); - - instance.AddFileAsChildCustom(taskAddButton2, ref num, ref num2, ref num3); - taskAddButton2.Role = roleBehaviour; - if (roleBehaviour is ICustomRole custom) + if (role is ICustomRole custom) { + if (prettyEnabled && custom.Team == ModdedRoleTeams.Custom) roleAddButton.FileImage.sprite = MiraAssets.CustomTeamFile.LoadAsset(); var customColor = custom.IntroConfiguration?.IntroTeamColor ?? Color.gray; if (custom.Team is ModdedRoleTeams.Crewmate) { @@ -409,26 +283,84 @@ internal static class TaskAdderPatches { customColor = Palette.ImpostorRed; } - taskAddButton2.FileImage.color = customColor; - taskAddButton2.RolloverHandler.OutColor = customColor; + roleAddButton.FileImage.color = customColor; + roleAddButton.RolloverHandler.OutColor = customColor; } - if (taskAddButton2.Button == null) + __instance.AddFileAsChildCustom(roleAddButton, ref num, ref num2, ref num3); + if (roleAddButton != null && roleAddButton.Button != null) { - continue; - } - - ControllerManager.Instance.AddSelectableUiElement(taskAddButton2.Button); - switch (m) - { - case 0 when instance.restorePreviousSelectionFound != null: - ControllerManager.Instance.SetDefaultSelection(instance.restorePreviousSelectionFound); - instance.restorePreviousSelectionByFolderName = string.Empty; - instance.restorePreviousSelectionFound = null; - break; - case 0: - ControllerManager.Instance.SetDefaultSelection(taskAddButton2.Button); - break; + ControllerManager.Instance.AddSelectableUiElement(roleAddButton.Button); } } + + if (folders.TryGetValue("Modifiers", out var modifiersFolder) && taskFolder.IsChildOf(modifiersFolder)) + { + var plugin = MiraPluginManager.GetPluginByGuid(taskFolder.gameObject.name.Replace("(Clone)", string.Empty)); + if (plugin != null) + { + foreach (var modifier in plugin.InternalModifiers) + { + if (!modifier.ShowInFreeplay) + { + continue; + } + + var taskAddButton = Object.Instantiate(__instance.RoleButton); + taskAddButton.name = modifier.TypeId.ToString(NumberFormatInfo.InvariantInfo); + taskAddButton.role = null; + taskAddButton.MyTask = null; + taskAddButton.SafePositionWorld = __instance.SafePositionWorld; + taskAddButton.Text.text = modifier.ModifierName; + taskAddButton.Text.fontSizeMin = 1; + taskAddButton.Text.EnableMasking(); + taskAddButton.FileImage.color = modifier.FreeplayFileColor; + taskAddButton.RolloverHandler.OutColor = modifier.FreeplayFileColor; + if (modifier is TimedModifier timed) + { + taskAddButton.FileImage.sprite = MiraAssets.TimedModifierFile.LoadAsset(); + taskAddButton.Text.text += $" ({timed.Duration}s)"; + } + else + { + taskAddButton.FileImage.sprite = MiraAssets.ModifierFile.LoadAsset(); + } + + taskAddButton.Button.OnClick = new Button.ButtonClickedEvent(); + var m = modifier; + taskAddButton.Button.OnClick.AddListener((UnityAction)(() => + { + var id = m.TypeId; + if (PlayerControl.LocalPlayer.HasModifier(id)) + { + PlayerControl.LocalPlayer.RemoveModifier(id); + taskAddButton.Overlay.enabled = false; + } + else + { + PlayerControl.LocalPlayer.AddModifier(id); + taskAddButton.Overlay.enabled = true; + } + })); + + __instance.AddFileAsChildCustom(taskAddButton, ref num, ref num2, ref num3); + + ControllerManager.Instance.AddSelectableUiElement(taskAddButton.Button); + } + } + } + + if (_scroller) + { + _scroller.CalculateAndSetYBounds(__instance.ActiveItems.Count, 6, 4.5f, 1.65f); + _scroller.SetYBoundsMin(0.0f); + _scroller.ScrollToTop(); + } + + ControllerManager.Instance.SetBackButton(__instance.Hierarchy.Count == 1 + ? __instance.BackButton + : __instance.FolderBackButton); + + __instance.SetPreviousControllerSelection(taskFolder.FolderName); + return false; } } diff --git a/MiraAPI/Patches/Roles/RoleManagerPatches.cs b/MiraAPI/Patches/Roles/RoleManagerPatches.cs index fa0cb5a..93daa82 100644 --- a/MiraAPI/Patches/Roles/RoleManagerPatches.cs +++ b/MiraAPI/Patches/Roles/RoleManagerPatches.cs @@ -33,7 +33,7 @@ public static class RoleManagerPatches data.Role.Deinitialize(targetPlayer); Object.Destroy(data.Role.gameObject); } - var roleBehaviour = Object.Instantiate(__instance.AllRoles.First(r => r.Role == roleType), data.gameObject.transform); + var roleBehaviour = Object.Instantiate(__instance.AllRoles.ToArray().First(r => r.Role == roleType), data.gameObject.transform); roleBehaviour.Initialize(targetPlayer); targetPlayer.Data.Role = roleBehaviour; targetPlayer.Data.RoleType = roleType; diff --git a/MiraAPI/Resources/CrewmateFile.png b/MiraAPI/Resources/CrewmateFile.png new file mode 100644 index 0000000..7cf1e74 Binary files /dev/null and b/MiraAPI/Resources/CrewmateFile.png differ diff --git a/MiraAPI/Resources/CustomFile.png b/MiraAPI/Resources/CustomFile.png new file mode 100644 index 0000000..90c076b Binary files /dev/null and b/MiraAPI/Resources/CustomFile.png differ diff --git a/MiraAPI/Resources/ImpostorFile.png b/MiraAPI/Resources/ImpostorFile.png new file mode 100644 index 0000000..bf1476b Binary files /dev/null and b/MiraAPI/Resources/ImpostorFile.png differ diff --git a/MiraAPI/Resources/ModifierFile.png b/MiraAPI/Resources/ModifierFile.png new file mode 100644 index 0000000..8d5b78d Binary files /dev/null and b/MiraAPI/Resources/ModifierFile.png differ diff --git a/MiraAPI/Resources/TimedModifierFile.png b/MiraAPI/Resources/TimedModifierFile.png index 666bd48..2909000 100644 Binary files a/MiraAPI/Resources/TimedModifierFile.png and b/MiraAPI/Resources/TimedModifierFile.png differ diff --git a/MiraAPI/Roles/CustomRoleConfiguration.cs b/MiraAPI/Roles/CustomRoleConfiguration.cs index 5b0158d..3974665 100644 --- a/MiraAPI/Roles/CustomRoleConfiguration.cs +++ b/MiraAPI/Roles/CustomRoleConfiguration.cs @@ -32,6 +32,12 @@ public record struct CustomRoleConfiguration TasksCountForProgress = role.Team is ModdedRoleTeams.Crewmate; HideSettings = roleBehaviour?.IsDead == true; ShowInFreeplay = roleBehaviour?.IsDead == false; + FreeplayFolder = role.Team switch + { + ModdedRoleTeams.Crewmate => "Crewmate", + ModdedRoleTeams.Impostor => "Impostor", + _ => "Neutral", + }; IntroSound = role.Team is ModdedRoleTeams.Crewmate ? CustomRoleManager.CrewmateIntroSound : CustomRoleManager.ImpostorIntroSound; @@ -112,6 +118,11 @@ public record struct CustomRoleConfiguration /// public bool ShowInFreeplay { get; set; } + /// + /// Gets or sets a freeplay folder this role belongs to. Defaults to team folder. + /// + public string FreeplayFolder { get; set; } + /// /// Gets or sets the outline color for the KillButton if is true. /// diff --git a/MiraAPI/Roles/CustomRoleManager.cs b/MiraAPI/Roles/CustomRoleManager.cs index 5e1c3ae..d16153b 100644 --- a/MiraAPI/Roles/CustomRoleManager.cs +++ b/MiraAPI/Roles/CustomRoleManager.cs @@ -35,6 +35,11 @@ public static class CustomRoleManager public static readonly LoadableAsset ImpostorIntroSound = CustomRoleUtils.GetIntroSound(RoleTypes.Impostor)!; + /// + /// Gets the list of all roles from vanilla RoleManager. + /// + public static RoleBehaviour[] AllRoles => RoleManager.Instance.AllRoles.ToArray(); + /// /// Gets the list of custom roles as RoleBehaviour objects. /// @@ -57,7 +62,10 @@ public static class CustomRoleManager internal static void RegisterInRoleManager() { - RoleManager.Instance.AllRoles = RoleManager.Instance.AllRoles.Concat(CustomRoles.Values).ToArray(); + foreach (var role in CustomRoles.Values) + { + RoleManager.Instance.AllRoles.Add(role); + } foreach (var role in CustomRoles.Values.Where(x => x.IsDead)) { diff --git a/MiraAPI/Roles/CustomRoleUtils.cs b/MiraAPI/Roles/CustomRoleUtils.cs index 01b402d..cb4dbfb 100644 --- a/MiraAPI/Roles/CustomRoleUtils.cs +++ b/MiraAPI/Roles/CustomRoleUtils.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Text; using AmongUs.GameOptions; using MiraAPI.Utilities.Assets; -using Reactor.Utilities; using UnityEngine; namespace MiraAPI.Roles; @@ -56,7 +55,7 @@ public static class CustomRoleUtils /// The intro sound. public static LoadableAsset? GetIntroSound(RoleTypes roleType) { - var role = RoleManager.Instance.AllRoles.FirstOrDefault(role => role.Role == roleType); + var role = CustomRoleManager.AllRoles.FirstOrDefault(role => role.Role == roleType); if (role is ICustomRole customRole) { return customRole.Configuration.IntroSound; diff --git a/MiraAPI/Utilities/Assets/MiraAssets.cs b/MiraAPI/Utilities/Assets/MiraAssets.cs index 1e39c28..b10944f 100644 --- a/MiraAPI/Utilities/Assets/MiraAssets.cs +++ b/MiraAPI/Utilities/Assets/MiraAssets.cs @@ -105,6 +105,26 @@ public static class MiraAssets /// public static LoadableResourceAsset KeybindButton { get; } = new("MiraAPI.Resources.KeybindButton.png"); + /// + /// Gets the sprite used for crewmate role files in TaskAdderGame. + /// + public static LoadableResourceAsset CrewmateFile { get; } = new("MiraAPI.Resources.CrewmateFile.png"); + + /// + /// Gets the sprite used for impostor role files in TaskAdderGame. + /// + public static LoadableResourceAsset ImpostorFile { get; } = new("MiraAPI.Resources.ImpostorFile.png"); + + /// + /// Gets the sprite used for custom team role files in TaskAdderGame. + /// + public static LoadableResourceAsset CustomTeamFile { get; } = new("MiraAPI.Resources.CustomFile.png"); + + /// + /// Gets the sprite used for modifier file in TaskAdderGame. + /// + public static LoadableResourceAsset ModifierFile { get; } = new("MiraAPI.Resources.ModifierFile.png"); + /// /// Gets the sprite used for timed modifier file in TaskAdderGame. ///