Compare commits

..
Author SHA1 Message Date
xChipseq 834d1d878e fix 2025-09-10 22:45:48 +02:00
xChipseq 790b37e8d3 cool role file icons 2025-09-10 22:40:09 +02:00
xChipseq dfa8b29710 update task adder (and improvements) 2025-09-10 22:07:47 +02:00
xChipseq 2c0042e6a8 bump to 2025.9.9 2025-09-09 22:58:18 +02:00
xChipseq 70f63f8b16 custom role fixes 2025-09-09 22:58:02 +02:00
xChipseq 03a3733966 fix body prefab 2025-09-09 22:57:19 +02:00
23 changed files with 325 additions and 475 deletions

No files matched your search

+2 -2
View File
@@ -2,12 +2,12 @@
<ItemGroup>
<PackageReference Include="Reactor" Version="2.3.1" />
<PackageReference Include="BepInEx.Unity.IL2CPP" Version="6.0.0-be.735" Private="false" ExcludeAssets="runtime;native" />
<PackageReference Include="AmongUs.GameLibs.Steam" Version="2025.4.15" PrivateAssets="all" />
<PackageReference Include="AmongUs.GameLibs.Steam" Version="2025.9.9" 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" />
</ItemGroup>
<Target Name="Copy" AfterTargets="Build" Condition="'$(AmongUs)' != ''">
<Copy SourceFiles="$(TargetPath)" DestinationFolder="$(AmongUs)/BepInEx/plugins/" UseSymboliclinksIfPossible="true" />
</Target>
+2 -4
View File
@@ -8,7 +8,6 @@ using MiraAPI.Patches;
using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets;
using Reactor.Utilities;
using Rewired;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
@@ -76,7 +75,7 @@ public abstract class CustomActionButton
/// <summary>
/// Gets the keybind for this button. If null, no keybind will be added.
/// </summary>
public virtual BaseKeybind? Keybind => null;
public virtual MiraKeybind? Keybind => null;
/// <summary>
/// Gets the button's text outline color.
@@ -495,8 +494,7 @@ public abstract class CustomActionButton
{
KeybindText.text = Keybind.CurrentKey.ToString();
KeybindIcon?.SetActive(ActiveInputManager.currentControlType == ActiveInputManager.InputType.Keyboard &&
LocalSettingsTabSingleton<MiraApiSettings>.Instance.ShowKeybinds.Value &&
Keybind.CurrentKey != KeyboardKeyCode.None);
LocalSettingsTabSingleton<MiraApiSettings>.Instance.ShowKeybinds.Value);
}
if (Timer >= 0)
-72
View File
@@ -1,72 +0,0 @@
using System;
using Rewired;
namespace MiraAPI.Keybinds;
/// <summary>
/// Base keybind class.
/// </summary>
public class BaseKeybind
{
/// <summary>
/// Gets the Rewired <see cref="InputAction"/> assinged for this keybind.
/// </summary>
public InputAction? RewiredInputAction { get; internal set; }
/// <summary>
/// Gets the unique identifier for this keybind. Used in Rewired.
/// </summary>
public string Id { get; protected set; }
/// <summary>
/// Gets the currently assigned keycode.
/// </summary>
public KeyboardKeyCode CurrentKey => KeybindUtils.GetKeycodeByKeybind(this);
/// <summary>
/// Gets or sets the handler of the keybind. Invoked when the keybind is activated.
/// </summary>
protected Action Handler { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="BaseKeybind"/> class.
/// </summary>
/// <param name="id">The unique identifier for this keybind.</param>
public BaseKeybind(string id)
{
Id = id;
Handler = () => { };
}
public BaseKeybind()
{
Handler = () => { };
}
/// <summary>
/// Triggers the keybind, invoking its handler.
/// </summary>
public void Invoke()
{
Handler.Invoke();
}
/// <summary>
/// Adds an <see cref="Action"/> that will be invoked when the keybind is used.
/// </summary>
/// <param name="action">The <see cref="Action"/> to add.</param>
public void OnActivate(Action action)
{
Handler += action;
}
/// <summary>
/// Removes an <see cref="Action"/> that would be invoked when the keybind is used.
/// Not recommended to use - add checks in the method added in <see cref="OnActivate"/> itself rather than removing it from the keybind.
/// </summary>
/// <param name="action">The <see cref="Action"/> to remove.</param>
public void RemoveOnActivate(Action action)
{
Handler -= action;
}
}
-4
View File
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Il2CppInterop.Runtime.Attributes;
using MiraAPI.Utilities;
using Reactor.Utilities;
using Rewired;
@@ -18,9 +17,6 @@ public static class KeybindManager
/// </summary>
public static List<MiraKeybind> Keybinds { get; } = new();
[HideFromIl2Cpp]
internal static Dictionary<Type, VanillaKeybind> VanillaKeybinds { get; set; } = [];
internal static void RewiredInit()
{
try
+4 -23
View File
@@ -18,11 +18,11 @@ public static class KeybindUtils
/// </summary>
/// <param name="keybind">The keybind to get the keycode from.</param>
/// <returns>The currently assigned keycode.</returns>
public static KeyboardKeyCode GetKeycodeByKeybind(BaseKeybind keybind)
public static KeyboardKeyCode? GetKeycodeByKeybind(MiraKeybind keybind)
{
if (keybind.RewiredInputAction == null)
{
return KeyboardKeyCode.None;
return null;
}
return GetKeycodeByActionId(keybind.RewiredInputAction.id);
@@ -34,29 +34,10 @@ public static class KeybindUtils
/// <param name="actionId">The action ID.</param>
/// <returns>The keyboard keycode.</returns>
public static KeyboardKeyCode GetKeycodeByActionId(int actionId)
{
return GetActionElementMap(actionId)?.keyboardKeyCode ?? KeyboardKeyCode.None;
}
/// <summary>
/// Gets a <see cref="ActionElementMap"/> by id with ReInput.
/// </summary>
/// <param name="actionId">The action ID.</param>
/// <returns>The keyboard keycode.</returns>
public static ActionElementMap? GetActionElementMap(int actionId)
{
var player = ReInput.players.GetPlayer(0);
return player.controllers.maps.GetFirstElementMapWithAction(ControllerType.Keyboard, actionId, false);
}
/// <summary>
/// Gets an <see cref="InputAction"/> by id.
/// </summary>
/// <param name="id">The action identifier name.</param>
/// <returns>The keyboard keycode.</returns>
public static InputAction? GetInputActionById(int id)
{
return RewiredInputManager?.userData?.GetActionById(id);
return player.controllers.maps.GetFirstElementMapWithAction(ControllerType.Keyboard, actionId, false)?
.keyboardKeyCode ?? KeyboardKeyCode.None;
}
/// <summary>
+58 -7
View File
@@ -1,4 +1,6 @@
using System.Globalization;
using System;
using System.Globalization;
using Reactor.Utilities;
using Rewired;
namespace MiraAPI.Keybinds;
@@ -6,28 +8,48 @@ namespace MiraAPI.Keybinds;
/// <summary>
/// Represents a registered keybind.
/// </summary>
public class MiraKeybind : BaseKeybind
public class MiraKeybind
{
/// <summary>
/// Gets name of the keybind
/// </summary>
public string Name { get; }
/// <summary>
/// Gets the unique identifier for this keybind. Used in Rewired.
/// </summary>
public string Id { get; }
/// <summary>
/// Gets the default keycode of this keybind.
/// </summary>
public KeyboardKeyCode DefaultKey { get; }
/// <summary>
/// Gets the modifier keys assigned to this keybind.
/// Gets a value indicating whether this keybind should be checked for conflicts with other exclusive keybinds using the same key.
/// </summary>
public bool Exclusive { get; }
/// <summary>
/// Gets the modifier keys assinged to this keybind.
/// Due to Rewired limitations, there can only be 3 modifier keys.
/// </summary>
public ModifierKey[] ModifierKeys { get; }
/// <summary>
/// Gets a value indicating whether this keybind should be checked for conflicts with other exclusive keybinds using the same key.
/// Gets the Rewired <see cref="InputAction"/> assinged for this keybind.
/// </summary>
public bool Exclusive { get; }
public InputAction? RewiredInputAction { get; internal set; }
/// <summary>
/// Gets the currently assigned keycode.
/// </summary>
public KeyboardKeyCode CurrentKey => KeybindUtils.GetKeycodeByKeybind(this) ?? KeyboardKeyCode.None;
/// <summary>
/// Gets or sets the handler of the keybind. Invoked when the keybind is activated.
/// </summary>
private Action Handler { get; set; }
internal string? SourcePluginName { get; set; }
@@ -42,12 +64,14 @@ public class MiraKeybind : BaseKeybind
string name,
KeyboardKeyCode? defaultKeycode,
ModifierKey[]? modifierKeys = null,
bool exclusive = true) : base(name.ToLower(CultureInfo.InvariantCulture).Replace(' ', '_'))
bool exclusive = true)
{
Name = name;
Id = name.ToLower(CultureInfo.InvariantCulture).Replace(' ', '_');
DefaultKey = defaultKeycode ?? KeyboardKeyCode.None;
ModifierKeys = modifierKeys ?? [];
ModifierKeys = modifierKeys ?? Array.Empty<ModifierKey>();
Exclusive = exclusive;
Handler = () => { };
}
/// <inheritdoc/>
@@ -55,4 +79,31 @@ public class MiraKeybind : BaseKeybind
{
return $"{Name} ({Id})";
}
/// <summary>
/// Triggers the keybind, invoking its handler.
/// </summary>
public void Invoke()
{
Handler.Invoke();
}
/// <summary>
/// Adds an <see cref="Action"/> that will be invoked when the keybind is used.
/// </summary>
/// <param name="action">The <see cref="Action"/> to add.</param>
public void OnActivate(Action action)
{
Handler += action;
}
/// <summary>
/// Removes an <see cref="Action"/> that would be invoked when the keybind is used.
/// Not recommended to use - add checks in the method added in <see cref="OnActivate"/> itself rather than removing it from the keybind.
/// </summary>
/// <param name="action">The <see cref="Action"/> to remove.</param>
public void RemoveOnActivate(Action action)
{
Handler -= action;
}
}
-24
View File
@@ -1,24 +0,0 @@
using Rewired;
namespace MiraAPI.Keybinds;
/// <summary>
/// Represents a vanilla keybind.
/// </summary>
public class VanillaKeybind : BaseKeybind
{
/// <summary>
/// Gets the <see cref="ActionButton"/> this keybind is binded to.
/// </summary>
public ActionButton? Button { get; internal set; }
/// <summary>
/// Initializes a new instance of the <see cref="VanillaKeybind"/> class.
/// </summary>
/// <param name="id">The keybind id.</param>
public VanillaKeybind(int id)
{
RewiredInputAction = KeybindUtils.GetInputActionById(id);
Id = RewiredInputAction?.name;
}
}
@@ -1,17 +0,0 @@
namespace MiraAPI.Keybinds;
/// <summary>
/// Singleton for getting a vanilla keybind by <see cref="ActionButton"/>.
/// </summary>
/// <typeparam name="T">ActionButton type.</typeparam>
public static class VanillaKeybinding<T> where T : ActionButton
{
private static VanillaKeybind? _instance;
/// <summary>
/// Gets the instance of the option group.
/// </summary>
#pragma warning disable CA1000
public static VanillaKeybind Instance => _instance ??= KeybindManager.VanillaKeybinds[typeof(T)];
#pragma warning restore CA1000
}
+6 -19
View File
@@ -1,7 +1,6 @@
using BepInEx.Configuration;
using MiraAPI.LocalSettings;
using MiraAPI.LocalSettings.Attributes;
using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets;
namespace MiraAPI;
@@ -27,26 +26,14 @@ public class MiraApiSettings(ConfigFile config) : LocalSettingsTab(config)
[LocalToggleSetting]
public ConfigEntry<bool> ModifiersHudLeftSide { get; private set; } = config.Bind("Displays", "Show Modifiers HUD on Left Side", false);
/// <summary>
/// Gets whether vents and their connections appear on the map.
/// </summary>
[LocalToggleSetting]
public ConfigEntry<bool> ShowVentsOnMap { get; private set; } = config.Bind("Displays", "Show Vent Layout on Map", true);
/// <summary>
/// Gets whether cooldowns for buttons below 10 seconds appear with decimals.
/// </summary>
[LocalToggleSetting]
public ConfigEntry<bool> PreciseCooldowns { get; private set; } = config.Bind("Buttons", "Precise Button Cooldowns Under 10s", false);
/// <summary>
/// Gets the size of ability buttons in-game.
/// </summary>
[LocalSliderSetting(min: 0.5f, max: 1.5f, displayValue: true, formatString: "0.00", suffixType: MiraNumberSuffixes.Multiplier)]
public ConfigEntry<float> ButtonUiScale { get; private set; } = config.Bind("Buttons", "Button Scale Factor", 0.8f);
/// <summary>
/// Gets whether to show keybinds in the control mapper.
/// </summary>
public ConfigEntry<bool> ShowKeybinds { get; private set; } = config.Bind("Keybinds", "Show Keybinds in Control Mapper", true);
/// <summary>
/// Gets whether to apply cosmetic changes to the TaskAdder.
/// </summary>
[LocalToggleSetting]
public ConfigEntry<bool> PrettyTaskAdder { get; private set; } = config.Bind("Freeplay", "Pretty Task Laptop", true);
}
+1 -1
View File
@@ -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);
+207 -275
View File
@@ -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<string, TaskFolder> folders = new();
[HarmonyPostfix]
[HarmonyPatch(nameof(TaskAdderGame.Begin))]
public static void AddRolesFolder(TaskAdderGame __instance)
@@ -60,46 +62,80 @@ internal static class TaskAdderPatches
__instance.RoleButton.GetComponent<PassiveButton>().ClickMask = collider;
__instance.RootFolderPrefab.GetComponent<PassiveButton>().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<string, TaskFolder>();
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<MiraApiSettings>.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<DivertPowerTask>().TargetSystem;
taskAddButton.Text.text = TranslationController.Instance.GetString(
StringNames.DivertPowerTo,
TranslationController.Instance.GetString(targetSystem));
break;
}
{
var targetSystem = task.Cast<DivertPowerTask>().TargetSystem;
taskAddButton.Text.text = TranslationController.Instance.GetString(
StringNames.DivertPowerTo,
TranslationController.Instance.GetString(targetSystem));
break;
}
case TaskTypes.FixWeatherNode:
{
var nodeId = taskAddButton.MyTask.Cast<WeatherNodeTask>().NodeId;
taskAddButton.Text.text =
TranslationController.Instance.GetString(
StringNames.FixWeatherNode) + " " +
TranslationController.Instance.GetString(
WeatherSwitchGame.ControlNames[nodeId]);
break;
}
{
var nodeId = task.Cast<WeatherNodeTask>().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<TaskFolder>();
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<TextMeshPro>().EnableStencilMasking();
chip.GetComponent<SpriteRenderer>().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;
}
}
+3 -11
View File
@@ -99,6 +99,7 @@ public static class HudManagerPatches
gridArrange.Start();
gridArrange.ArrangeChilds();
aspectPosition.AdjustPosition();
vanillaKeybindIcons = [];
@@ -110,7 +111,6 @@ public static class HudManagerPatches
{ __instance.ReportButton.gameObject, 7 },
{ __instance.ImpostorVentButton.gameObject, 50 },
{ __instance.SabotageButton.gameObject, 4 },
{ __instance.AbilityButton.gameObject, 49 },
};
foreach (var kvp in vanillaButtons)
@@ -125,8 +125,6 @@ public static class HudManagerPatches
}
var icon = Helpers.CreateKeybindIcon(buttonObj, key, keybindIconPos);
vanillaKeybindIcons.Add(icon.transform.GetChild(0).GetComponent<TextMeshPro>(), actionId);
var comp = buttonObj.GetComponent<ActionButton>();
KeybindManager.VanillaKeybinds[comp.GetType()].Button = comp;
}
}
@@ -171,10 +169,11 @@ public static class HudManagerPatches
LocalSettingsTabSingleton<MiraApiSettings>.Instance.ShowKeybinds.Value);
}
var player = ReInput.players.GetPlayer(0);
foreach (var entry in KeybindManager.Keybinds)
{
var player = ReInput.players.GetPlayer(0);
var keyboard = player.controllers.Keyboard;
bool modifierKeysPressed = entry.ModifierKeys.All(k => keyboard.GetModifierKey(k)) ||
entry.ModifierKeys.Length <= 0;
if (player.GetButtonDown(entry.Id) && modifierKeysPressed)
@@ -182,12 +181,5 @@ public static class HudManagerPatches
entry.Invoke();
}
}
foreach (var entry in KeybindManager.VanillaKeybinds.Values)
{
if (player.GetButtonDown(entry.Id))
{
entry.Invoke();
}
}
}
}
-12
View File
@@ -1,6 +1,5 @@
using HarmonyLib;
using MiraAPI.Keybinds;
using MonoMod.Utils;
using Rewired;
namespace MiraAPI.Patches.Keybinds;
@@ -21,16 +20,5 @@ public static class KeybindMenuPatch
KeybindUtils.RewiredInputManager = __instance;
KeybindManager.RewiredInit();
_registered = true;
// Set these here right after input manager initializes
KeybindManager.VanillaKeybinds = new()
{
{ typeof(KillButton), new VanillaKeybind(8) },
{ typeof(UseButton), new VanillaKeybind(6) },
{ typeof(ReportButton), new VanillaKeybind(7) },
{ typeof(VentButton), new VanillaKeybind(50) },
{ typeof(SabotageButton), new VanillaKeybind(4) },
{ typeof(AbilityButton), new VanillaKeybind(49) },
};
}
}
+1 -1
View File
@@ -33,7 +33,7 @@ public static class RoleManagerPatches
data.Role.Deinitialize(targetPlayer);
Object.Destroy(data.Role.gameObject);
}
var roleBehaviour = Object.Instantiate<RoleBehaviour>(__instance.AllRoles.First(r => r.Role == roleType), data.gameObject.transform);
var roleBehaviour = Object.Instantiate<RoleBehaviour>(__instance.AllRoles.ToArray().First(r => r.Role == roleType), data.gameObject.transform);
roleBehaviour.Initialize(targetPlayer);
targetPlayer.Data.Role = roleBehaviour;
targetPlayer.Data.RoleType = roleType;
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

