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.
///