+11
View File
@@ -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
/// </summary>
public bool ShowInFreeplay { get; set; }
/// <summary>
/// Gets or sets a freeplay folder this role belongs to. Defaults to team folder.
/// </summary>
public string FreeplayFolder { get; set; }
/// <summary>
/// Gets or sets the outline color for the KillButton if <see cref="UseVanillaKillButton"/> is true.
/// </summary>
+9 -1
View File
@@ -35,6 +35,11 @@ public static class CustomRoleManager
public static readonly LoadableAsset<AudioClip> ImpostorIntroSound =
CustomRoleUtils.GetIntroSound(RoleTypes.Impostor)!;
/// <summary>
/// Gets the list of all roles from vanilla RoleManager.
/// </summary>
public static RoleBehaviour[] AllRoles => RoleManager.Instance.AllRoles.ToArray();
/// <summary>
/// Gets the list of custom roles as RoleBehaviour objects.
/// </summary>
@@ -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))
{
+1 -2
View File
@@ -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
/// <returns>The intro sound.</returns>
public static LoadableAsset<AudioClip>? 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;
+20
View File
@@ -105,6 +105,26 @@ public static class MiraAssets
/// </summary>
public static LoadableResourceAsset KeybindButton { get; } = new("MiraAPI.Resources.KeybindButton.png");
/// <summary>
/// Gets the sprite used for crewmate role files in TaskAdderGame.
/// </summary>
public static LoadableResourceAsset CrewmateFile { get; } = new("MiraAPI.Resources.CrewmateFile.png");
/// <summary>
/// Gets the sprite used for impostor role files in TaskAdderGame.
/// </summary>
public static LoadableResourceAsset ImpostorFile { get; } = new("MiraAPI.Resources.ImpostorFile.png");
/// <summary>
/// Gets the sprite used for custom team role files in TaskAdderGame.
/// </summary>
public static LoadableResourceAsset CustomTeamFile { get; } = new("MiraAPI.Resources.CustomFile.png");
/// <summary>
/// Gets the sprite used for modifier file in TaskAdderGame.
/// </summary>
public static LoadableResourceAsset ModifierFile { get; } = new("MiraAPI.Resources.ModifierFile.png");
/// <summary>
/// Gets the sprite used for timed modifier file in TaskAdderGame.
/// </summary>