Compare commits

..
Author SHA1 Message Date
Atony 0aa4a82d48 Add options to list (no logic)
Issue is mainly the button scale, that needs to update as soon as the change is made
2025-09-09 21:50:45 -05:00
113 changed files with 644 additions and 3001 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.10.14" PrivateAssets="all" />
<PackageReference Include="AmongUs.GameLibs.Steam" Version="2025.4.15" 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>
+1 -1
View File
@@ -8,7 +8,7 @@
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<VersionPrefix>0.3.6</VersionPrefix>
<VersionPrefix>0.2.1</VersionPrefix>
<VersionSuffix>dev</VersionSuffix>
<Authors>All Of Us, XtraCube, Angxl</Authors>
+2 -18
View File
@@ -1,5 +1,4 @@
using System.Globalization;
using System.Linq;
using System.Linq;
using MiraAPI.Events;
using MiraAPI.Events.Mira;
using MiraAPI.Events.Vanilla.Gameplay;
@@ -18,7 +17,7 @@ public static class ExampleEventHandlers
{
public static void Initialize()
{
// You can register event handlers with the MiraEventManager class
// You can register event handlers with the MiraEventManager class.
var handle = MiraEventManager.RegisterEventHandler<BeforeMurderEvent>(@event =>
{
Logger<ExamplePlugin>.Info($"{@event.Source.Data.PlayerName} is about to kill {@event.Target.Data.PlayerName}");
@@ -38,21 +37,6 @@ public static class ExampleEventHandlers
MiraEventManager.UnregisterEventHandler(handle);
}
// If you want to check vanilla buttons, use something like this!
[RegisterEvent]
public static void VanillaButtonClickHandler(VanillaButtonClickEvent @event)
{
Logger<ExamplePlugin>.Info($"{@event.Button.buttonLabelText.text} is being used!");
if (@event.PlayerTarget != null)
{
Logger<ExamplePlugin>.Info($"{@event.PlayerTarget.Data.PlayerName} is being targeted!");
}
else if (@event.VentTarget != null)
{
Logger<ExamplePlugin>.Info($"Vent with id {@event.VentTarget.Id.ToString(CultureInfo.InvariantCulture)} is being targeted!");
}
}
// If you want to add extra votes to a player, do something like this.
[RegisterEvent]
public static void StartMeetingEvent(StartMeetingEvent _)
+1 -3
View File
@@ -1,5 +1,4 @@
using Il2CppInterop.Runtime.Attributes;
using MiraAPI.Example.GameOver;
using MiraAPI.Example.GameOver;
using MiraAPI.GameEnd;
using MiraAPI.Roles;
using UnityEngine;
@@ -23,7 +22,6 @@ public class NeutralKillerRole : ImpostorRole, ICustomRole
public RoleOptionsGroup RoleOptionsGroup { get; } = new("Outcast", Color.gray);
[HideFromIl2Cpp]
public TeamIntroConfiguration? IntroConfiguration { get; } = new(
Color.gray,
"OUTCAST",
+2 -2
View File
@@ -34,7 +34,7 @@ public static class MiraEventManager
}
catch (Exception ex)
{
Error($"Error invoking event handler for {typeof(T).Name}: {ex.ToString()}");
Logger<MiraApiPlugin>.Error($"Error invoking event handler for {typeof(T).Name}: {ex.ToString()}");
}
}
@@ -63,7 +63,7 @@ public static class MiraEventManager
}
catch (Exception ex)
{
Error($"Error invoking event handler for {type.Name}: {ex.ToString()}");
Logger<MiraApiPlugin>.Error($"Error invoking event handler for {type.Name}: {ex.ToString()}");
}
}
@@ -1,44 +0,0 @@
using MiraAPI.Hud;
namespace MiraAPI.Events.Vanilla.Gameplay;
/// <summary>
/// Invoked when a Vanilla Button click is cancelled. Do not use for custom buttons.
/// </summary>
/// <typeparam name="T">The Vanilla Button type.</typeparam>
public sealed class VanillaButtonCancelledEvent<T> : MiraEvent where T : AbilityButton
{
/// <summary>
/// Gets Vanilla Button whose click was cancelled.
/// </summary>
public T Button { get; }
/// <summary>
/// Initializes a new instance of the <see cref="VanillaButtonCancelledEvent{T}"/> class.
/// </summary>
/// <param name="button">The Mira Button whose click was cancelled.</param>
public VanillaButtonCancelledEvent(T button)
{
Button = button;
}
}
/// <summary>
/// Invoked when a Vanilla Button click is cancelled. Do not use for custom buttons.
/// </summary>
public sealed class VanillaButtonCancelledEvent : MiraEvent
{
/// <summary>
/// Gets Vanilla Button whose click was cancelled.
/// </summary>
public AbilityButton Button { get; }
/// <summary>
/// Initializes a new instance of the <see cref="VanillaButtonCancelledEvent"/> class.
/// </summary>
/// <param name="button">The Vanilla Button whose click was cancelled.</param>
public VanillaButtonCancelledEvent(AbilityButton button)
{
Button = button;
}
}
@@ -1,65 +0,0 @@
using MiraAPI.Hud;
namespace MiraAPI.Events.Vanilla.Gameplay;
/// <summary>
/// Button click event for Vanilla Buttons only. Do not use for custom buttons.
/// </summary>
/// <typeparam name="T">The Vanilla Button type.</typeparam>
public sealed class VanillaButtonClickEvent<T> : MiraCancelableEvent where T : AbilityButton
{
/// <summary>
/// Gets Vanilla Button that was clicked.
/// </summary>
public T Button { get; }
/// <summary>
/// Gets the generic click event that is invoked before this button-specific event.
/// </summary>
public VanillaButtonClickEvent GenericClickEvent { get; }
/// <summary>
/// Initializes a new instance of the <see cref="VanillaButtonClickEvent{T}"/> class.
/// </summary>
/// <param name="button">The Mira Button that was clicked.</param>
/// <param name="genericClickEvent">The generic click event invoked before button-specific events.</param>
public VanillaButtonClickEvent(T button, VanillaButtonClickEvent genericClickEvent)
{
GenericClickEvent = genericClickEvent;
Button = button;
}
}
/// <summary>
/// Button click event for Vanilla Buttons only. Do not use for custom buttons.
/// </summary>
public sealed class VanillaButtonClickEvent : MiraCancelableEvent
{
/// <summary>
/// Gets Vanilla Button that was clicked.
/// </summary>
public AbilityButton Button { get; }
/// <summary>
/// Gets the target player, if any, of the Vanilla Button that was clicked.
/// </summary>
public PlayerControl? PlayerTarget { get; }
/// <summary>
/// Gets the target vent, if any, of the Vanilla Button that was clicked.
/// </summary>
public Vent? VentTarget { get; }
/// <summary>
/// Initializes a new instance of the <see cref="VanillaButtonClickEvent"/> class.
/// </summary>
/// <param name="button">The Vanilla Button that was clicked.</param>
/// <param name="playerTarget">The Player target, if any.</param>
/// <param name="ventTarget">The Vent target, if any.</param>
public VanillaButtonClickEvent(AbilityButton button, PlayerControl? playerTarget = null, Vent? ventTarget = null)
{
Button = button;
PlayerTarget = playerTarget;
VentTarget = ventTarget;
}
}
@@ -1,35 +0,0 @@
namespace MiraAPI.Events.Vanilla.Player;
/// <summary>
/// The event that is invoked when a player completes a task within Hide and Seek. Non-cancelable.
/// </summary>
public class CompleteHnsTaskEvent : MiraEvent
{
/// <summary>
/// Gets the instance of the <see cref="PlayerControl"/>.
/// </summary>
public PlayerControl Player { get; }
/// <summary>
/// Gets the instance of the <see cref="PlayerTask"/> that the player completed.
/// </summary>
public PlayerTask Task { get; }
/// <summary>
/// Gets the instance of the <see cref="HnsManager"/> attached to the game.
/// </summary>
public HideAndSeekManager HnsManager { get; }
/// <summary>
/// Initializes a new instance of the <see cref="CompleteHnsTaskEvent"/> class.
/// </summary>
/// <param name="hnsManager">The Hide and Seek Manager currently active.</param>
/// <param name="player">The player who completed the task.</param>
/// <param name="task">The task that the player completed.</param>
public CompleteHnsTaskEvent(HideAndSeekManager hnsManager, PlayerControl player, PlayerTask task)
{
HnsManager = hnsManager;
Player = player;
Task = task;
}
}
+2 -2
View File
@@ -30,13 +30,13 @@ public static class GameOverManager
if (gameOverType.IsAbstract)
{
Error("The type must not be abstract.");
Logger<MiraApiPlugin>.Error("The type must not be abstract.");
return false;
}
if (gameOverType.GetConstructor(Type.EmptyTypes) == null)
{
Error("The type must have a parameterless constructor.");
Logger<MiraApiPlugin>.Error("The type must have a parameterless constructor.");
return false;
}
+4 -4
View File
@@ -37,7 +37,7 @@ public static class CustomGameModeManager
return;
}
Error($"No gamemode with id {id} found!");
Logger<MiraApiPlugin>.Error($"No gamemode with id {id} found!");
}
/// <summary>
@@ -48,7 +48,7 @@ public static class CustomGameModeManager
{
if (!typeof(CustomGameMode).IsAssignableFrom(gameModeType))
{
Warning($"{gameModeType.Name} does not inherit CustomGameMode!");
Logger<MiraApiPlugin>.Warning($"{gameModeType.Name} does not inherit CustomGameMode!");
return;
}
@@ -56,13 +56,13 @@ public static class CustomGameModeManager
if (modeObj is not CustomGameMode gameMode)
{
Error($"Failed to create instance of {gameModeType.Name}");
Logger<MiraApiPlugin>.Error($"Failed to create instance of {gameModeType.Name}");
return;
}
if (GameModes.Any(x => x.Key == gameMode.Id))
{
Error($"ID for gamemode {gameMode.Name} already exists!");
Logger<MiraApiPlugin>.Error($"ID for gamemode {gameMode.Name} already exists!");
return;
}
+1 -36
View File
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using MiraAPI.Modifiers;
using UnityEngine;
namespace MiraAPI.GameOptions;
@@ -29,16 +28,11 @@ public abstract class AbstractOptionGroup
public virtual Type? OptionableType => null;
/// <summary>
/// Gets a value indicating whether the group should be shown in the modifiers menu. This is deprecated, please use ParentMenu!
/// Gets a value indicating whether the group should be shown in the modifiers menu.
/// </summary>
// TODO: make this not a boolean
public virtual bool ShowInModifiersMenu => false;
/// <summary>
/// Gets a value indicating which menu the group is in.
/// </summary>
public virtual MenuCategory ParentMenu => MenuCategory.Roles;
/// <summary>
/// Gets the function that determines whether the group should be visible or not.
/// </summary>
@@ -68,33 +62,4 @@ public abstract class AbstractOptionGroup<T> : AbstractOptionGroup where T : IOp
{
/// <inheritdoc />
public override Type OptionableType => typeof(T);
/// <summary>
/// Gets a value indicating which menu the group is in.
/// </summary>
public override MenuCategory ParentMenu
{
get
{
var type = typeof(T);
if (type == typeof(BaseModifier))
{
return MenuCategory.Modifiers;
}
else if (type == typeof(RoleBehaviour))
{
return MenuCategory.Roles;
}
return MenuCategory.Game;
}
}
}
public enum MenuCategory
{
Game,
Roles,
Modifiers,
CustomOne,
CustomTwo
}
@@ -21,15 +21,7 @@ public class ModdedNumberOptionAttribute(
{
internal override IModdedOption CreateOption(object? value, PropertyInfo property)
{
return new ModdedNumberOption(
Title,
(float)(value ?? min + increment),
min,
max,
increment,
suffixType,
formatString,
zeroInfinity);
return new ModdedNumberOption(Title, (float)(value ?? min+increment), min, max, increment, suffixType, formatString, zeroInfinity);
}
/// <inheritdoc />
+2 -2
View File
@@ -17,9 +17,9 @@ public interface IModdedOption
uint Id { get; }
/// <summary>
/// Gets or sets the title of the option.
/// Gets the title of the option.
/// </summary>
string Title { get; set; }
string Title { get; }
/// <summary>
/// Gets the StringName for the option, used for localization.
@@ -1,40 +0,0 @@
using System;
using Il2CppInterop.Runtime.Attributes;
using MiraAPI.GameOptions.OptionTypes;
using Reactor.Utilities.Attributes;
using UnityEngine;
namespace MiraAPI.GameOptions;
/// <summary>
/// Component added onto options generated via MiraAPI. This allows developers to hook patches onto options in general, without using a list.
/// </summary>
[RegisterInIl2Cpp]
public class MiraNumberOptionComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
{
/// <summary>
/// Gets or sets the modded option associated with the object.
/// </summary>
[HideFromIl2Cpp]
public ModdedNumberOption NumberOption { get; set; }
/// <summary>
/// Gets or sets the default increment.
/// </summary>
public float DefaultIncrement { get; set; }
/// <summary>
/// Gets or sets a value indicating whether holding shift will split the increment in half.
/// </summary>
public bool ShiftIncrementToggle { get; set; }
/// <summary>
/// Gets or sets a value indicating what Zero displays.
/// </summary>
public string ZeroValue { get; set; } = "#";
/// <summary>
/// Gets or sets a value indicating what Negative One displays.
/// </summary>
public string NegativeValue { get; set; } = "#";
}
+6 -6
View File
@@ -39,7 +39,7 @@ public static class ModdedOptionsManager
if (TypeToGroup.ContainsKey(type))
{
Error($"Group {type.Name} already exists.");
Logger<MiraApiPlugin>.Error($"Group {type.Name} already exists.");
return false;
}
@@ -60,13 +60,13 @@ public static class ModdedOptionsManager
{
if (!TypeToGroup.TryGetValue(type, out var group))
{
Error($"Failed to get group for {type.Name}");
Logger<MiraApiPlugin>.Error($"Failed to get group for {type.Name}");
return;
}
if (property.GetValue(group) is not IModdedOption option)
{
Error($"Failed to get option for {property.Name}");
Logger<MiraApiPlugin>.Error($"Failed to get option for {property.Name}");
return;
}
@@ -81,13 +81,13 @@ public static class ModdedOptionsManager
{
if (OptionAttributes.ContainsKey(property))
{
Error($"Property {property.Name} already has an attribute registered.");
Logger<MiraApiPlugin>.Error($"Property {property.Name} already has an attribute registered.");
return;
}
if (!TypeToGroup.TryGetValue(type, out var group))
{
Error($"Failed to get group for {type.Name}");
Logger<MiraApiPlugin>.Error($"Failed to get group for {type.Name}");
return;
}
@@ -95,7 +95,7 @@ public static class ModdedOptionsManager
if (option == null)
{
Error($"Failed to get option for {property.Name}");
Logger<MiraApiPlugin>.Error($"Failed to get option for {property.Name}");
return;
}
@@ -47,8 +47,6 @@ public class ModdedEnumOption : ModdedOption<int>
public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, PlayerOption playerOpt, Transform container)
{
var stringOption = Object.Instantiate(stringOpt, container);
stringOption.name =
$"{ParentMod!.OptionsTitleText}.EnumOption.{TranslationController.Instance.GetString(StringName)}";
stringOption.SetUpFromData(Data, 20);
stringOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged;
@@ -140,8 +138,6 @@ public class ModdedEnumOption<T> : ModdedOption<T> where T : Enum
public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, PlayerOption playerOpt, Transform container)
{
var stringOption = Object.Instantiate(stringOpt, container);
stringOption.name =
$"{ParentMod!.OptionsTitleText}.EnumOption.{TranslationController.Instance.GetString(StringName)}";
stringOption.SetUpFromData(Data, 20);
stringOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged;
@@ -32,82 +32,10 @@ public class ModdedNumberOption : ModdedOption<float>
/// </summary>
public MiraNumberSuffixes SuffixType { get; }
/// <summary>
/// Gets a value indicating what zero appears as.
/// </summary>
public string ZeroWordValue { get; }
/// <summary>
/// Gets a value indicating what a negative one appears as.
/// </summary>
public string NegativeWordValue { get; }
/// <summary>
/// Gets a value indicating whether zero is infinity.
/// </summary>
public bool ZeroInfinity => ZeroWordValue == "∞";
/// <summary>
/// Gets a value indicating whether holding shift will divide the increment.
/// </summary>
public bool ShiftIncrement { get; }
/// <summary>
/// Initializes a new instance of the <see cref="ModdedNumberOption"/> class.
/// </summary>
/// <param name="title">The title of the option.</param>
/// <param name="defaultValue">The default value as a float.</param>
/// <param name="min">The minimum value.</param>
/// <param name="max">The maximum value.</param>
/// <param name="increment">The increment.</param>
/// <param name="suffixType">The suffix type.</param>
/// <param name="formatString">Optional format string for the option screen.</param>
/// <param name="zeroBehavior">Determines what is shown for zero. Options: ∞, #, or a word value (such as Off).</param>
/// <param name="negativeBehavior">Determines what is shown for negative 1. Options: ∞, #, or a word value (such as Off).</param>
/// <param name="halfIncrements">Whether increments can be split in half.</param>
/// <param name="includeInPreset">Whether to include this option in the preset or not.</param>
public ModdedNumberOption(
string title,
float defaultValue,
float min,
float max,
float increment,
string zeroBehavior,
string negativeBehavior,
MiraNumberSuffixes suffixType,
string? formatString = null,
bool halfIncrements = false,
bool includeInPreset = true) : base(title, defaultValue, includeInPreset)
{
Min = min;
Max = max;
Increment = increment;
SuffixType = suffixType;
ZeroWordValue = zeroBehavior;
NegativeWordValue = negativeBehavior;
ShiftIncrement = halfIncrements;
Value = Mathf.Clamp(defaultValue, min, max);
Data = ScriptableObject.CreateInstance<FloatGameSetting>();
var data = (FloatGameSetting)Data;
data.Type = global::OptionTypes.Float;
data.Title = StringName;
data.Value = Value;
data.Increment = Increment;
data.ValidRange = new FloatRange(Min, Max);
data.FormatString = formatString ??
(defaultValue.IsInteger() &&
Increment.IsInteger() &&
Value.IsInteger() &&
Min.IsInteger() &&
Max.IsInteger() ? "0" : "0.0");
data.ZeroIsInfinity = ZeroInfinity;
data.SuffixType = (NumberSuffixes)SuffixType;
data.OptionName = FloatOptionNames.Invalid;
}
public bool ZeroInfinity { get; }
/// <summary>
/// Initializes a new instance of the <see cref="ModdedNumberOption"/> class.
@@ -136,9 +64,7 @@ public class ModdedNumberOption : ModdedOption<float>
Max = max;
Increment = increment;
SuffixType = suffixType;
ZeroWordValue = zeroInfinity ? "∞" : "#";
NegativeWordValue = "#";
ShiftIncrement = increment != 1f;
ZeroInfinity = zeroInfinity;
Value = Mathf.Clamp(defaultValue, min, max);
@@ -171,14 +97,6 @@ public class ModdedNumberOption : ModdedOption<float>
Transform container)
{
var numberOption = Object.Instantiate(numberOpt, Vector3.zero, Quaternion.identity, container);
numberOption.name =
$"{ParentMod!.OptionsTitleText}.NumberOption.{TranslationController.Instance.GetString(StringName)}";
var optionComponent = numberOption.gameObject.AddComponent<MiraNumberOptionComponent>();
optionComponent.NumberOption = this;
optionComponent.DefaultIncrement = Increment;
optionComponent.ShiftIncrementToggle = ShiftIncrement;
optionComponent.NegativeValue = NegativeWordValue;
optionComponent.ZeroValue = ZeroWordValue;
numberOption.SetUpFromData(Data, 20);
numberOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged;
@@ -21,7 +21,7 @@ public abstract class ModdedOption<T> : IModdedOption
public uint Id { get; }
/// <inheritdoc />
public string Title { get; set; }
public string Title { get; }
/// <inheritdoc />
public StringNames StringName { get; }
@@ -125,7 +125,7 @@ public abstract class ModdedOption<T> : IModdedOption
{
if (ConfigDefinition is null)
{
Error($"Attempted to save {Title} to preset, but ConfigDefinition is null.");
Logger<MiraApiPlugin>.Error($"Attempted to save {Title} to preset, but ConfigDefinition is null.");
return;
}
Bind(presetConfig);
@@ -147,7 +147,7 @@ public abstract class ModdedOption<T> : IModdedOption
}
else
{
Error($"Attempted to load {Title} from preset, but ConfigDefinition is not found in preset.");
Logger<MiraApiPlugin>.Error($"Attempted to load {Title} from preset, but ConfigDefinition is not found in preset.");
}
}
@@ -55,8 +55,6 @@ public class ModdedPlayerOption : ModdedOption<int>
Transform container)
{
var playerOption = Object.Instantiate(playerOpt, container);
playerOption.name =
$"{ParentMod!.OptionsTitleText}.PlayerOption.{TranslationController.Instance.GetString(StringName)}";
var filteredList = GetFilteredPlayers();
Values = filteredList.ToIl2CppList();
@@ -29,8 +29,6 @@ public class ModdedToggleOption : ModdedOption<bool>
public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, PlayerOption playerOpt, Transform container)
{
var toggleOption = Object.Instantiate(toggleOpt, Vector3.zero, Quaternion.identity, container);
toggleOption.name =
$"{ParentMod!.OptionsTitleText}.ToggleOption.{TranslationController.Instance.GetString(StringName)}";
toggleOption.SetUpFromData(Data, 20);
+8 -46
View File
@@ -64,14 +64,9 @@ public abstract class CustomActionButton
public virtual float EffectDuration => 0;
/// <summary>
/// Gets a value indicating whether the button has limited uses.
/// Gets the maximum amount of uses the button has. If the button has infinite uses, set to 0.
/// </summary>
public bool LimitedUses => ZeroIsInfinite ? MaxUses > 0 : MaxUses >= 0;
/// <summary>
/// Gets the maximum number of uses the button has. If the button has infinite uses, set to 0 or -1 based on what value that ZeroIsInfinite is set to.
/// </summary>
public virtual int MaxUses => ZeroIsInfinite ? 0 : -1;
public virtual int MaxUses => 0;
/// <summary>
/// Gets the value indicating uses mode.
@@ -94,9 +89,9 @@ public abstract class CustomActionButton
public virtual ButtonLocation Location { get; set; } = ButtonLocation.BottomLeft;
/// <summary>
/// Gets or sets a value indicating whether limited uses are determined via zero or a negative number of uses.
/// Gets a value indicating whether the button has limited uses.
/// </summary>
public virtual bool ZeroIsInfinite { get; set; } = true;
public bool LimitedUses => MaxUses > 0;
/// <summary>
/// Gets or sets a value indicating whether the effect is currently active, if there is one.
@@ -211,39 +206,7 @@ public abstract class CustomActionButton
{
if (Enabled(PlayerControl.LocalPlayer.Data.Role))
{
// Invoke the generic button click event.
var genericEvent = new MiraButtonClickEvent(this);
MiraEventManager.InvokeEvent(genericEvent);
if (genericEvent.IsCancelled)
{
MiraEventManager.InvokeEvent(new MiraButtonCancelledEvent(this));
}
// Invoke the button click event for specific button.
var eventType = CustomButtonManager.ButtonEventTypes[GetType()];
var @event = (MiraCancelableEvent)Activator.CreateInstance(eventType, this, genericEvent)!;
var specificInvoked = MiraEventManager.InvokeEvent(@event, eventType);
if (@event.IsCancelled)
{
var cancelEventType = CustomButtonManager.ButtonCancelledEventTypes[GetType()];
var cancelEvent = (MiraEvent)Activator.CreateInstance(cancelEventType, this)!;
MiraEventManager.InvokeEvent(cancelEvent, cancelEventType);
}
if (specificInvoked)
{
if (!@event.IsCancelled)
{
ClickHandler();
}
}
else
{
if (!genericEvent.IsCancelled)
{
ClickHandler();
}
}
ClickHandler();
}
});
@@ -251,10 +214,9 @@ public abstract class CustomActionButton
Helpers.CreateKeybindIcon(
Button.gameObject,
Keybind.CurrentKey,
new Vector3(0.4f, 0.45f, -9f)
new Vector3(MaxUses <= 0 ? -0.4f : 0.4f, 0.45f, -9f)
);
KeybindText = KeybindIcon.transform.GetChild(0).GetComponent<TextMeshPro>();
HudManagerPatches.ModdedKeybindIcons.Add(KeybindText);
Button.usesRemainingSprite.transform.localPosition = new(-0.341f, 0.45f, -0.1f);
}
}
@@ -271,7 +233,7 @@ public abstract class CustomActionButton
return;
}
Location = location;
this.Location = location;
if (!moveButton)
{
@@ -532,7 +494,7 @@ public abstract class CustomActionButton
if (Keybind != null && KeybindText != null)
{
KeybindText.text = Keybind.CurrentKey.ToString();
KeybindIcon?.SetActive(ActiveInputManager.currentControlType is ActiveInputManager.InputType.Keyboard &&
KeybindIcon?.SetActive(ActiveInputManager.currentControlType == ActiveInputManager.InputType.Keyboard &&
LocalSettingsTabSingleton<MiraApiSettings>.Instance.ShowKeybinds.Value &&
Keybind.CurrentKey != KeyboardKeyCode.None);
}
+1 -1
View File
@@ -69,4 +69,4 @@ public class BaseKeybind
{
Handler -= action;
}
}
}
+3 -3
View File
@@ -26,11 +26,11 @@ public static class KeybindManager
try
{
var instance = KeybindUtils.RewiredInputManager!;
foreach (var keybind in Keybinds)
foreach (var keybind in KeybindManager.Keybinds)
{
if (instance.userData.actions.ToArray().Any(x => x.name == keybind.Id))
{
Warning($"Keybind of id {keybind.Id} already exists. Skipping it");
Logger<MiraApiPlugin>.Warning($"Keybind of id {keybind.Id} already exists. Skipping it");
continue;
}
@@ -44,7 +44,7 @@ public static class KeybindManager
}
catch (Exception e)
{
Error($"Error while registering keybinds in Rewired: {e}");
Logger<MiraApiPlugin>.Error($"Error while registering keybinds in Rewired: {e}");
}
}
+1 -1
View File
@@ -9,7 +9,7 @@ namespace MiraAPI.Keybinds;
public class MiraKeybind : BaseKeybind
{
/// <summary>
/// Gets name of the keybind.
/// Gets name of the keybind
/// </summary>
public string Name { get; }
+1 -1
View File
@@ -19,6 +19,6 @@ public class VanillaKeybind : BaseKeybind
public VanillaKeybind(int id)
{
RewiredInputAction = KeybindUtils.GetInputActionById(id);
Id = RewiredInputAction != null ? RewiredInputAction.name : id.ToString();
Id = RewiredInputAction?.name;
}
}
@@ -20,7 +20,7 @@ public class LocalEnumSettingAttribute(
private readonly string? _description = description;
/// <inheritdoc/>
public override LocalEnumSetting CreateSetting(Type tab, ConfigEntryBase configEntryBase)
internal override LocalEnumSetting CreateSetting(Type tab, ConfigEntryBase configEntryBase)
{
return new LocalEnumSetting(tab, configEntryBase, configEntryBase.SettingType, _name, _description, names);
}
@@ -29,7 +29,7 @@ public class LocalNumberSettingAttribute(
private readonly string? _description = description;
/// <inheritdoc/>
public override LocalNumberSetting CreateSetting(Type tab, ConfigEntryBase configEntryBase)
internal override LocalNumberSetting CreateSetting(Type tab, ConfigEntryBase configEntryBase)
{
return new LocalNumberSetting(tab, configEntryBase, _name, _description, new FloatRange(min, max), increment, suffixType, formatString);
}
@@ -20,5 +20,5 @@ public abstract class LocalSettingAttribute(
/// <param name="tab">Gets the tab where the setting is located.</param>
/// <param name="configEntryBase">Gets the config entry the setting is attached to.</param>
/// <returns>The created class.</returns>
public abstract ILocalSetting CreateSetting(Type tab, ConfigEntryBase configEntryBase);
internal abstract ILocalSetting CreateSetting(Type tab, ConfigEntryBase configEntryBase);
}
@@ -29,7 +29,7 @@ public class LocalSliderSettingAttribute(
private readonly string? _description = description;
/// <inheritdoc/>
public override LocalSliderSetting CreateSetting(Type tab, ConfigEntryBase configEntryBase)
internal override LocalSliderSetting CreateSetting(Type tab, ConfigEntryBase configEntryBase)
{
return new LocalSliderSetting(tab, configEntryBase, _name, _description, new FloatRange(min, max), displayValue, suffixType, formatString, roundValue);
}
@@ -18,7 +18,7 @@ public class LocalToggleSettingAttribute(
private readonly string? _description = description;
/// <inheritdoc/>
public override LocalToggleSetting CreateSetting(Type tab, ConfigEntryBase configEntryBase)
internal override LocalToggleSetting CreateSetting(Type tab, ConfigEntryBase configEntryBase)
{
return new LocalToggleSetting(tab, configEntryBase, _name, _description);
}
@@ -24,7 +24,7 @@ public static class LocalSettingsManager
if (TypeToTab.ContainsKey(type))
{
Error($"Local settings tab {type.Name} already exists.");
Logger<MiraApiPlugin>.Error($"Local settings tab {type.Name} already exists.");
return false;
}
@@ -128,7 +128,6 @@ public abstract class LocalSettingsTab(ConfigFile config)
{
CreateLabel(generalLabel, Scroller.Inner, pair.Key, ref contentOffset);
contentOrder = 1;
contentIndex = 1;
}
foreach (var setting in pair.Value)
@@ -246,7 +245,6 @@ public abstract class LocalSettingsTab(ConfigFile config)
tabButtonText.maxVisibleCharacters = int.MaxValue;
tabButtonText.text = TabName;
tabButtonText.alignment = TextAlignmentOptions.Left;
tabButtonRend?.gameObject.SetActive(!TabAppearance.HideIconOnHover);
}));
tabButton.OnMouseOut.AddListener((UnityAction)(() =>
{
@@ -4,7 +4,7 @@ using UnityEngine;
namespace MiraAPI.LocalSettings;
/// <summary>
/// Struct for modifying tab appearance.
/// Struct for modyfing tab apparance.
/// Defines how the settings look.
/// </summary>
public record struct LocalSettingTabAppearance()
@@ -24,11 +24,6 @@ public record struct LocalSettingTabAppearance()
/// </summary>
public Color TabButtonColor { get; set; } = Color.white;
/// <summary>
/// Gets or sets a value indicating whether the icon should be hidden when tab button is hovered.
/// </summary>
public bool HideIconOnHover { get; set; } = true;
/// <summary>
/// Gets or sets hover color of the tab button.
/// </summary>
@@ -83,7 +78,7 @@ public record struct LocalSettingTabAppearance()
/// Gets or sets hover color of enum buttons.
/// </summary>
public Color EnumHoverColor { get; set; } = Palette.AcceptedGreen;
/// <summary>
/// Gets or sets color of buttons.
/// </summary>
@@ -20,10 +20,7 @@ public abstract class LocalSettingBase<T> : ILocalSetting
/// <inheritdoc />
public ConfigEntryBase ConfigEntry { get; }
/// <summary>
/// Gets the tab of the local setting.
/// </summary>
public LocalSettingsTab? Tab => LocalSettingsManager.Tabs.FirstOrDefault(x => x.Settings.Contains(this));
internal LocalSettingsTab? Tab => LocalSettingsManager.Tabs.FirstOrDefault(x => x.Settings.Contains(this));
/// <summary>
/// Initializes a new instance of the <see cref="LocalSettingBase{T}"/> class.
@@ -56,7 +53,7 @@ public abstract class LocalSettingBase<T> : ILocalSetting
}
/// <summary>
/// Gets the value of the config entry, cast to the setting type.
/// Gets the value of the config entry, casted to the setting type.
/// </summary>
/// <returns>The value.</returns>
public virtual T GetValue()
+1 -7
View File
@@ -1,5 +1,4 @@
global using static Reactor.Utilities.Logger<MiraAPI.MiraApiPlugin>;
using BepInEx;
using BepInEx;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using MiraAPI.PluginLoading;
@@ -21,11 +20,6 @@ namespace MiraAPI;
[ReactorModFlags(ModFlags.RequireOnAllClients)]
public partial class MiraApiPlugin : BasePlugin
{
/// <summary>
/// Gets whether the current device is running Starlight (on mobile).
/// </summary>
public static bool IsMobile => Constants.GetPlatformType() is Platforms.Android or Platforms.IPhone;
/// <summary>
/// Gets the branding Mira API color.
/// </summary>
+11 -60
View File
@@ -1,15 +1,13 @@
using BepInEx.Configuration;
using MiraAPI.LocalSettings;
using MiraAPI.LocalSettings.Attributes;
using MiraAPI.Patches;
using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets;
using UnityEngine;
namespace MiraAPI;
/// <summary>
/// Mira API Config File Handler.
/// Mira API Config File Handler
/// </summary>
public class MiraApiSettings(ConfigFile config) : LocalSettingsTab(config)
{
@@ -21,81 +19,34 @@ public class MiraApiSettings(ConfigFile config) : LocalSettingsTab(config)
{
TabButtonHoverColor = MiraApiPlugin.MiraColor,
TabIcon = MiraAssets.SettingsIcon,
HideIconOnHover = false,
};
/// <summary>
/// Gets or sets the value stored for scaling buttons properly.
/// </summary>
public static float OldButtonScaleFactor { get; set; }
/// <inheritdoc />
public override void Open()
{
base.Open();
OldButtonScaleFactor = ButtonUIFactorSlider.Value;
}
/// <inheritdoc />
public override void OnOptionChanged(ConfigEntryBase configEntry)
{
base.OnOptionChanged(configEntry);
if (configEntry == ButtonUIFactorSlider)
{
if (HudManager.InstanceExists)
{
HudManagerPatches.ResizeUI(1f / OldButtonScaleFactor);
HudManagerPatches.ResizeUI(ButtonUIFactorSlider.Value);
}
OldButtonScaleFactor = ButtonUIFactorSlider.Value;
}
else if (configEntry == SetFpsSlider)
{
Application.targetFrameRate = (int)SetFpsSlider.Value;
}
}
/// <summary>
/// Gets whether the modifiers hud should be on the left side of the screen (under roles/task tab). Recommended for streamers.
/// </summary>
[LocalToggleSetting]
public ConfigEntry<bool> ModifiersHudLeftSide { get; private set; } = config.Bind("Visuals/UI", "Show Modifiers HUD on Left Side", false);
public ConfigEntry<bool> ModifiersHudLeftSide { get; private set; } = config.Bind("Displays", "Show Modifiers HUD on Left Side", false);
/// <summary>
/// Gets whether to show keybinds on buttons.
/// Gets whether vents and their connections appear on the map.
/// </summary>
[LocalToggleSetting]
public ConfigEntry<bool> ShowKeybinds { get; private set; } = config.Bind("Visuals/UI", "Show Keybinds on Buttons", true);
public ConfigEntry<bool> ShowVentsOnMap { get; private set; } = config.Bind("Displays", "Show Vent Layout on Map", true);
/// <summary>
/// Gets the scale of the buttons.
/// </summary>
[LocalSliderSetting(min: 0.5f, max: 1.5f, suffixType: MiraNumberSuffixes.Multiplier, formatString: "0.00", displayValue: true)]
public ConfigEntry<float> ButtonUIFactorSlider { get; private set; } =
config.Bind("Visuals/UI", "Button Scale Factor", 0.75f);
/// <summary>
/// Gets the fps specified by the player.
/// </summary>
[LocalSliderSetting(min: 60f, max: 240f, suffixType: MiraNumberSuffixes.None, formatString: "0", displayValue: true, roundValue: true)]
public ConfigEntry<float> SetFpsSlider { get; private set; } =
config.Bind("Visuals/UI", "Frames Per Seconds", 120f);
/// <summary>
/// Gets whether to apply cosmetic changes to the TaskAdder.
/// Gets whether cooldowns for buttons below 10 seconds appear with decimals.
/// </summary>
[LocalToggleSetting]
public ConfigEntry<bool> PrettyTaskAdder { get; private set; } = config.Bind("Freeplay", "Pretty Task Laptop", true);
public ConfigEntry<bool> PreciseCooldowns { get; private set; } = config.Bind("Buttons", "Precise Button Cooldowns Under 10s", false);
/// <summary>
/// Gets whether to show the red flash from sabotages.
/// Gets the size of ability buttons in-game.
/// </summary>
[LocalToggleSetting]
public ConfigEntry<bool> EnableSabotageFlashes { get; private set; } = config.Bind("Accessibility", "Enable Sabotage Flashes", true);
[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 enable the sabotage sound effects or not.
/// Gets whether to show keybinds in the control mapper.
/// </summary>
[LocalToggleSetting]
public ConfigEntry<bool> EnableSabotageBlares { get; private set; } = config.Bind("Accessibility", "Enable Sabotage Blare", true);
public ConfigEntry<bool> ShowKeybinds { get; private set; } = config.Bind("Keybinds", "Show Keybinds in Control Mapper", true);
}
+11 -11
View File
@@ -47,7 +47,7 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
_player = GetComponent<PlayerControl>();
if (_player == null)
{
Error("ModifierComponent could not find PlayerControl on the same GameObject.");
Logger<MiraApiPlugin>.Error("ModifierComponent could not find PlayerControl on the same GameObject.");
Destroy(this);
return;
}
@@ -77,7 +77,7 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
}
catch (Exception e)
{
Error($"Error while deactivating modifier {modifier.ModifierName}: {e.ToString()}");
Logger<MiraApiPlugin>.Error($"Error while deactivating modifier {modifier.ModifierName}: {e.ToString()}");
}
Modifiers.Remove(modifier);
@@ -95,7 +95,7 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
}
catch (Exception e)
{
Error($"Error while activating modifier {modifier.ModifierName}: {e.ToString()}");
Logger<MiraApiPlugin>.Error($"Error while activating modifier {modifier.ModifierName}: {e.ToString()}");
}
if (modifier is TimedModifier { AutoStart: true } timer)
@@ -122,7 +122,7 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
}
catch (Exception e)
{
Error($"Error while (fixed) updating modifier {modifier.ModifierName}: {e.ToString()}");
Logger<MiraApiPlugin>.Error($"Error while (fixed) updating modifier {modifier.ModifierName}: {e.ToString()}");
}
}
@@ -153,7 +153,7 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
}
catch (Exception e)
{
Error($"Error while updating modifier {modifier.ModifierName}: {e.ToString()}");
Logger<MiraApiPlugin>.Error($"Error while updating modifier {modifier.ModifierName}: {e.ToString()}");
}
}
}
@@ -336,7 +336,7 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
var modifier = modifiers.FirstOrDefault();
if (modifier is null)
{
Error($"Cannot remove modifier {type.Name} because it is not active.");
Logger<MiraApiPlugin>.Error($"Cannot remove modifier {type.Name} because it is not active.");
return;
}
@@ -352,7 +352,7 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
{
if (!ActiveModifiers.Contains(modifier))
{
Error($"Cannot remove modifier {modifier.ModifierName} because it is not active on this player.");
Logger<MiraApiPlugin>.Error($"Cannot remove modifier {modifier.ModifierName} because it is not active on this player.");
return;
}
@@ -382,7 +382,7 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
var modifier = GetModifier(uniqueId);
if (modifier == null)
{
Error($"Cannot remove modifier with unique id {uniqueId} because it is not active.");
Logger<MiraApiPlugin>.Error($"Cannot remove modifier with unique id {uniqueId} because it is not active.");
return;
}
@@ -412,19 +412,19 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
// TODO: Make a proper synchronization system.
if (LobbyBehaviour.Instance)
{
Warning($"Modifiers added in the lobby won't sync to new players!");
Logger<MiraApiPlugin>.Warning($"Modifiers added in the lobby won't sync to new players!");
}
var id = modifier.TypeId;
if (modifier.Unique && ActiveModifiers.Find(x => x.TypeId == id) != null)
{
Error($"Player already has modifier with id {id}!");
Logger<MiraApiPlugin>.Error($"Player already has modifier with id {id}!");
return null;
}
if (ActiveModifiers.Contains(modifier))
{
Error($"Player already has this modifier!");
Logger<MiraApiPlugin>.Error($"Player already has this modifier!");
return null;
}
+1 -1
View File
@@ -83,7 +83,7 @@ public static class ModifierExtensions
var modifier = target.GetModifier(typeId, predicate);
if (modifier is null)
{
Error($"Player {target.PlayerId} does not have modifier with type ID {typeId}.");
Logger<MiraApiPlugin>.Error($"Player {target.PlayerId} does not have modifier with type ID {typeId}.");
return;
}
+5 -6
View File
@@ -97,7 +97,7 @@ public static class ModifierManager
if (modifierType.GetConstructor(Type.EmptyTypes) == null)
{
Error($"Game Modifier {modifierType.FullName} does not have a parameterless constructor!");
Logger<MiraApiPlugin>.Error($"Game Modifier {modifierType.FullName} does not have a parameterless constructor!");
return false;
}
@@ -137,7 +137,7 @@ public static class ModifierManager
var validPlayers = plrs.Where(x => IsGameModifierValid(x, modifier, modifier.TypeId)).ToList();
if (validPlayers.Count == 0)
{
Warning($"No valid players for modifier {modifier.ModifierName}");
Logger<MiraApiPlugin>.Warning($"No valid players for modifier {modifier.ModifierName}");
continue;
}
@@ -158,7 +158,7 @@ public static class ModifierManager
if (candidates.Count == 0)
{
Warning(
Logger<MiraApiPlugin>.Warning(
$"No available players for modifier {modifier.ModifierName} at assignment {i + 1}");
break;
}
@@ -166,7 +166,7 @@ public static class ModifierManager
var plr = candidates.Random();
if (plr == null)
{
Warning($"Valid player for modifier {modifier.ModifierName} disappeared");
Logger<MiraApiPlugin>.Warning($"Valid player for modifier {modifier.ModifierName} disappeared");
continue;
}
@@ -179,7 +179,6 @@ public static class ModifierManager
private static bool IsGameModifierValid(PlayerControl player, GameModifier modifier, uint modifierId)
{
return (player.Data.Role is not ICustomRole role || role.IsModifierApplicable(modifier)) &&
modifier.IsModifierValidOn(player.Data.Role) && !player.HasModifier(modifierId) &&
modifier.CanSpawnOnCurrentMode();
modifier.IsModifierValidOn(player.Data.Role) && !player.HasModifier(modifierId);
}
}
-6
View File
@@ -39,10 +39,4 @@ public abstract class GameModifier : BaseModifier
/// <param name="reason">The reason why the game ended.</param>
/// <returns>True if the player won, false if they lost. Return null to use the player's default win condition.</returns>
public virtual bool? DidWin(GameOverReason reason) => null;
/// <summary>
/// Determines whether the modifier can spawn in general, accounting for gamemodes and everything else.
/// </summary>
/// <returns>True if the modifier is able to spawn, otherwise false.</returns>
public virtual bool CanSpawnOnCurrentMode() => !GameManager.Instance.IsHideAndSeek();
}
+1 -1
View File
@@ -80,7 +80,7 @@ public abstract class TimedModifier : BaseModifier
{
if (TimerActive)
{
Error("Can't start a timer that has already been started.");
Logger<MiraApiPlugin>.Error("Can't start a timer that has already been started.");
return;
}
+2 -2
View File
@@ -50,7 +50,7 @@ public class CustomGameOverRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc<
{
if (!gameOver.VerifyCondition(innerNetObject, [.. data.Winners]))
{
Info($"Game over condition not met for {gameOver.GetType().Name}");
Logger<MiraApiPlugin>.Info($"Game over condition not met for {gameOver.GetType().Name}");
return;
}
@@ -64,7 +64,7 @@ public class CustomGameOverRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc<
else
{
CustomGameOver.Instance = null;
Error($"Unknown game over reason: {data.Reason}");
Logger<MiraApiPlugin>.Error($"Unknown game over reason: {data.Reason}");
}
}
}
+5 -22
View File
@@ -1,5 +1,4 @@
using System;
using System.Collections;
using System.Collections;
using System.Linq;
using AmongUs.Data;
using AmongUs.GameOptions;
@@ -9,10 +8,8 @@ using MiraAPI.Events;
using MiraAPI.Events.Vanilla.Gameplay;
using Reactor.Networking.Attributes;
using Reactor.Networking.Rpc;
using Reactor.Utilities;
using Reactor.Utilities.Extensions;
using UnityEngine;
using Object = UnityEngine.Object;
namespace MiraAPI.Networking;
@@ -87,11 +84,11 @@ public static class CustomMurderRpc
bool playKillSound = true)
{
source.isKilling = false;
var data = target.Data;
if (resultFlags.HasFlag(MurderResultFlags.FailedError))
{
return;
}
var data = target.Data;
if (resultFlags.HasFlag(MurderResultFlags.FailedProtected) ||
(resultFlags.HasFlag(MurderResultFlags.DecisionByHost) && target.protectedByGuardianId > -1))
@@ -172,14 +169,7 @@ public static class CustomMurderRpc
if (showKillAnim)
{
try
{
HudManager.Instance.KillOverlay.ShowKillAnimation(source.Data, data);
}
catch (Exception e)
{
Error($"Error with kill animation: {e.ToString()}");
}
HudManager.Instance.KillOverlay.ShowKillAnimation(source.Data, data);
}
target.cosmetics.SetNameMask(false);
@@ -212,7 +202,7 @@ public static class CustomMurderRpc
bool teleportMurderer = true)
{
var cam = Camera.main?.GetComponent<FollowerCamera>();
var isParticipant = source.AmOwner || target.AmOwner;
var isParticipant = PlayerControl.LocalPlayer == source || PlayerControl.LocalPlayer == target;
var sourcePhys = source.MyPhysics;
if (teleportMurderer)
@@ -232,7 +222,7 @@ public static class CustomMurderRpc
if (createDeadBody)
{
deadBody = Object.Instantiate(GameManager.Instance.GetDeadBody(source.Data.Role));
deadBody = Object.Instantiate(GameManager.Instance.DeadBodyPrefab);
deadBody.enabled = false;
deadBody.ParentId = target.PlayerId;
deadBody.bodyRenderers.ToList().ForEach(target.SetPlayerMaterialColors);
@@ -243,13 +233,6 @@ public static class CustomMurderRpc
deadBody.transform.position = vector;
}
source.Data.Role.KillAnimSpecialSetup(deadBody, source, target);
target.Data.Role.KillAnimSpecialSetup(deadBody, source, target);
// no idea if this causes bugs, but innersloth is brain-dead
// I HATE INNERSCUFF I HATE INNERSCUFF I HATE INNERSCUFF I HATE INNERSCUFF I HATE INNERSCUFF I HATE INNERSCUFF
PlayerControl.LocalPlayer.Data.Role.KillAnimSpecialSetup(deadBody, source, target);
if (isParticipant)
{
if (cam != null)
@@ -1,67 +0,0 @@
using System.Collections;
using BepInEx.Unity.IL2CPP.Utils.Collections;
using HarmonyLib;
using MiraAPI.LocalSettings;
using UnityEngine;
namespace MiraAPI.Patches.Accessibility;
[HarmonyPatch]
public static class HudManagerFlashPatches
{
[HarmonyPatch(typeof(HudManager), nameof(HudManager.StartReactorFlash))]
[HarmonyPrefix]
public static bool ReactorFlashPrefix(HudManager __instance)
{
if (__instance.ReactorFlash == null)
{
__instance.ReactorFlash = __instance.StartCoroutine(CoReactorFlash().WrapToIl2Cpp());
}
return false;
}
[HarmonyPatch(typeof(HudManager), nameof(HudManager.StartOxyFlash))]
[HarmonyPrefix]
public static bool OxygenFlashPrefix(HudManager __instance)
{
if (__instance.OxyFlash == null)
{
__instance.OxyFlash = __instance.StartCoroutine(CoReactorFlash().WrapToIl2Cpp());
}
return false;
}
public static IEnumerator CoReactorFlash()
{
if (!HudManager.InstanceExists)
{
yield break;
}
var hudManager = HudManager.Instance;
WaitForSeconds wait = new WaitForSeconds(1f);
bool light = false;
hudManager.FullScreen.color = new Color(1f, 0f, 0f, 0.37254903f);
while (true)
{
var settins = LocalSettingsTabSingleton<MiraApiSettings>.Instance;
hudManager.FullScreen.gameObject.SetActive(settins.EnableSabotageFlashes.Value && !hudManager.FullScreen.gameObject.activeSelf);
if (settins.EnableSabotageBlares.Value)
{
SoundManager.Instance.PlaySound(ShipStatus.Instance.SabotageSound, false, 1f, null);
}
light = !light;
/*if (!MiraApiPlugin.IsMobile)
{
if (hudManager.lightFlashHandle == null)
{
hudManager.lightFlashHandle = DualshockLightManager.Instance.AllocateLight();
hudManager.lightFlashHandle.color = new Color(1f, 0f, 0f, 1f);
hudManager.lightFlashHandle.intensity = 1f;
}
hudManager.lightFlashHandle.color.SetAlpha(light ? 1f : 0f);
}*/
yield return wait;
}
}
}
@@ -1,42 +0,0 @@
using System.Linq;
using AmongUs.GameOptions;
using HarmonyLib;
using Il2CppSystem.Collections.Generic;
using MiraAPI.Roles;
using UnityEngine;
namespace MiraAPI.Patches;
[HarmonyPatch(typeof(DetectiveNotesMinigame), nameof(DetectiveNotesMinigame.SetImpostorPopup))]
public static class DetectiveNotesMinigamePatch
{
public static void Prefix(DetectiveNotesMinigame __instance, bool active)
{
__instance.impostorTypePopup.gameObject.SetActive(active);
if (!active)
{
ControllerManager.Instance.CloseOverlayMenu(__instance.ImpostorOverlay);
ControllerManager.Instance.SetUpSelectables(ControllerManager.Instance.CurrentUiState, __instance.ControllerSelectable[0], __instance.ControllerSelectable);
ControllerManager.Instance.SetCurrentSelected(__instance.ControllerSelectable[2]);
return;
}
if (__instance.impostorButton != null)
{
ControllerManager.Instance.OpenOverlayMenu(__instance.ImpostorOverlay, __instance.impostorTypePopup, __instance.ImpostorIconControllerSelectables[0], __instance.ImpostorIconControllerSelectables, false);
return;
}
__instance.impostorButton = new List<GameObject>();
var allRoles = DestroyableSingleton<RoleManager>.Instance.AllRoles;
var customRoles = CustomRoleManager.CustomRoleBehaviours;
foreach (var role in allRoles)
{
if (role.TeamType != RoleTeamTypes.Impostor || role.Role == RoleTypes.ImpostorGhost || customRoles.Contains(role) )
continue;
var gameObject = Object.Instantiate(__instance.impostorTypePrefab, __instance.impostorTypeParent);
__instance.impostorButton.Add(gameObject);
gameObject.GetComponent<DetectiveImpostorType>().Initialize(__instance, role);
__instance.ImpostorIconControllerSelectables.Add(gameObject.GetComponent<PassiveButton>());
}
ControllerManager.Instance.OpenOverlayMenu(__instance.ImpostorOverlay, __instance.impostorTypePopup, __instance.ImpostorIconControllerSelectables[0], __instance.ImpostorIconControllerSelectables, false);
}
}
+1 -1
View File
@@ -8,7 +8,7 @@ namespace MiraAPI.Patches.Events;
internal static class EndGamePatches
{
[HarmonyPatch(typeof(GameManager), nameof(GameManager.RpcEndGame))]
public static class GameManagerRpcEndGamePatch
public static class GameManagerPatches
{
public static bool Prefix(GameOverReason endReason)
{
@@ -1,20 +1,13 @@
using System.Reflection;
using HarmonyLib;
using HarmonyLib;
using MiraAPI.Events;
using MiraAPI.Events.Vanilla.Gameplay;
using MiraAPI.Utilities;
namespace MiraAPI.Patches.Events;
[HarmonyPatch]
[HarmonyPatch(typeof(TutorialManager._RunTutorial_d__3), nameof(TutorialManager._RunTutorial_d__3.MoveNext))]
public static class FreeplayRoundStartPatch
{
public static MethodBase TargetMethod()
{
return Helpers.GetStateMachineMoveNext<TutorialManager>(nameof(TutorialManager.RunTutorial))!;
}
public static void Postfix(ref bool __result)
public static void Postfix(TutorialManager._RunTutorial_d__3 __instance, ref bool __result)
{
if (!__result)
{
@@ -1,17 +0,0 @@
using HarmonyLib;
using MiraAPI.Events;
using MiraAPI.Events.Vanilla.Player;
namespace MiraAPI.Patches.Events;
[HarmonyPatch(typeof(HideAndSeekManager))]
public static class HnsFinishTaskPatch
{
[HarmonyPostfix]
[HarmonyPatch(nameof(HideAndSeekManager.FinishTask))]
public static void FinishTaskPostfix(HideAndSeekManager __instance, PlayerTask task)
{
var completeTaskEvent = new CompleteHnsTaskEvent(__instance, task.Owner, task);
MiraEventManager.InvokeEvent(completeTaskEvent);
}
}
@@ -1,35 +0,0 @@
using System.Reflection;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes;
using MiraAPI.Events;
using MiraAPI.Events.Vanilla.Gameplay;
using MiraAPI.Utilities;
namespace MiraAPI.Patches.Events;
/// <summary>
/// Patch to invoke AfterMurderEvent after a kill animation is performed.
/// </summary>
[HarmonyPatch]
public static class KillAnimationMurderEventPatch
{
public static MethodBase TargetMethod()
{
return Helpers.GetStateMachineMoveNext<KillAnimation>(nameof(KillAnimation.CoPerformKill))!;
}
public static void Postfix(Il2CppObjectBase __instance)
{
var wrapper = new StateMachineWrapper<KillAnimation>(__instance);
if (wrapper.GetState() != 1)
{
return;
}
var source = wrapper.GetParameter<PlayerControl>("source");
var target = wrapper.GetParameter<PlayerControl>("target");
var afterMurderEvent = new AfterMurderEvent(source, target, Helpers.GetBodyById(target.PlayerId));
MiraEventManager.InvokeEvent(afterMurderEvent);
}
}
@@ -0,0 +1,30 @@
using HarmonyLib;
using MiraAPI.Events;
using MiraAPI.Events.Vanilla.Gameplay;
using MiraAPI.Utilities;
namespace MiraAPI.Patches.Events;
/// <summary>
/// General patches for the KillAnimation class.
/// </summary>
[HarmonyPatch]
public static class KillAnimationPatches
{
/// <summary>
/// Used to trigger the <see cref="AfterMurderEvent"/>.
/// </summary>
[HarmonyPostfix]
[HarmonyPatch(typeof(KillAnimation._CoPerformKill_d__2), nameof(KillAnimation._CoPerformKill_d__2.MoveNext))]
public static void KillAnimPostfix(KillAnimation._CoPerformKill_d__2 __instance)
{
// Checking if state is -1 to wait for the end of coroutine.
if (__instance.__1__state != -1)
{
return;
}
var afterMurderEvent = new AfterMurderEvent(__instance.source, __instance.target, Helpers.GetBodyById(__instance.target.PlayerId));
MiraEventManager.InvokeEvent(afterMurderEvent);
}
}
@@ -41,7 +41,7 @@ public static class ReportBodyEventPatch
var source = AmongUsClient.Instance.FindObjectByNetId<PlayerControl>(reportBodyMessage.rpcObjectNetId);
if (source == null)
{
Error("Failed to find source player for RpcReportDeadBodyMessage with NetId: " + reportBodyMessage.rpcObjectNetId);
Logger<MiraApiPlugin>.Error("Failed to find source player for RpcReportDeadBodyMessage with NetId: " + reportBodyMessage.rpcObjectNetId);
return true;
}
@@ -1,5 +1,4 @@
/*
using HarmonyLib;
using HarmonyLib;
using MiraAPI.Events;
using MiraAPI.Events.Vanilla.Gameplay;
@@ -19,4 +18,3 @@ internal static class SpawnInMinigamePatch
MiraEventManager.InvokeEvent(@event2);
}
}
*/
@@ -1,5 +1,4 @@
/*
using System.Collections.Generic;
using System.Collections.Generic;
using System.Reflection;
using HarmonyLib;
using MiraAPI.Events;
@@ -35,4 +34,3 @@ internal static class SubmergedSpawnGamePatch
MiraEventManager.InvokeEvent(@event2);
}
}
*/
@@ -1,75 +0,0 @@
using System.Linq;
using AmongUs.GameOptions;
using HarmonyLib;
using MiraAPI.Events;
using MiraAPI.Events.Vanilla.Gameplay;
using MiraAPI.Events.Vanilla.Usables;
namespace MiraAPI.Patches.Events;
/// <summary>
/// Patches to invoke vent related MiraEvents.
/// </summary>
[HarmonyPatch]
public static class VanillaButtonPatches
{
[HarmonyPrefix]
[HarmonyPatch(typeof(AbilityButton), nameof(AbilityButton.DoClick))]
public static bool DoClickPrefix(AbilityButton __instance)
{
// Invoke the generic button click event.
PlayerControl? playerTarget = null;
Vent? ventTarget = null;
var role = PlayerControl.LocalPlayer.Data.Role;
if (role.Role is RoleTypes.Tracker)
{
playerTarget = role.Cast<TrackerRole>().currentTarget;
}
else if (role.Role is RoleTypes.GuardianAngel)
{
playerTarget = role.Cast<GuardianAngelRole>().currentTarget;
}
else if (role.Role is RoleTypes.Engineer)
{
ventTarget = role.Cast<EngineerRole>().currentTarget;
}
var genericEvent = new VanillaButtonClickEvent(__instance, playerTarget, ventTarget);
MiraEventManager.InvokeEvent(genericEvent);
if (genericEvent.IsCancelled)
{
MiraEventManager.InvokeEvent(new VanillaButtonCancelledEvent(__instance));
}
if (!genericEvent.IsCancelled)
{
return true;
}
return false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(SecondaryAbilityButton), nameof(SecondaryAbilityButton.DoClick))]
public static bool SecondaryDoClickPrefix(AbilityButton __instance)
{
// Invoke the generic button click event.
PlayerControl? playerTarget = null;
var role = PlayerControl.LocalPlayer.Data.Role;
if (role.Role is RoleTypes.Detective)
{
playerTarget = role.Cast<DetectiveRole>().currentTarget;
}
var genericEvent = new VanillaButtonClickEvent(__instance, playerTarget);
MiraEventManager.InvokeEvent(genericEvent);
if (genericEvent.IsCancelled)
{
MiraEventManager.InvokeEvent(new VanillaButtonCancelledEvent(__instance));
}
if (!genericEvent.IsCancelled)
{
return true;
}
return false;
}
}
@@ -1,5 +1,7 @@
using HarmonyLib;
using MiraAPI.Modifiers;
using MiraAPI.Roles;
using UnityEngine;
namespace MiraAPI.Patches.Freeplay;
+259 -338
View File
@@ -1,18 +1,15 @@
using System.Collections.Generic;
using System;
using System.Globalization;
using System.Linq;
using System.Text;
using AmongUs.GameOptions;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using MiraAPI.LocalSettings;
using MiraAPI.Modifiers;
using MiraAPI.Modifiers.Types;
using MiraAPI.PluginLoading;
using MiraAPI.Roles;
using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets;
using Reactor.Utilities.Extensions;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
@@ -21,15 +18,9 @@ using Object = UnityEngine.Object;
namespace MiraAPI.Patches.Freeplay;
[HarmonyPatch(typeof(TaskAdderGame))]
public static class TaskAdderPatches
internal static class TaskAdderPatches
{
private static Scroller _scroller = null!;
private static Dictionary<string, TaskFolder> folders = new();
public static string CrewmateName => TranslationController.Instance.GetString(StringNames.Crewmate);
public static string ImpostorName => TranslationController.Instance.GetString(StringNames.Impostor);
public static string NeutralName => "Neutral";
public static string ModifiersName => "Modifiers";
[HarmonyPostfix]
[HarmonyPatch(nameof(TaskAdderGame.Begin))]
public static void AddRolesFolder(TaskAdderGame __instance)
@@ -69,165 +60,46 @@ public 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 crewmateFolder = __instance.Root.SubFolders.ToArray().FirstOrDefault(x => x.FolderName == CrewmateName)!;
var impostorFolder = __instance.Root.SubFolders.ToArray().FirstOrDefault(x => x.FolderName == ImpostorName)!;
//var neutralFolder = __instance.CreateFolder("Neutral", __instance.Root, 2, Color.gray);
var modifiersFolder = __instance.CreateFolder(ModifiersName, __instance.Root, 0, Color.blue);
folders.Clear();
folders.Add(crewmateFolder.FolderName, crewmateFolder);
folders.Add(impostorFolder.FolderName, impostorFolder);
//folders.Add("Neutrals", neutralFolder);
folders.Add(ModifiersName, modifiersFolder);
var rolesFolder = Object.Instantiate(__instance.RootFolderPrefab, _scroller.Inner);
rolesFolder.gameObject.SetActive(false);
rolesFolder.FolderName = "Roles";
rolesFolder.name = "RolesFolder";
var modifiersFolder = Object.Instantiate(__instance.RootFolderPrefab, _scroller.Inner);
modifiersFolder.gameObject.SetActive(false);
modifiersFolder.FolderName = "Modifiers";
modifiersFolder.name = "ModifiersFolder";
int folderIdx = 2;
foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins)
{
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))
if (plugin.InternalRoles.Any(x => x.Value is ICustomRole { Configuration.ShowInFreeplay: true }))
{
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);
pluginFolder.name = $"Roles:{folderName}:{plugin.PluginId}";
pluginFolders.Add(folderName, teamFolder);
}
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);
}
if (plugin.InternalModifiers.Exists(x => x.ShowInFreeplay))
{
__instance.CreateFolder(plugin.PluginInfo.Metadata.Name, modifiersFolder)
.gameObject.name = plugin.PluginId;
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);
}
}
// Modifier folder at the end
__instance.Root.SubFolders.Insert(folderIdx, modifiersFolder);
__instance.Root.SubFolders.Add(rolesFolder);
__instance.Root.SubFolders.Add(modifiersFolder);
__instance.GoToRoot();
}
[HarmonyPrefix]
[HarmonyPatch(nameof(TaskAdderGame.PopulateRoot))]
private static bool PopulateRootPrefix(TaskAdderGame __instance, TaskAdderGame.FolderType folderType, TaskFolder rootFolder, Il2CppSystem.Collections.Generic.Dictionary<string, TaskFolder> folders, Il2CppReferenceArray<NormalPlayerTask> taskList)
{
if (folderType != TaskAdderGame.FolderType.Tasks)
{
if (folderType != TaskAdderGame.FolderType.Roles)
{
return false;
}
TaskFolder crewFolder = folders["Crewmates"] =
Object.Instantiate(__instance.RootFolderPrefab, __instance.transform);
crewFolder.gameObject.SetActive(false);
crewFolder.SetFolderColor(TaskFolder.FolderColor.Blue);
TaskFolder impFolder = folders["Impostors"] =
Object.Instantiate(__instance.RootFolderPrefab, __instance.transform);
impFolder.gameObject.SetActive(false);
impFolder.SetFolderColor(TaskFolder.FolderColor.Red);
crewFolder.FolderName = CrewmateName;
impFolder.FolderName = ImpostorName;
rootFolder.SubFolders.Insert(0, crewFolder);
rootFolder.SubFolders.Insert(0, impFolder);
Il2CppSystem.Collections.Generic.List<RoleBehaviour> impRoles = new();
Il2CppSystem.Collections.Generic.List<RoleBehaviour> crewRoles = new();
foreach (var role in RoleManager.Instance.AllRoles)
{
if (role.Role != RoleTypes.ImpostorGhost && role.Role != RoleTypes.CrewmateGhost && !role.IsCustomRole())
{
if (role.TeamType == RoleTeamTypes.Crewmate)
{
crewRoles.Add(role);
}
else
{
impRoles.Add(role);
}
}
}
crewFolder.RoleChildren = crewRoles;
impFolder.RoleChildren = impRoles;
return false;
}
if (taskList == null)
{
return false;
}
foreach (NormalPlayerTask normalPlayerTask in taskList)
{
SystemTypes systemTypes = normalPlayerTask.StartAt;
if (normalPlayerTask is DivertPowerTask task)
{
systemTypes = task.TargetSystem;
}
if (systemTypes == SystemTypes.LowerEngine)
{
systemTypes = SystemTypes.UpperEngine;
}
if (!folders.TryGetValue(systemTypes.ToString(), out var taskFolder3))
{
taskFolder3 = folders[systemTypes.ToString()] = Object.Instantiate(__instance.RootFolderPrefab, __instance.transform);
taskFolder3.SetFolderColor(TaskFolder.FolderColor.Tan);
taskFolder3.gameObject.SetActive(false);
if (systemTypes == SystemTypes.UpperEngine)
{
taskFolder3.FolderName = TranslationController.Instance.GetString(StringNames.Engines);
}
else
{
taskFolder3.FolderName = TranslationController.Instance.GetString(systemTypes);
}
rootFolder.SubFolders.Add(taskFolder3);
}
taskFolder3.TaskChildren.Add(normalPlayerTask);
}
return false;
}
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,
@@ -250,46 +122,40 @@ public 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)
{
StringBuilder stringBuilder = new StringBuilder(64);
var stringBuilder = new Il2CppSystem.Text.StringBuilder(64);
__instance.Hierarchy.Add(taskFolder);
foreach (var t in __instance.Hierarchy)
for (var i = 0; i < __instance.Hierarchy.Count; i++)
{
stringBuilder.Append(t.FolderName);
stringBuilder.Append('\\');
stringBuilder.Append(__instance.Hierarchy.ToArray()[i].FolderName);
stringBuilder.Append("\\");
}
__instance.PathText.text = stringBuilder.ToString();
var prettyEnabled = LocalSettingsTabSingleton<MiraApiSettings>.Instance.PrettyTaskAdder.Value;
if (prettyEnabled)
for (var j = 0; j < __instance.ActiveItems.Count; j++)
{
__instance.PathText.fontSizeMin = 3;
__instance.PathText.fontSizeMax = 3;
__instance.transform.FindChild("TitleText_TMP")?.gameObject.DestroyImmediate();
Object.Destroy(__instance.ActiveItems.ToArray()[j].gameObject);
}
__instance.ActiveItems.ToArray().Do(x => x.gameObject.Destroy());
__instance.ActiveItems.Clear();
float num = 0f;
float num2 = 0f;
float num3 = 0f;
foreach (var t in taskFolder.SubFolders)
var num = 0f;
var num2 = 0f;
var num3 = 0f;
for (var k = 0; k < taskFolder.SubFolders.Count; k++)
{
TaskFolder taskFolder2 = Object.Instantiate(t, __instance.TaskParent);
var taskFolder2 = Object.Instantiate(taskFolder.SubFolders.ToArray()[k], __instance.TaskParent);
var folderTransform = taskFolder2.transform;
taskFolder2.gameObject.SetActive(true);
taskFolder2.Parent = __instance;
taskFolder2.transform.localPosition = new Vector3(num, num2, 0f);
taskFolder2.transform.localScale = Vector3.one;
num3 = Mathf.Max(num3, taskFolder2.Text.bounds.size.y + 1.1f);
folderTransform.localPosition = new Vector3(num, num2, 0f);
folderTransform.localScale = Vector3.one;
num3 = Mathf.Max(num3, taskFolder2.Text.bounds.size.y + 1.3f);
num += __instance.folderWidth;
if (num > __instance.lineWidth)
{
@@ -298,204 +164,141 @@ public static class TaskAdderPatches
num3 = 0f;
}
__instance.ActiveItems.Add(taskFolder2.transform);
if (taskFolder2 != null && taskFolder2.Button != null)
__instance.ActiveItems.Add(folderTransform);
if (!taskFolder2 || !taskFolder2.Button)
{
ControllerManager.Instance.AddSelectableUiElement(taskFolder2.Button);
continue;
}
ControllerManager.Instance.AddSelectableUiElement(taskFolder2.Button);
if (!string.IsNullOrEmpty(__instance.restorePreviousSelectionByFolderName) &&
taskFolder2.FolderName.Equals(__instance.restorePreviousSelectionByFolderName, StringComparison.Ordinal))
{
__instance.restorePreviousSelectionFound = taskFolder2.Button;
}
}
var list = taskFolder.TaskChildren
.ToArray()
.OrderBy(x => x.TaskType)
.ToArray();
foreach (var task in list)
var flag = false;
var list = taskFolder.Children.ToArray().OrderBy(t => t.TaskType).ToList();
for (var l = 0; l < list.Count; l++)
{
TaskAddButton taskAddButton = Object.Instantiate(__instance.TaskPrefab);
taskAddButton.MyTask = task;
switch (task.TaskType)
var taskAddButton = Object.Instantiate(__instance.TaskPrefab);
taskAddButton.MyTask = list.ToArray()[l];
switch (taskAddButton.MyTask.TaskType)
{
case TaskTypes.DivertPower:
{
var targetSystem = task.Cast<DivertPowerTask>().TargetSystem;
taskAddButton.Text.text = TranslationController.Instance.GetString(
StringNames.DivertPowerTo,
TranslationController.Instance.GetString(targetSystem));
break;
}
{
var targetSystem = taskAddButton.MyTask.Cast<DivertPowerTask>().TargetSystem;
taskAddButton.Text.text = TranslationController.Instance.GetString(
StringNames.DivertPowerTo,
TranslationController.Instance.GetString(targetSystem));
break;
}
case TaskTypes.FixWeatherNode:
{
var nodeId = task.Cast<WeatherNodeTask>().NodeId;
taskAddButton.Text.text =
TranslationController.Instance.GetString(
StringNames.FixWeatherNode) + " " +
TranslationController.Instance.GetString(
WeatherSwitchGame.ControlNames[nodeId]);
break;
}
{
var nodeId = taskAddButton.MyTask.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(task.TaskType);
TranslationController.Instance.GetString(taskAddButton.MyTask.TaskType);
break;
}
__instance.AddFileAsChildCustom(taskAddButton, ref num, ref num2, ref num3);
if (taskAddButton != null && taskAddButton.Button != null)
if (taskAddButton.Button == null)
{
ControllerManager.Instance.AddSelectableUiElement(taskAddButton.Button, false);
}
}
var roleChildren = taskFolder.RoleChildren
.ToArray()
.Where(x => x is not ICustomRole)
.ToArray();
foreach (var role in roleChildren)
{
TaskAddButton roleAddButton = Object.Instantiate(__instance.RoleButton);
roleAddButton.SafePositionWorld = __instance.SafePositionWorld;
roleAddButton.Text.text = prettyEnabled ? role.GetRoleName() : "Be_" + role.GetRoleName() + ".exe";
roleAddButton.Role = role;
if (prettyEnabled)
{
roleAddButton.Text.fontSizeMin = 1;
roleAddButton.FileImage.sprite = role.IsImpostor
? MiraAssets.ImpostorFile.LoadAsset()
: MiraAssets.CrewmateFile.LoadAsset();
continue;
}
__instance.AddFileAsChildCustom(roleAddButton, ref num, ref num2, ref num3);
if (roleAddButton != null && roleAddButton.Button != null)
ControllerManager.Instance.AddSelectableUiElement(taskAddButton.Button);
if (__instance.Hierarchy.Count == 1 || flag)
{
ControllerManager.Instance.AddSelectableUiElement(roleAddButton.Button);
continue;
}
var component =
ControllerManager.Instance.CurrentUiState.CurrentSelection.GetComponent<TaskFolder>();
if (component != null)
{
__instance.restorePreviousSelectionByFolderName = component.FolderName;
}
ControllerManager.Instance.SetDefaultSelection(taskAddButton.Button);
flag = true;
}
string[] split = [];
try
if (taskFolder.FolderName == "Roles")
{
split = taskFolder.name.Replace("(Clone)", string.Empty).Split(':');
}
catch
{
// I hate you
}
if (split is ["Roles", _, _] && folders.Any(x => taskFolder.IsChildOf(x.Value)))
{
var plugin = MiraPluginManager.GetPluginByGuid(split[2]);
if (plugin != null)
for (var m = 0; m < RoleManager.Instance.AllRoles.Length; m++)
{
foreach (var role in plugin.InternalRoles)
var roleBehaviour = RoleManager.Instance.AllRoles[m];
if (roleBehaviour.Role == RoleTypes.ImpostorGhost || roleBehaviour.Role == RoleTypes.CrewmateGhost ||
CustomRoleManager.CustomRoles.ContainsKey((ushort)roleBehaviour.Role))
{
var roleBehaviour = role.Value;
if (roleBehaviour.Role == RoleTypes.ImpostorGhost || roleBehaviour.Role == RoleTypes.CrewmateGhost ||
roleBehaviour is ICustomRole { Configuration.ShowInFreeplay: false })
{
continue;
}
var crewLocale = TranslationController.Instance.GetString(StringNames.Crewmate);
var impLocale = TranslationController.Instance.GetString(StringNames.Impostor);
continue;
}
var teamFolder = roleBehaviour.IsImpostor ? impLocale : crewLocale;
if (roleBehaviour is ICustomRole cs)
{
teamFolder = cs.Configuration.FreeplayFolder;
}
if (split[1] != teamFolder)
{
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;
var roleAddButton = Object.Instantiate(__instance.RoleButton);
roleAddButton.MyTask = null;
roleAddButton.SafePositionWorld = __instance.SafePositionWorld;
roleAddButton.Text.text = prettyEnabled ? roleBehaviour.GetRoleName() : "Be_" + roleBehaviour.GetRoleName() + ".exe";
roleAddButton.Text.EnableMasking();
roleAddButton.role = roleBehaviour;
if (prettyEnabled)
{
roleAddButton.Text.fontSizeMin = 1;
roleAddButton.FileImage.sprite = roleBehaviour.IsImpostor
? MiraAssets.ImpostorFile.LoadAsset()
: MiraAssets.CrewmateFile.LoadAsset();
}
if (roleBehaviour 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)
{
customColor = Palette.CrewmateBlue;
}
else if (custom.Team is ModdedRoleTeams.Impostor)
{
customColor = Palette.ImpostorRed;
}
roleAddButton.FileImage.color = customColor;
roleAddButton.RolloverHandler.OutColor = customColor;
}
if (taskAddButton2.Button == null)
{
continue;
}
__instance.AddFileAsChildCustom(roleAddButton, ref num, ref num2, ref num3);
ControllerManager.Instance.AddSelectableUiElement(roleAddButton.Button);
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;
}
}
}
if (folders.TryGetValue(ModifiersName, out var modifiersFolder) && taskFolder.IsChildOf(modifiersFolder))
if (taskFolder)
{
var plugin = MiraPluginManager.GetPluginByGuid(taskFolder.name.Replace("(Clone)", string.Empty));
if (plugin != null)
var split = taskFolder.name.Split(':');
if (split.Length == 2)
{
foreach (var modifier in plugin.InternalModifiers)
var pluginId = split[0];
var folderName = split[1];
if (MiraPluginManager.GetPluginByGuid(pluginId) is { } plugin)
{
if (!modifier.ShowInFreeplay)
if (folderName == "Roles(Clone)")
{
continue;
PluginRoles(__instance, plugin, ref num, ref num2, ref num3);
}
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)
else if (folderName == "Modifiers(Clone)")
{
taskAddButton.FileImage.sprite = MiraAssets.TimedModifierFile.LoadAsset();
taskAddButton.Text.text += $" ({timed.Duration}s)";
PluginModifiers(__instance, plugin, ref num, ref num2, ref num3);
}
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);
}
}
}
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);
@@ -503,11 +306,129 @@ public static class TaskAdderPatches
_scroller.ScrollToTop();
}
ControllerManager.Instance.SetBackButton(__instance.Hierarchy.Count == 1
? __instance.BackButton
: __instance.FolderBackButton);
if (__instance.Hierarchy.Count == 1)
{
ControllerManager.Instance.SetBackButton(__instance.BackButton);
return false;
}
__instance.SetPreviousControllerSelection(taskFolder.FolderName);
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)
{
var customColor = custom.IntroConfiguration?.IntroTeamColor ?? Color.gray;
if (custom.Team is ModdedRoleTeams.Crewmate)
{
customColor = Palette.CrewmateBlue;
}
else if (custom.Team is ModdedRoleTeams.Impostor)
{
customColor = Palette.ImpostorRed;
}
taskAddButton2.FileImage.color = customColor;
taskAddButton2.RolloverHandler.OutColor = customColor;
}
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;
}
}
}
}
+1 -6
View File
@@ -9,11 +9,6 @@ namespace MiraAPI.Patches.Hud;
public static class ButtonResetPatches
{
public static void Postfix()
{
ResetCooldowns();
}
public static void ResetCooldowns()
{
foreach (var customActionButton in CustomButtonManager.CustomButtons)
{
@@ -23,7 +18,7 @@ public static class ButtonResetPatches
}
catch (System.Exception ex)
{
Error($"Error resetting cooldown and effect for button {customActionButton.GetType().Name}: {ex}");
Logger<MiraApiPlugin>.Error($"Error resetting cooldown and effect for button {customActionButton.GetType().Name}: {ex}");
}
if (customActionButton.UsesMode == ButtonUsesMode.PerRound)
+13 -122
View File
@@ -1,5 +1,4 @@
using System.Collections;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using HarmonyLib;
using MiraAPI.Hud;
@@ -28,86 +27,6 @@ public static class HudManagerPatches
private static Dictionary<TextMeshPro, int> vanillaKeybindIcons = new();
internal static List<TextMeshPro> ModdedKeybindIcons = new();
public static IEnumerator CoResizeUI()
{
while (!HudManager.Instance)
{
yield return null;
}
yield return new WaitForSeconds(0.01f);
ResizeUI(LocalSettingsTabSingleton<MiraApiSettings>.Instance.ButtonUIFactorSlider.Value);
}
public static void ResizeUI(float scaleFactor)
{
var baseButtons = HudManager.Instance.transform.FindChild("Buttons");
if (baseButtons != null)
{
foreach (var aspect in baseButtons.GetComponentsInChildren<AspectPosition>(true))
{
if (aspect.gameObject == null)
{
continue;
}
if (aspect.gameObject.transform.parent.name == "TopRight")
{
continue;
}
if (aspect.gameObject.transform.parent.transform.parent.name == "TopRight")
{
continue;
}
aspect.gameObject.SetActive(!aspect.isActiveAndEnabled);
aspect.DistanceFromEdge *= new Vector2(scaleFactor, scaleFactor);
aspect.gameObject.SetActive(!aspect.isActiveAndEnabled);
}
}
foreach (var button in HudManager.Instance.GetComponentsInChildren<ActionButton>(true))
{
if (button.gameObject == null)
{
continue;
}
button.gameObject.SetActive(!button.isActiveAndEnabled);
button.gameObject.transform.localScale *= scaleFactor;
button.gameObject.SetActive(!button.isActiveAndEnabled);
}
if (baseButtons != null)
{
foreach (var arrange in baseButtons.GetComponentsInChildren<GridArrange>(true))
{
if (!arrange.gameObject || !arrange.transform)
{
continue;
}
arrange.gameObject.SetActive(!arrange.isActiveAndEnabled);
arrange.CellSize = new Vector2(scaleFactor, scaleFactor);
arrange.gameObject.SetActive(!arrange.isActiveAndEnabled);
if (arrange.isActiveAndEnabled && arrange.gameObject.transform.childCount != 0)
{
try
{
arrange.ArrangeChilds();
}
catch
{
// Error($"Error arranging child objects in GridArrange: {e}");
}
}
}
}
}
/*
/// <summary>
/// Trigger hudstart on current custom gamemode
@@ -154,23 +73,6 @@ public static class HudManagerPatches
gridArrange.Alignment = GridArrange.StartAlign.Right;
aspectPosition.Alignment = AspectPosition.EdgeAlignments.LeftBottom;
if (Constants.GetPlatformType() is Platforms.Android or Platforms.IPhone)
{
var fakeButton = Object.Instantiate(__instance.AbilityButton, BottomLeft.transform);
foreach (var renderer in fakeButton.GetComponentsInChildren<Renderer>())
{
renderer.enabled = false;
}
fakeButton.buttonLabelText.Destroy();
fakeButton.cooldownTimerText.Destroy();
fakeButton.usesRemainingText.Destroy();
fakeButton.GetComponent<PassiveButton>().Destroy();
fakeButton.ToggleVisible(true);
fakeButton.Destroy();
}
ModdedKeybindIcons = [];
foreach (var button in CustomButtonManager.CustomButtons)
{
var location = button.Location switch
@@ -191,18 +93,16 @@ public static class HudManagerPatches
}
catch (System.Exception e)
{
Error($"Failed to create custom button {button.GetType().Name}: {e}");
Logger<MiraApiPlugin>.Error($"Failed to create custom button {button.GetType().Name}: {e}");
}
}
__instance.ImpostorVentButton.transform.SetParent(null);
__instance.ImpostorVentButton.transform.SetParent(BottomRight.transform);
gridArrange.Start();
gridArrange.ArrangeChilds();
aspectPosition.AdjustPosition();
vanillaKeybindIcons = [];
var keybindIconPos = new Vector3(0.4f, 0.4f, -9.5f);
var keybindIconPos = new Vector3(-0.4f, 0.4f, -9.5f);
var vanillaButtons = new Dictionary<GameObject, int>
{
{ __instance.KillButton.gameObject, 8 },
@@ -225,15 +125,9 @@ public static class HudManagerPatches
}
var icon = Helpers.CreateKeybindIcon(buttonObj, key, keybindIconPos);
vanillaKeybindIcons.Add(icon.transform.GetChild(0).GetComponent<TextMeshPro>(), actionId);
if (!MiraApiPlugin.IsMobile)
{
var comp = buttonObj.GetComponent<ActionButton>();
KeybindManager.VanillaKeybinds[comp.GetType()].Button = comp;
}
var comp = buttonObj.GetComponent<ActionButton>();
KeybindManager.VanillaKeybinds[comp.GetType()].Button = comp;
}
MiraApiSettings.OldButtonScaleFactor =
LocalSettingsTabSingleton<MiraApiSettings>.Instance.ButtonUIFactorSlider.Value;
Coroutines.Start(CoResizeUI());
}
/// <summary>
@@ -261,7 +155,7 @@ public static class HudManagerPatches
}
catch (System.Exception e)
{
Error($"Failed to set custom button {button.GetType().Name} active: {e}");
Logger<MiraApiPlugin>.Error($"Failed to set custom button {button.GetType().Name} active: {e}");
}
}
}
@@ -270,16 +164,11 @@ public static class HudManagerPatches
[HarmonyPostfix]
public static void UpdatePostfix()
{
var canSeeBinds = ActiveInputManager.currentControlType == ActiveInputManager.InputType.Keyboard &&
LocalSettingsTabSingleton<MiraApiSettings>.Instance.ShowKeybinds.Value;
foreach (var btnIcon in vanillaKeybindIcons)
{
btnIcon.Key.text = KeybindUtils.GetKeycodeByActionId(btnIcon.Value).ToString();
btnIcon.Key.transform.parent.gameObject.SetActive(canSeeBinds);
}
foreach (var btnIcon in ModdedKeybindIcons)
{
btnIcon.transform.parent.gameObject.SetActive(canSeeBinds);
btnIcon.Key.transform.parent.gameObject.SetActive(ActiveInputManager.currentControlType == ActiveInputManager.InputType.Keyboard &&
LocalSettingsTabSingleton<MiraApiSettings>.Instance.ShowKeybinds.Value);
}
var player = ReInput.players.GetPlayer(0);
@@ -293,10 +182,12 @@ public static class HudManagerPatches
entry.Invoke();
}
}
foreach (var entry in KeybindManager.VanillaKeybinds.Values.Where(x => player.GetButtonDown(x.Id)))
foreach (var entry in KeybindManager.VanillaKeybinds.Values)
{
entry.Invoke();
if (player.GetButtonDown(entry.Id))
{
entry.Invoke();
}
}
}
}
+16 -73
View File
@@ -1,12 +1,8 @@
using System.Linq;
using System.Reflection;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes;
using HarmonyLib;
using MiraAPI.Events;
using MiraAPI.Events.Vanilla.Gameplay;
using MiraAPI.Roles;
using MiraAPI.Utilities;
using Reactor.Utilities;
namespace MiraAPI.Patches;
@@ -33,29 +29,14 @@ public static class IntroCutscenePatches
MiraEventManager.InvokeEvent(@event);
}
[HarmonyPatch]
public static class IntroCutsceneShowRolePatch
[HarmonyPostfix]
[HarmonyPatch(typeof(IntroCutscene._ShowRole_d__41), nameof(IntroCutscene._ShowRole_d__41.MoveNext))]
[HarmonyPriority(Priority.Last)]
public static void ShowRolePatch(IntroCutscene._ShowRole_d__41 __instance)
{
public static MethodBase TargetMethod()
{
return Helpers.GetStateMachineMoveNext<IntroCutscene>(nameof(IntroCutscene.ShowRole))!;
}
public static void Postfix(Il2CppObjectBase __instance)
{
var wrapper = new StateMachineWrapper<IntroCutscene>(__instance);
// run before the first yield
if (wrapper.GetState() != 1)
{
return;
}
var introCutscene = wrapper.Instance;
Info("IntroCutscene ShowRole reached");
var @event = new IntroRoleRevealEvent(introCutscene);
MiraEventManager.InvokeEvent(@event);
}
var realInstance = __instance.__4__this;
var @event = new IntroRoleRevealEvent(realInstance);
MiraEventManager.InvokeEvent(@event);
}
[HarmonyPrefix]
@@ -85,54 +66,16 @@ public static class IntroCutscenePatches
}
}
[HarmonyPatch]
public static class IntroCutsceneDestroyPatch
[HarmonyPostfix]
[HarmonyPatch(nameof(IntroCutscene.OnDestroy))]
public static void GameBeginPatch(IntroCutscene __instance)
{
private static bool _usedFallback;
MiraEventManager.InvokeEvent(new IntroEndEvent(__instance));
public static MethodBase TargetMethod()
{
var onDestroy = AccessTools.Method(typeof(IntroCutscene), "OnDestroy");
if (onDestroy != null)
{
_usedFallback = false;
Info("Using OnDestroy for IntroCutsceneDestroyPatch");
return onDestroy;
}
var @event = new BeforeRoundStartEvent(true);
MiraEventManager.InvokeEvent(@event);
_usedFallback = true;
return Helpers.GetStateMachineMoveNext<IntroCutscene>(nameof(IntroCutscene.CoBegin))!;
}
public static void Postfix(Il2CppObjectBase __instance)
{
IntroCutscene introCutscene;
if (_usedFallback)
{
var wrapper = new StateMachineWrapper<IntroCutscene>(__instance);
// run after the final yield
if (wrapper.GetState() != -1)
{
return;
}
introCutscene = wrapper.Instance;
}
else
{
introCutscene = __instance.Cast<IntroCutscene>();
}
Info("IntroCutscene ended");
MiraEventManager.InvokeEvent(new IntroEndEvent(introCutscene));
var @event = new BeforeRoundStartEvent(true);
MiraEventManager.InvokeEvent(@event);
if (@event.IsCancelled) return;
MiraEventManager.InvokeEvent(new RoundStartEvent(true));
}
if (@event.IsCancelled) return;
MiraEventManager.InvokeEvent(new RoundStartEvent(true));
}
}
@@ -15,8 +15,7 @@ namespace MiraAPI.Patches.Keybinds;
[HarmonyPatch(typeof(ControlMapper))]
public static class ControlMapperPatches
{
// Patching this crashes for Epic Games users
/*[HarmonyPrefix]
[HarmonyPrefix]
[HarmonyPatch(nameof(ControlMapper.Start))]
private static void StartPrefix(ControlMapper __instance)
{
@@ -38,7 +37,7 @@ public static class ControlMapperPatches
entry.Value = !entry.Value;
text.text = $"Show Keybinds: {(entry.Value ? "On" : "Off")}";
}));
}*/
}
[HarmonyPrefix]
[HarmonyPatch(nameof(ControlMapper.Update))]
@@ -22,7 +22,7 @@ public static class OptionsMenuPatches
private static Dictionary<int, List<GameObject>> tabButtons = new();
/// <summary>
/// Creates the tabs and their content.
/// Creates the tabs and their content
/// </summary>
[HarmonyPostfix]
[HarmonyPatch(nameof(OptionsMenuBehaviour.Start))]
@@ -8,7 +8,7 @@ public static class ToggleBehaviourPatch
{
/// <summary>
/// ResetText was inlined ofc.
/// Skill issue.
/// Skill issue
/// </summary>
[HarmonyPrefix]
[HarmonyPatch(nameof(ToggleButtonBehaviour.UpdateText))]
+13 -35
View File
@@ -6,13 +6,10 @@ using System.Linq;
using AmongUs.Data;
using HarmonyLib;
using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets;
using Reactor.Utilities;
using Reactor.Utilities.Extensions;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
using Object = UnityEngine.Object;
namespace MiraAPI.Patches.Menu;
@@ -23,20 +20,6 @@ public static class HatsTabPatches
private static SortedList<string, List<HatData>> sortedHats = [];
private static int currentPage;
private static void PreviousPage(HatsTab hatsTab)
{
currentPage--;
currentPage = currentPage < 0 ? sortedHats.Count - 1 : currentPage;
GenerateHats(hatsTab, currentPage);
}
private static void NextPage(HatsTab hatsTab)
{
currentPage++;
currentPage = currentPage > sortedHats.Count - 1 ? 0 : currentPage;
GenerateHats(hatsTab, currentPage);
}
[HarmonyPatch(nameof(HatsTab.OnEnable))]
[HarmonyPrefix]
public static bool OnEnablePrefix(HatsTab __instance)
@@ -55,8 +38,6 @@ public static class HatsTabPatches
}
}
InventoryUtility.CreateNextBackButtons(__instance, PreviousPage, NextPage);
GenerateHats(__instance, currentPage);
return false;
@@ -64,17 +45,22 @@ public static class HatsTabPatches
[HarmonyPatch(typeof(HatsTab), nameof(HatsTab.Update))]
[HarmonyPrefix]
public static void UpdatePrefix(HatsTab __instance)
{
if (sortedHats.Count == 0) return;
if (Input.GetKeyDown(KeyCode.LeftControl) || Input.GetKeyDown(KeyCode.RightControl) || Input.GetKeyDown(KeyCode.LeftArrow))
{
PreviousPage(__instance);
currentPage--;
currentPage = currentPage < 0 ? sortedHats.Count - 1 : currentPage;
GenerateHats(__instance, currentPage);
}
else if (Input.GetKeyDown(KeyCode.Tab) || Input.GetKeyDown(KeyCode.RightArrow))
{
NextPage(__instance);
currentPage++;
currentPage = currentPage > sortedHats.Count - 1 ? 0 : currentPage;
GenerateHats(__instance, currentPage);
}
}
@@ -98,10 +84,10 @@ public static class HatsTabPatches
text.gameObject.transform.localScale = Vector3.one;
text.GetComponent<TextTranslatorTMP>().Destroy();
text.EnableStencilMasking();
text.text = $"{groupName} ({currentPage + 1}/{sortedHats.Count})";
text.text = $"{groupName} ({currentPage + 1}/{sortedHats.Count})\nPress Ctrl or Tab to cycle pages";
text.alignment = TextAlignmentOptions.Center;
text.fontSize = 5f;
text.fontSizeMax = 5f;
text.fontSize = 3f;
text.fontSizeMax = 3f;
text.fontSizeMin = 0f;
var xLerp = __instance.XRange.Lerp(0.5f);
var yLerp = __instance.YStart - hatIndex / __instance.NumPerRow * __instance.YOffset;
@@ -139,17 +125,9 @@ public static class HatsTabPatches
{
var colorChip = Object.Instantiate(__instance.ColorTabPrefab, __instance.scroller.Inner);
colorChip.gameObject.name = hat.ProductId;
if (ActiveInputManager.currentControlType == ActiveInputManager.InputType.Keyboard)
{
colorChip.Button.OnClick.AddListener((Action)__instance.ClickEquip);
colorChip.Button.OnMouseOver.AddListener((Action)(() => __instance.SelectHat(hat)));
colorChip.Button.OnMouseOut.AddListener((Action)(() => __instance.SelectHat(HatManager.Instance.GetHatById(DataManager.Player.Customization.Hat))));
}
else
{
colorChip.Button.OnClick.AddListener((Action)(() => __instance.SelectHat(hat)));
}
colorChip.Button.OnClick.AddListener((Action)(() => __instance.ClickEquip()));
colorChip.Button.OnMouseOver.AddListener((Action)(() => __instance.SelectHat(hat)));
colorChip.Button.OnMouseOut.AddListener((Action)(() => __instance.SelectHat(HatManager.Instance.GetHatById(DataManager.Player.Customization.Hat))));
colorChip.Inner.SetHat(hat, __instance.HasLocalPlayer() ? PlayerControl.LocalPlayer.Data.DefaultOutfit.ColorId : DataManager.Player.Customization.Color);
colorChip.Button.ClickMask = __instance.scroller.Hitbox;
colorChip.SelectionHighlight.gameObject.SetActive(false);
-54
View File
@@ -1,54 +0,0 @@
using System;
using MiraAPI.Utilities.Assets;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
using Object = UnityEngine.Object;
namespace MiraAPI.Patches.Menu;
public static class InventoryUtility
{
public static void CreateNextBackButtons<T>(T tab, Action<T> previousPage, Action<T> nextPage) where T : InventoryTab
{
var title = tab.transform.FindChild("Text");
title.localPosition = new Vector3(.9123f, -.23f, -55);
title.GetComponent<TextMeshPro>().alignment = TextAlignmentOptions.Center;
tab.scroller.Hitbox.transform.localScale -= new Vector3(0, 0.1f, 0);
tab.scroller.Hitbox.transform.localPosition -= new Vector3(0, 0.1f, 0);
var leftButtonElement = Object.Instantiate(PlayerCustomizationMenu.Instance.BackButton.gameObject, tab.transform);
var leftSpriteRenderer = leftButtonElement.GetComponent<SpriteRenderer>();
var leftButton = leftButtonElement.GetComponent<PassiveButton>();
leftButtonElement.transform.localPosition = new Vector3(-1.1f, -0.2f, -55);
leftSpriteRenderer.sprite = MiraAssets.NextButton.LoadAsset();
leftSpriteRenderer.flipX = true;
leftButton.OnMouseOver = new UnityEvent();
leftButton.OnMouseOver.AddListener((Action)(() => leftSpriteRenderer.sprite = MiraAssets.NextButtonActive.LoadAsset()));
leftButton.OnMouseOut = new UnityEvent();
leftButton.OnMouseOut.AddListener((Action)(() => leftSpriteRenderer.sprite = MiraAssets.NextButton.LoadAsset()));
leftButton.OnClick = new Button.ButtonClickedEvent();
leftButton.OnClick.AddListener((Action)(() => previousPage(tab)));
var rightButtonElement = Object.Instantiate(leftButtonElement, tab.transform);
var rightSpriteRenderer = rightButtonElement.GetComponent<SpriteRenderer>();
var rightButton = rightButtonElement.GetComponent<PassiveButton>();
rightButtonElement.transform.localPosition = new Vector3(2.8f, -0.2f, -55);
rightSpriteRenderer.sprite = MiraAssets.NextButton.LoadAsset();
rightSpriteRenderer.flipX = false;
rightButton.OnMouseOver = new UnityEvent();
rightButton.OnMouseOver.AddListener((Action)(() => rightSpriteRenderer.sprite = MiraAssets.NextButtonActive.LoadAsset()));
rightButton.OnMouseOut = new UnityEvent();
rightButton.OnMouseOut.AddListener((Action)(() => rightSpriteRenderer.sprite = MiraAssets.NextButton.LoadAsset()));
rightButton.OnClick = new Button.ButtonClickedEvent();
rightButton.OnClick.AddListener((Action)(() => nextPage(tab)));
}
}
@@ -1,7 +1,5 @@
using HarmonyLib;
using MiraAPI.LocalSettings;
using MiraAPI.Utilities.Assets;
using UnityEngine;
namespace MiraAPI.Patches.Menu;
@@ -19,6 +17,5 @@ public static class MainMenuManagerPatches
public static void AwakePostfix()
{
AddressablesLoader.LoadAll();
Application.targetFrameRate = (int)LocalSettingsTabSingleton<MiraApiSettings>.Instance.SetFpsSlider.Value;
}
}
+12 -36
View File
@@ -26,20 +26,6 @@ public static class NameplatesTabPatches
}
}
private static void PreviousPage(NameplatesTab tab)
{
currentPage--;
currentPage = currentPage < 0 ? SortedNameplates.Count - 1 : currentPage;
GenerateHats(tab, currentPage);
}
private static void NextPage(NameplatesTab tab)
{
currentPage++;
currentPage = currentPage > SortedNameplates.Count - 1 ? 0 : currentPage;
GenerateHats(tab, currentPage);
}
[HarmonyPatch(nameof(NameplatesTab.OnEnable))]
[HarmonyPrefix]
public static bool OnEnablePrefix(NameplatesTab __instance)
@@ -47,9 +33,6 @@ public static class NameplatesTabPatches
__instance.plateId = HatManager.Instance.GetNamePlateById(DataManager.Player.Customization.namePlate).ProdId;
if (!SortedNameplates.ContainsKey("Vanilla")) AddRange(DestroyableSingleton<HatManager>.Instance.GetUnlockedNamePlates().Select(x => ("Vanilla", x)));
InventoryUtility.CreateNextBackButtons(__instance, PreviousPage, NextPage);
GenerateHats(__instance, currentPage);
return false;
@@ -62,11 +45,15 @@ public static class NameplatesTabPatches
{
if (Input.GetKeyDown(KeyCode.LeftControl) || Input.GetKeyDown(KeyCode.RightControl) || Input.GetKeyDown(KeyCode.LeftArrow))
{
PreviousPage(__instance);
currentPage--;
currentPage = currentPage < 0 ? SortedNameplates.Count - 1 : currentPage;
GenerateHats(__instance, currentPage);
}
else if (Input.GetKeyDown(KeyCode.Tab) || Input.GetKeyDown(KeyCode.RightArrow))
{
NextPage(__instance);
currentPage++;
currentPage = currentPage > SortedNameplates.Count - 1 ? 0 : currentPage;
GenerateHats(__instance, currentPage);
}
}
@@ -86,10 +73,10 @@ public static class NameplatesTabPatches
text.gameObject.transform.localScale = Vector3.one;
text.GetComponent<TextTranslatorTMP>().Destroy();
text.EnableStencilMasking();
text.text = $"{groupName} ({currentPage + 1}/{SortedNameplates.Count})";
text.text = $"{groupName} ({currentPage + 1}/{SortedNameplates.Count})\nPress Ctrl or Tab to cycle pages";
text.alignment = TextAlignmentOptions.Center;
text.fontSize = 5f;
text.fontSizeMax = 5f;
text.fontSize = 3f;
text.fontSizeMax = 3f;
text.fontSizeMin = 0f;
float xLerp = __instance.XRange.Lerp(0.5f);
float yLerp = __instance.YStart - hatIndex / __instance.NumPerRow * __instance.YOffset;
@@ -112,20 +99,9 @@ public static class NameplatesTabPatches
{
var colorChip = Object.Instantiate(__instance.ColorTabPrefab, __instance.scroller.Inner);
colorChip.gameObject.name = namePlate.ProductId;
if (ActiveInputManager.currentControlType == ActiveInputManager.InputType.Keyboard)
{
colorChip.Button.OnClick.AddListener((Action)__instance.ClickEquip);
colorChip.Button.OnMouseOver.AddListener((Action)(() => __instance.SelectNameplate(namePlate)));
colorChip.Button.OnMouseOut.AddListener(
(Action)(() => __instance.SelectNameplate(
HatManager.Instance.GetNamePlateById(DataManager.Player.Customization.NamePlate))));
}
else
{
colorChip.Button.OnClick.AddListener((Action)(() => __instance.SelectNameplate(namePlate)));
}
colorChip.Button.OnClick.AddListener((Action)(() => __instance.ClickEquip()));
colorChip.Button.OnMouseOver.AddListener((Action)(() => __instance.SelectNameplate(namePlate)));
colorChip.Button.OnMouseOut.AddListener((Action)(() => __instance.SelectNameplate(HatManager.Instance.GetNamePlateById(DataManager.Player.Customization.NamePlate))));
colorChip.Button.ClickMask = __instance.scroller.Hitbox;
colorChip.ProductId = namePlate.ProdId;
+12 -33
View File
@@ -18,20 +18,6 @@ public static class SkinsTabPatches
private static SortedList<string, List<SkinData>> sortedSkins = [];
private static int currentPage;
private static void PreviousPage(SkinsTab tab)
{
currentPage--;
currentPage = currentPage < 0 ? sortedSkins.Count - 1 : currentPage;
GenerateHats(tab, currentPage);
}
private static void NextPage(SkinsTab tab)
{
currentPage++;
currentPage = currentPage > sortedSkins.Count - 1 ? 0 : currentPage;
GenerateHats(tab, currentPage);
}
[HarmonyPatch(nameof(SkinsTab.OnEnable))]
[HarmonyPrefix]
public static bool OnEnablePrefix(SkinsTab __instance)
@@ -50,8 +36,6 @@ public static class SkinsTabPatches
}
}
InventoryUtility.CreateNextBackButtons(__instance, PreviousPage, NextPage);
GenerateHats(__instance, currentPage);
return false;
@@ -64,11 +48,15 @@ public static class SkinsTabPatches
{
if (Input.GetKeyDown(KeyCode.LeftControl) || Input.GetKeyDown(KeyCode.RightControl) || Input.GetKeyDown(KeyCode.LeftArrow))
{
PreviousPage(__instance);
currentPage--;
currentPage = currentPage < 0 ? sortedSkins.Count - 1 : currentPage;
GenerateHats(__instance, currentPage);
}
else if (Input.GetKeyDown(KeyCode.Tab) || Input.GetKeyDown(KeyCode.RightArrow))
{
NextPage(__instance);
currentPage++;
currentPage = currentPage > sortedSkins.Count - 1 ? 0 : currentPage;
GenerateHats(__instance, currentPage);
}
}
@@ -88,10 +76,10 @@ public static class SkinsTabPatches
text.gameObject.transform.localScale = Vector3.one;
text.GetComponent<TextTranslatorTMP>().Destroy();
text.EnableStencilMasking();
text.text = $"{groupName} ({currentPage + 1}/{sortedSkins.Count})";
text.text = $"{groupName} ({currentPage + 1}/{sortedSkins.Count})\nPress Ctrl or Tab to cycle pages";
text.alignment = TextAlignmentOptions.Center;
text.fontSize = 5f;
text.fontSizeMax = 5f;
text.fontSize = 3f;
text.fontSizeMax = 3f;
text.fontSizeMin = 0f;
float xLerp = __instance.XRange.Lerp(0.5f);
float yLerp = __instance.YStart - hatIndex / __instance.NumPerRow * __instance.YOffset;
@@ -114,18 +102,9 @@ public static class SkinsTabPatches
{
var colorChip = Object.Instantiate(__instance.ColorTabPrefab, __instance.scroller.Inner);
colorChip.gameObject.name = skin.ProductId;
if (ActiveInputManager.currentControlType == ActiveInputManager.InputType.Keyboard)
{
colorChip.Button.OnClick.AddListener((Action)__instance.ClickEquip);
colorChip.Button.OnMouseOver.AddListener((Action)(() => __instance.SelectSkin(skin)));
colorChip.Button.OnMouseOut.AddListener(
(Action)(() =>
__instance.SelectSkin(HatManager.Instance.GetSkinById(DataManager.Player.Customization.Skin))));
}
else
{
colorChip.Button.OnClick.AddListener((Action)(() => __instance.SelectSkin(skin)));
}
colorChip.Button.OnClick.AddListener((Action)(() => __instance.ClickEquip()));
colorChip.Button.OnMouseOver.AddListener((Action)(() => __instance.SelectSkin(skin)));
colorChip.Button.OnMouseOut.AddListener((Action)(() => __instance.SelectSkin(HatManager.Instance.GetSkinById(DataManager.Player.Customization.Skin))));
colorChip.Button.ClickMask = __instance.scroller.Hitbox;
colorChip.ProductId = skin.ProductId;
colorChip.SelectionHighlight.gameObject.SetActive(false);
+12 -37
View File
@@ -25,21 +25,6 @@ public static class VisorsTabPatches
}
}
private static void PreviousPage(VisorsTab tab)
{
currentPage--;
currentPage = currentPage < 0 ? SortedVisors.Count - 1 : currentPage;
GenerateHats(tab, currentPage);
}
private static void NextPage(VisorsTab tab)
{
currentPage++;
currentPage = currentPage > SortedVisors.Count - 1 ? 0 : currentPage;
GenerateHats(tab, currentPage);
}
[HarmonyPatch(nameof(VisorsTab.OnEnable))]
[HarmonyPrefix]
public static bool OnEnablePrefix(VisorsTab __instance)
@@ -47,9 +32,6 @@ public static class VisorsTabPatches
__instance.visorId = HatManager.Instance.GetVisorById(DataManager.Player.Customization.Visor).ProdId;
if (!SortedVisors.ContainsKey("Vanilla")) AddRange(DestroyableSingleton<HatManager>.Instance.GetUnlockedVisors().Select(x => ("Vanilla", x)));
InventoryUtility.CreateNextBackButtons(__instance, PreviousPage, NextPage);
GenerateHats(__instance, currentPage);
return false;
@@ -62,11 +44,15 @@ public static class VisorsTabPatches
{
if (Input.GetKeyDown(KeyCode.LeftControl) || Input.GetKeyDown(KeyCode.RightControl) || Input.GetKeyDown(KeyCode.LeftArrow))
{
PreviousPage(__instance);
currentPage--;
currentPage = currentPage < 0 ? SortedVisors.Count - 1 : currentPage;
GenerateHats(__instance, currentPage);
}
else if (Input.GetKeyDown(KeyCode.Tab) || Input.GetKeyDown(KeyCode.RightArrow))
{
NextPage(__instance);
currentPage++;
currentPage = currentPage > SortedVisors.Count - 1 ? 0 : currentPage;
GenerateHats(__instance, currentPage);
}
}
@@ -86,10 +72,10 @@ public static class VisorsTabPatches
text.gameObject.transform.localScale = Vector3.one;
text.GetComponent<TextTranslatorTMP>().Destroy();
text.EnableStencilMasking();
text.text = $"{groupName} ({currentPage + 1}/{SortedVisors.Count})";
text.text = $"{groupName} ({currentPage + 1}/{SortedVisors.Count})\nPress Ctrl or Tab to cycle pages";
text.alignment = TextAlignmentOptions.Center;
text.fontSize = 5f;
text.fontSizeMax = 5f;
text.fontSize = 3f;
text.fontSizeMax = 3f;
text.fontSizeMin = 0f;
float xLerp = __instance.XRange.Lerp(0.5f);
float yLerp = __instance.YStart - hatIndex / __instance.NumPerRow * __instance.YOffset;
@@ -112,20 +98,9 @@ public static class VisorsTabPatches
{
var colorChip = Object.Instantiate(__instance.ColorTabPrefab, __instance.scroller.Inner);
colorChip.gameObject.name = visor.ProductId;
if (ActiveInputManager.currentControlType == ActiveInputManager.InputType.Keyboard)
{
colorChip.Button.OnClick.AddListener((Action)__instance.ClickEquip);
colorChip.Button.OnMouseOver.AddListener((Action)(() => __instance.SelectVisor(visor)));
colorChip.Button.OnMouseOut.AddListener(
(Action)(() =>
__instance.SelectVisor(HatManager.Instance.GetVisorById(DataManager.Player.Customization.Visor))));
}
else
{
colorChip.Button.OnClick.AddListener((Action)(() => __instance.SelectVisor(visor)));
}
colorChip.Button.OnClick.AddListener((Action)(() => __instance.ClickEquip()));
colorChip.Button.OnMouseOver.AddListener((Action)(() => __instance.SelectVisor(visor)));
colorChip.Button.OnMouseOut.AddListener((Action)(() => __instance.SelectVisor(HatManager.Instance.GetVisorById(DataManager.Player.Customization.Visor))));
colorChip.Button.ClickMask = __instance.scroller.Hitbox;
colorChip.ProductId = visor.ProductId;
colorChip.SelectionHighlight.gameObject.SetActive(false);
@@ -17,19 +17,6 @@ namespace MiraAPI.Patches.Options;
[HarmonyPatch(typeof(GameOptionsMenu))]
internal static class GameOptionsMenuPatch
{
[HarmonyPostfix]
[HarmonyPatch(nameof(GameOptionsMenu.OnEnable))]
public static void OpenPatch(GameOptionsMenu __instance)
{
HudManager.Instance.PlayerCam.OverrideScreenShakeEnabled = false;
}
[HarmonyPostfix]
[HarmonyPatch(nameof(GameOptionsMenu.OnDisable))]
public static void ClosePatch(GameOptionsMenu __instance)
{
HudManager.Instance.PlayerCam.OverrideScreenShakeEnabled = true;
}
[HarmonyPostfix]
[HarmonyPatch(nameof(GameOptionsMenu.Update))]
// ReSharper disable once InconsistentNaming
@@ -45,20 +32,11 @@ internal static class GameOptionsMenuPatch
{
ModifiersUpdate(ref num);
}
else if (__instance.name == "CUSTOM TAB 1")
{
CustomMenuOneUpdate(ref num);
}
else if (__instance.name == "CUSTOM TAB 2")
{
CustomMenuTwoUpdate(ref num);
}
else
{
var filteredGroups =
GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
.Where(x => x.OptionableType == null &&
!x.ShowInModifiersMenu && x.ParentMenu == MenuCategory.Roles) ?? [];
.Where(x => x.OptionableType == null && !x.ShowInModifiersMenu) ?? [];
foreach (var group in filteredGroups)
{
@@ -121,48 +99,6 @@ internal static class GameOptionsMenuPatch
}
}
private static void CustomMenuOneUpdate(ref float num)
{
var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
.Where(x => x.ParentMenu == MenuCategory.CustomOne) ?? [];
foreach (var modGroup in groups)
{
UpdateGroup(modGroup, ref num);
}
}
private static void CustomMenuOneCreate(GameOptionsMenu menu)
{
var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
.Where(x => x.ParentMenu == MenuCategory.CustomOne) ?? [];
foreach (var group in groups)
{
CreateGroup(menu, group);
}
}
private static void CustomMenuTwoUpdate(ref float num)
{
var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
.Where(x => x.ParentMenu == MenuCategory.CustomTwo) ?? [];
foreach (var modGroup in groups)
{
UpdateGroup(modGroup, ref num);
}
}
private static void CustomMenuTwoCreate(GameOptionsMenu menu)
{
var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
.Where(x => x.ParentMenu == MenuCategory.CustomTwo) ?? [];
foreach (var group in groups)
{
CreateGroup(menu, group);
}
}
private static void ModifiersUpdate(ref float num)
{
var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
@@ -201,19 +137,9 @@ internal static class GameOptionsMenuPatch
ModifiersCreate(__instance);
return false;
}
else if (__instance.name == "CUSTOM TAB 1")
{
CustomMenuOneCreate(__instance);
return false;
}
else if (__instance.name == "CUSTOM TAB 2")
{
CustomMenuTwoCreate(__instance);
return false;
}
var filteredGroups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
.Where(x => x.OptionableType == null && !x.ShowInModifiersMenu && x.ParentMenu == MenuCategory.Roles) ?? [];
.Where(x => x.OptionableType == null && !x.ShowInModifiersMenu) ?? [];
foreach (var group in filteredGroups)
{
@@ -117,7 +117,7 @@ internal static class GamePresetsTabPatches
// ReSharper disable once InconsistentNaming
public static void Postfix()
{
Error("OnDisable called");
Logger<MiraApiPlugin>.Error("OnDisable called");
if (_saveButton)
{
@@ -155,7 +155,7 @@ internal static class GamePresetsTabPatches
var prefab = GameSettingMenu.Instance.GameSettingsButton;
if (prefab == null)
{
Error("GameSettingsButton prefab is null");
Logger<MiraApiPlugin>.Error("GameSettingsButton prefab is null");
return;
}
@@ -318,10 +318,10 @@ internal static class GamePresetsTabPatches
private static void Refresh()
{
Error("Refreshing presets");
Logger<MiraApiPlugin>.Error("Refreshing presets");
if (GameSettingMenuPatches.SelectedMod == null)
{
Error("Selected mod is null, cannot refresh presets");
Logger<MiraApiPlugin>.Error("Selected mod is null, cannot refresh presets");
return;
}
lock (Lock)
+33 -202
View File
@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Linq;
using HarmonyLib;
using MiraAPI.GameOptions;
using MiraAPI.Modifiers;
using MiraAPI.PluginLoading;
using MiraAPI.Presets;
@@ -28,23 +27,13 @@ internal static class GameSettingMenuPatches
private static Vector3 _roleBtnOgPos;
private static Vector3 _smallRoleBtnOgPos;
private static Vector3 _modifierBtnOgPos;
private static Vector3 _customOneBtnOgPos;
private static Vector3 _customTwoBtnOgPos;
private static GameOptionsMenu? _modifiersTab;
private static PassiveButton? _modifiersButton;
private static PassiveButton? _smallRolesButton;
private static GameOptionsMenu? _customOneTab;
private static PassiveButton? _customOneButton;
private static GameOptionsMenu? _customTwoTab;
private static PassiveButton? _customTwoButton;
private static GameObject? _nextModButton;
private static GameObject? _previousModButton;
private static Dictionary<int, Vector3> OptionsPositions { get; } = [];
private static Dictionary<int, Vector3> ModifiersPositions { get; } = [];
private static Dictionary<int, Vector3> CustomOnePositions { get; } = [];
private static Dictionary<int, Vector3> CustomTwoPositions { get; } = [];
private static void SaveScrollPositions(GameSettingMenu gameSettingMenu)
{
@@ -52,14 +41,6 @@ internal static class GameSettingMenuPatches
{
ModifiersPositions[SelectedModIdx] = _modifiersTab!.scrollBar.Inner.localPosition;
}
else if (_customOneTab)
{
CustomOnePositions[SelectedModIdx] = _customOneTab!.scrollBar.Inner.localPosition;
}
else if (_customTwoTab)
{
CustomTwoPositions[SelectedModIdx] = _customTwoTab!.scrollBar.Inner.localPosition;
}
RoleSettingMenuPatches.RolePositions[SelectedModIdx] = gameSettingMenu.RoleSettingsTab.scrollBar.Inner.localPosition;
OptionsPositions[SelectedModIdx] = gameSettingMenu.GameSettingsTab.scrollBar.Inner.localPosition;
@@ -84,21 +65,9 @@ internal static class GameSettingMenuPatches
_modifiersTab!.gameObject.SetActive(tabNum == 3);
}
if (_customOneTab)
{
_customOneTab!.gameObject.SetActive(tabNum == 4);
}
if (_customTwoTab)
{
_customTwoTab!.gameObject.SetActive(tabNum == 5);
}
_modifiersButton?.SelectButton(tabNum == 3);
_smallRolesButton?.SelectButton(tabNum == 2);
_customOneButton?.SelectButton(tabNum == 4);
_customTwoButton?.SelectButton(tabNum == 5);
if (tabNum >= 3 && tabNum <= 5)
if (tabNum == 3)
{
if (__instance.RoleSettingsButton.gameObject.active)
{
@@ -106,21 +75,7 @@ internal static class GameSettingMenuPatches
}
else
{
switch (tabNum)
{
case 3:
_modifiersButton?.SelectButton(true);
__instance.MenuDescriptionText.text = SelectedMod != null ? SelectedMod.MiraPlugin.ModifierMenuDescription : "Configure modifiers and their settings here!";
break;
case 4:
_customOneButton?.SelectButton(true);
__instance.MenuDescriptionText.text = SelectedMod != null ? SelectedMod.MiraPlugin.CustomOptionMenuOneDescription : "Apply game settings for this mod!";
break;
case 5:
_customTwoButton?.SelectButton(true);
__instance.MenuDescriptionText.text = SelectedMod != null ? SelectedMod.MiraPlugin.CustomOptionMenuTwoDescription : "Apply game settings for this mod!";
break;
}
_modifiersButton?.SelectButton(true);
}
}
}
@@ -132,30 +87,19 @@ internal static class GameSettingMenuPatches
_modifiersButton?.SelectButton(tabNum == 3);
_smallRolesButton?.SelectButton(tabNum == 2);
_customOneButton?.SelectButton(tabNum == 4);
_customTwoButton?.SelectButton(tabNum == 5);
if (tabNum >= 3 && tabNum <= 5)
if (tabNum != 3)
{
if (__instance.RoleSettingsButton.gameObject.active)
{
__instance.RoleSettingsButton.SelectButton(true);
}
else
{
switch (tabNum)
{
case 3:
_modifiersButton?.SelectButton(true);
break;
case 4:
_customOneButton?.SelectButton(true);
break;
case 5:
_customTwoButton?.SelectButton(true);
break;
}
}
return;
}
if (__instance.RoleSettingsButton.gameObject.active)
{
__instance.RoleSettingsButton.SelectButton(true);
}
else
{
_modifiersButton?.SelectButton(true);
}
}
@@ -183,16 +127,16 @@ internal static class GameSettingMenuPatches
_text.alignment = TextAlignmentOptions.Center;
_nextModButton = Object.Instantiate(__instance.BackButton, __instance.BackButton.transform.parent).gameObject;
_nextModButton.transform.localPosition = new Vector3(-2.2663f, 1.5272f, -25f);
_nextModButton.name = "RightArrowButton";
_nextModButton.transform.FindChild("Inactive").gameObject.GetComponent<SpriteRenderer>().sprite =
var nextButton = Object.Instantiate(__instance.BackButton, __instance.BackButton.transform.parent).gameObject;
nextButton.transform.localPosition = new Vector3(-2.2663f, 1.5272f, -25f);
nextButton.name = "RightArrowButton";
nextButton.transform.FindChild("Inactive").gameObject.GetComponent<SpriteRenderer>().sprite =
MiraAssets.NextButton.LoadAsset();
_nextModButton.transform.FindChild("Active").gameObject.GetComponent<SpriteRenderer>().sprite =
nextButton.transform.FindChild("Active").gameObject.GetComponent<SpriteRenderer>().sprite =
MiraAssets.NextButtonActive.LoadAsset();
_nextModButton.gameObject.GetComponent<CloseButtonConsoleBehaviour>().DestroyImmediate();
nextButton.gameObject.GetComponent<CloseButtonConsoleBehaviour>().DestroyImmediate();
var passiveButton = _nextModButton.gameObject.GetComponent<PassiveButton>();
var passiveButton = nextButton.gameObject.GetComponent<PassiveButton>();
passiveButton.OnClick = new ButtonClickedEvent();
passiveButton.OnClick.AddListener(
(UnityAction)(() =>
@@ -207,13 +151,13 @@ internal static class GameSettingMenuPatches
UpdateText(__instance, __instance.GameSettingsTab, __instance.RoleSettingsTab);
}));
_previousModButton = Object.Instantiate(_nextModButton, __instance.BackButton.transform.parent).gameObject;
_previousModButton.transform.localPosition = new Vector3(-4.4209f, 1.5272f, -25f);
_previousModButton.name = "LeftArrowButton";
_previousModButton.gameObject.GetComponent<CloseButtonConsoleBehaviour>().Destroy();
_previousModButton.transform.FindChild("Active").gameObject.GetComponent<SpriteRenderer>().flipX =
_previousModButton.transform.FindChild("Inactive").gameObject.GetComponent<SpriteRenderer>().flipX = true;
_previousModButton.gameObject.GetComponent<PassiveButton>().OnClick.AddListener(
var backButton = Object.Instantiate(nextButton, __instance.BackButton.transform.parent).gameObject;
backButton.transform.localPosition = new Vector3(-4.4209f, 1.5272f, -25f);
backButton.name = "LeftArrowButton";
backButton.gameObject.GetComponent<CloseButtonConsoleBehaviour>().Destroy();
backButton.transform.FindChild("Active").gameObject.GetComponent<SpriteRenderer>().flipX =
backButton.transform.FindChild("Inactive").gameObject.GetComponent<SpriteRenderer>().flipX = true;
backButton.gameObject.GetComponent<PassiveButton>().OnClick.AddListener(
(UnityAction)(() =>
{
SaveScrollPositions(__instance);
@@ -230,61 +174,6 @@ internal static class GameSettingMenuPatches
_modifiersTab = Object.Instantiate(__instance.GameSettingsTab, __instance.GameSettingsTab.transform.parent);
_modifiersTab.name = "MODIFIERS TAB";
// clone game settings tab for both custom tabs
_customOneTab = Object.Instantiate(__instance.GameSettingsTab, __instance.GameSettingsTab.transform.parent);
_customOneTab.name = "CUSTOM TAB 1";
_customTwoTab = Object.Instantiate(__instance.GameSettingsTab, __instance.GameSettingsTab.transform.parent);
_customTwoTab.name = "CUSTOM TAB 2";
// create button for the first custom category
var customPos = new Vector3(
__instance.RoleSettingsButton.transform.localPosition.x,
__instance.RoleSettingsButton.transform.localPosition.y,
__instance.RoleSettingsButton.transform.localPosition.z);
_customOneButton = Object.Instantiate(__instance.RoleSettingsButton, __instance.RoleSettingsButton.transform.parent);
_customOneButton.buttonText.gameObject.GetComponent<TextTranslatorTMP>().Destroy();
_customOneButton.OnClick = new ButtonClickedEvent();
_customOneButton.OnClick.AddListener(
(UnityAction)(() =>
{
__instance.ChangeTab(4, false);
}));
_customOneButton.OnMouseOver = new UnityEvent();
_customOneButton.OnMouseOver.AddListener(
(UnityAction)(() =>
{
__instance.ChangeTab(4, true);
}));
_customOneButton.buttonText.text = SelectedMod != null ? SelectedMod.MiraPlugin.CustomOptionMenuNameOne : "Custom Category 1";
customPos.y -= 0.637f;
_customOneButton.transform.localPosition = customPos;
_customOneButton.name = "CustomOneButton";
_customOneBtnOgPos = _customOneButton.transform.localPosition;
// create button for the first custom category
_customTwoButton = Object.Instantiate(_customOneButton, _customOneButton.transform.parent);
_customTwoButton.OnClick = new ButtonClickedEvent();
_customTwoButton.OnClick.AddListener(
(UnityAction)(() =>
{
__instance.ChangeTab(5, false);
}));
_customTwoButton.OnMouseOver = new UnityEvent();
_customTwoButton.OnMouseOver.AddListener(
(UnityAction)(() =>
{
__instance.ChangeTab(5, true);
}));
_customTwoButton.buttonText.text = SelectedMod != null ? SelectedMod.MiraPlugin.CustomOptionMenuNameTwo : "Custom Category 2";
customPos.y -= 0.637f;
_customTwoButton.transform.localPosition = customPos;
_customTwoButton.name = "CustomTwoButton";
_customTwoBtnOgPos = _customTwoButton.transform.localPosition;
// create button for modifiers
__instance.RoleSettingsButton.buttonText.gameObject.GetComponent<TextTranslatorTMP>().Destroy();
__instance.RoleSettingsButton.gameObject.SetActive(false);
@@ -415,32 +304,20 @@ internal static class GameSettingMenuPatches
bool replaceWithModifiers = true;
_smallRolesButton!.transform.localPosition = _smallRoleBtnOgPos;
_modifiersButton!.transform.localPosition = _modifierBtnOgPos;
_customOneButton!.transform.localPosition = _customOneBtnOgPos;
_customTwoButton!.transform.localPosition = _customTwoBtnOgPos;
menu.RoleSettingsButton.transform.localPosition = _roleBtnOgPos;
if (SelectedModIdx != 0)
{
_customOneButton.buttonText.text = SelectedMod!.MiraPlugin.CustomOptionMenuNameOne;
_customTwoButton.buttonText.text = SelectedMod!.MiraPlugin.CustomOptionMenuNameTwo;
var modHasRoles = SelectedMod!.InternalRoles.Count != 0;
var modHasCustomOne = SelectedMod.InternalOptionGroups.Exists(
x => x.ParentMenu == MenuCategory.CustomOne);
var modHasCustomTwo = SelectedMod.InternalOptionGroups.Exists(
x => x.ParentMenu == MenuCategory.CustomTwo);
var modHasModifiers = SelectedMod.InternalOptionGroups.Exists(
x => x.ShowInModifiersMenu || x.ParentMenu == MenuCategory.Modifiers || x.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true);
x => x.ShowInModifiersMenu || x.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true);
var modHasOptions =
SelectedMod.InternalOptionGroups.Exists(x => x.OptionableType == null && (!x.ShowInModifiersMenu ||
(x.ParentMenu != MenuCategory.Modifiers && x.ParentMenu != MenuCategory.Roles)));
SelectedMod.InternalOptionGroups.Exists(x => x.OptionableType == null && !x.ShowInModifiersMenu);
_modifiersButton.gameObject.SetActive(true);
_smallRolesButton.gameObject.SetActive(true);
_customOneButton.gameObject.SetActive(true);
_customTwoButton.gameObject.SetActive(true);
menu.GameSettingsButton.gameObject.SetActive(true);
menu.RoleSettingsButton.gameObject.SetActive(false);
var defaultButton = menu.RoleSettingsButton;
// If there are no registered custom roles in the selected mod, hide the button.
if (!modHasRoles)
@@ -457,7 +334,6 @@ internal static class GameSettingMenuPatches
{
_modifiersButton.gameObject.SetActive(false);
menu.RoleSettingsButton.gameObject.SetActive(true);
defaultButton = _modifiersButton;
if (_modifiersTab!.gameObject.active)
{
@@ -480,7 +356,6 @@ internal static class GameSettingMenuPatches
// If the mod has roles, we can enable the bigger role button and disable the small one.
if (modHasRoles)
{
defaultButton = menu.RoleSettingsButton;
menu.RoleSettingsButton.gameObject.SetActive(true);
_smallRolesButton.gameObject.SetActive(false);
}
@@ -495,7 +370,6 @@ internal static class GameSettingMenuPatches
{
menu.ChangeTab(0, false);
}
defaultButton = menu.RoleSettingsButton;
// If the mod has roles and modifiers, we can move their buttons to the game settings button position, since nothing is there.
if (menu.RoleSettingsButton.gameObject.active)
@@ -515,37 +389,16 @@ internal static class GameSettingMenuPatches
_smallRolesButton.transform.localPosition.z);
}
}
// If the mod has no primary custom tab, we can go ahead and hide the button.
if (!modHasCustomOne)
{
_customOneButton.gameObject.SetActive(false);
if (_customOneTab!.gameObject.active)
{
defaultButton.SelectButton(true);
}
}
// If the mod has no secondary custom tab, we can go ahead and hide the button.
if (!modHasCustomTwo)
{
_customTwoButton.gameObject.SetActive(false);
if (_customTwoTab!.gameObject.active)
{
defaultButton.SelectButton(true);
}
}
}
else
{
_modifiersButton.gameObject.SetActive(false);
_smallRolesButton.gameObject.SetActive(false);
_customOneButton.gameObject.SetActive(false);
_customTwoButton.gameObject.SetActive(false);
menu.RoleSettingsButton.gameObject.SetActive(true);
menu.GameSettingsButton.gameObject.SetActive(true);
replaceWithModifiers = false;
if (_modifiersTab!.gameObject.active || _customOneTab!.gameObject.active || _customTwoTab!.gameObject.active)
if (_modifiersTab!.gameObject.active)
{
menu.ChangeTab(0, false);
}
@@ -593,17 +446,7 @@ internal static class GameSettingMenuPatches
if (_modifiersTab?.Children?.Count > 0)
{
CleanupSettings(_modifiersTab, MenuCategory.Modifiers);
}
if (_customOneTab?.Children?.Count > 0)
{
CleanupSettings(_customOneTab, MenuCategory.CustomOne);
}
if (_customTwoTab?.Children?.Count > 0)
{
CleanupSettings(_customTwoTab, MenuCategory.CustomTwo);
CleanupSettings(_modifiersTab, true);
}
void CleanupRoleSettings(RolesSettingsMenu rolesMenu)
@@ -641,7 +484,7 @@ internal static class GameSettingMenuPatches
}
}
void CleanupSettings(GameOptionsMenu gameOptMenu, MenuCategory menuCategory = MenuCategory.Roles)
void CleanupSettings(GameOptionsMenu gameOptMenu, bool modifiers = false)
{
ClearOptions(gameOptMenu.Children);
gameOptMenu.Children = null;
@@ -652,19 +495,7 @@ internal static class GameSettingMenuPatches
.ForEach(header => header.gameObject.DestroyImmediate());
gameOptMenu.Initialize();
var positions = OptionsPositions;
switch (menuCategory)
{
case MenuCategory.Modifiers:
positions = ModifiersPositions;
break;
case MenuCategory.CustomOne:
positions = CustomOnePositions;
break;
case MenuCategory.CustomTwo:
positions = CustomTwoPositions;
break;
}
var positions = modifiers ? ModifiersPositions : OptionsPositions;
if (positions.TryGetValue(SelectedModIdx, out var pos))
{
gameOptMenu.scrollBar.Inner.localPosition = pos;
+4 -108
View File
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using AmongUs.GameOptions;
using HarmonyLib;
using MiraAPI.GameOptions;
using MiraAPI.Modifiers;
@@ -164,110 +163,7 @@ public static class LobbyViewPanePatches
{
if (SelectedModIdx == 0)
{
float num = 0.95f;
float num2 = -6.53f;
CategoryHeaderMasked categoryHeaderMasked =
Object.Instantiate(__instance.categoryHeaderOrigin, __instance.settingsContainer);
categoryHeaderMasked.SetHeader(StringNames.RoleQuotaLabel, 61);
categoryHeaderMasked.transform.localScale = Vector3.one;
categoryHeaderMasked.transform.localPosition = new Vector3(-9.77f, 1.26f, -2f);
__instance.settingsInfo.Add(categoryHeaderMasked.gameObject);
var list = new List<RoleBehaviour>();
for (int i = 0; i < 2; i++)
{
var categoryHeaderRoleVariant =
Object.Instantiate(__instance.categoryHeaderRoleOrigin, __instance.settingsContainer);
categoryHeaderRoleVariant.SetHeader(
(i == 0) ? StringNames.CrewmateRolesHeader : StringNames.ImpostorRolesHeader,
61);
categoryHeaderRoleVariant.transform.localScale = Vector3.one;
categoryHeaderRoleVariant.transform.localPosition = new Vector3(0.09f, num, -2f);
__instance.settingsInfo.Add(categoryHeaderRoleVariant.gameObject);
num -= 0.696f;
var roles = RoleManager.Instance.AllRoles.ToArray().Where(x =>
x is not ICustomRole && !x.IsRoleBlacklisted() && x.Role != RoleTypes.Crewmate &&
x.Role != RoleTypes.Impostor && ((i == 0 && x.TeamType is RoleTeamTypes.Crewmate) ||
(i == 1 && x.TeamType is RoleTeamTypes.Impostor)) &&
x.Role != RoleTypes.CrewmateGhost && x.Role != RoleTypes.ImpostorGhost).ToList();
for (int j = 0; j < roles.Count; j++)
{
var roleBehaviour = roles[j];
int chancePerGame =
GameOptionsManager.Instance.CurrentGameOptions.RoleOptions.GetChancePerGame(
roleBehaviour.Role);
int numPerGame =
GameOptionsManager.Instance.CurrentGameOptions.RoleOptions
.GetNumPerGame(roleBehaviour.Role);
bool flag = numPerGame == 0;
var viewSettingsInfoPanelRoleVariant =
Object.Instantiate(
__instance.infoPanelRoleOrigin,
__instance.settingsContainer);
viewSettingsInfoPanelRoleVariant.transform.localScale = Vector3.one;
viewSettingsInfoPanelRoleVariant.transform.localPosition = new Vector3(num2, num, -2f);
if (!flag)
{
list.Add(roleBehaviour);
}
viewSettingsInfoPanelRoleVariant.SetInfo(
roleBehaviour.NiceName,
numPerGame,
chancePerGame,
61,
(i == 0) ? Palette.CrewmateRoleBlue : Palette.ImpostorRoleRed,
roleBehaviour.RoleIconSolid,
i == 0,
flag);
__instance.settingsInfo.Add(viewSettingsInfoPanelRoleVariant.gameObject);
num -= 0.664f;
}
}
if (list.Count > 0)
{
var categoryHeaderMasked2 =
Object.Instantiate(__instance.categoryHeaderOrigin, __instance.settingsContainer);
categoryHeaderMasked2.SetHeader(StringNames.RoleSettingsLabel, 61);
categoryHeaderMasked2.transform.localScale = Vector3.one;
categoryHeaderMasked2.transform.localPosition = new Vector3(-9.77f, num, -2f);
__instance.settingsInfo.Add(categoryHeaderMasked2.gameObject);
num -= 2.1f;
float num3 = 0f;
for (int k = 0; k < list.Count; k++)
{
float num4;
if (k % 2 == 0)
{
num4 = -5.8f;
if (k > 0)
{
num -= num3 + 0.85f;
num3 = 0f;
}
}
else
{
num4 = 0.14999962f;
}
var advancedRoleViewPanel =
Object.Instantiate(__instance.advancedRolePanelOrigin, __instance.settingsContainer);
advancedRoleViewPanel.transform.localScale = Vector3.one;
advancedRoleViewPanel.transform.localPosition = new Vector3(num4, num, -2f);
float num5 = advancedRoleViewPanel.SetUp(list[k], 0.85f, 61);
if (num5 > num3)
{
num3 = num5;
}
__instance.settingsInfo.Add(advancedRoleViewPanel.gameObject);
}
}
__instance.scrollBar.SetYBoundsMax(-num);
return false;
return true;
}
if (__instance.currentTab == ModifiersTabName)
@@ -382,7 +278,7 @@ public static class LobbyViewPanePatches
foreach (var grouping in sortedRoleGroups)
{
if (!grouping.Any() || grouping.All(x => x.Configuration.HideSettings || !x.VisibleInSettings()))
if (!grouping.Any() || grouping.All(x => x.Configuration.HideSettings))
{
continue;
}
@@ -413,7 +309,7 @@ public static class LobbyViewPanePatches
foreach (var customRole in grouping)
{
if (customRole.Configuration.HideSettings || !customRole.VisibleInSettings())
if (customRole.Configuration.HideSettings)
{
continue;
}
@@ -448,7 +344,7 @@ public static class LobbyViewPanePatches
}
viewSettingsInfoPanelRoleVariant.SetInfo(
roleBehaviour.GetRoleName(),
roleBehaviour.NiceName,
numPerGame,
chancePerGame,
61,
+2 -20
View File
@@ -216,16 +216,7 @@ public static class OptionsPatches
return true;
}
var increment = __instance.Increment;
if (Input.GetKey(KeyCode.LeftShift) &&
__instance.TryGetComponent<MiraNumberOptionComponent>(out var miraNumber) &&
miraNumber.ShiftIncrementToggle)
{
increment /= 2;
}
__instance.Value += increment;
__instance.Value += __instance.Increment;
if (__instance.Value > __instance.ValidRange.max)
{
__instance.Value = __instance.ValidRange.min;
@@ -244,16 +235,7 @@ public static class OptionsPatches
return true;
}
var increment = __instance.Increment;
if (Input.GetKey(KeyCode.LeftShift) &&
__instance.TryGetComponent<MiraNumberOptionComponent>(out var miraNumber) &&
miraNumber.ShiftIncrementToggle)
{
increment /= 2;
}
__instance.Value -= increment;
__instance.Value -= __instance.Increment;
if (__instance.Value < __instance.ValidRange.min)
{
__instance.Value = __instance.ValidRange.max;
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using AmongUs.GameOptions;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using MiraAPI.GameOptions;
@@ -35,19 +34,6 @@ public static class RoleSettingMenuPatches
private static RoleBehaviour? CurrentRole { get; set; }
private static List<IModdedOption>? CurrentRoleOptions { get; set; }
[HarmonyPostfix]
[HarmonyPatch(nameof(RolesSettingsMenu.OnEnable))]
public static void OpenPatch(RolesSettingsMenu __instance)
{
HudManager.Instance.PlayerCam.OverrideScreenShakeEnabled = false;
}
[HarmonyPostfix]
[HarmonyPatch(nameof(RolesSettingsMenu.OnDisable))]
public static void ClosePatch(RolesSettingsMenu __instance)
{
HudManager.Instance.PlayerCam.OverrideScreenShakeEnabled = true;
}
[HarmonyPostfix]
[HarmonyPatch(nameof(RolesSettingsMenu.SetQuotaTab))]
public static void SetQuotaTabPostfix(RolesSettingsMenu __instance)
@@ -71,7 +57,6 @@ public static class RoleSettingMenuPatches
var maskBg = __instance.scrollBar.transform.FindChild("MaskBg");
var hitbox = __instance.scrollBar.transform.FindChild("Hitbox");
var dividerImage = __instance.transform.FindChild("HeaderButtons/DividerImage");
if (GameSettingMenuPatches.SelectedModIdx == 0)
{
@@ -82,64 +67,7 @@ public static class RoleSettingMenuPatches
maskBg.localScale = new Vector3(6.6811f, 3.3563f, 0.5598f);
hitbox.localPosition = new Vector3(0.3297f, -.2333f, 4f);
hitbox.localScale = new Vector3(1, 1, 1);
dividerImage.gameObject.SetActive(true);
var num = 0.662f;
var list = CustomRoleManager.AllRoles.Where(
r => !r.IsCustomRole() && !r.IsRoleBlacklisted() && r.TeamType == RoleTeamTypes.Crewmate && r.Role != RoleTypes.Crewmate &&
r.Role != RoleTypes.CrewmateGhost).ToList();
var list2 = CustomRoleManager.AllRoles.Where(
r => !r.IsCustomRole() && !r.IsRoleBlacklisted() && r.TeamType == RoleTeamTypes.Impostor && r.Role != RoleTypes.Impostor &&
r.Role != RoleTypes.ImpostorGhost).ToList();
if (__instance.roleTabs == null || __instance.roleTabs.Count == 0)
{
var num2 = -1.928f;
__instance.roleTabs = new();
__instance.roleTabs.Add(__instance.AllButton);
foreach (var t in list)
{
__instance.AddRoleTab(t, ref num2);
}
foreach (var t in list2)
{
__instance.AddRoleTab(t, ref num2);
}
}
var categoryHeaderEditRole = Object.Instantiate(
__instance.categoryHeaderEditRoleOrigin,
Vector3.zero,
Quaternion.identity,
__instance.RoleChancesSettings.transform);
categoryHeaderEditRole.SetHeader(StringNames.CrewmateRolesHeader, 20);
categoryHeaderEditRole.transform.localPosition = new Vector3(4.986f, num, -2f);
num -= 0.522f;
var num3 = 0;
foreach (var t in list)
{
__instance.CreateQuotaOption(t, ref num, num3);
num3++;
}
num -= 0.22f;
var categoryHeaderEditRole2 = Object.Instantiate(
__instance.categoryHeaderEditRoleOrigin,
Vector3.zero,
Quaternion.identity,
__instance.RoleChancesSettings.transform);
categoryHeaderEditRole2.SetHeader(StringNames.ImpostorRolesHeader, 20);
categoryHeaderEditRole2.transform.localPosition = new Vector3(4.986f, num, -2f);
num -= 0.522f;
foreach (var t in list2)
{
__instance.CreateQuotaOption(t, ref num, num3);
num3++;
}
return false;
return true;
}
ScrollerNum = 0.522f;
@@ -151,16 +79,15 @@ public static class RoleSettingMenuPatches
maskBg.localScale = new Vector3(6.6811f, 4.1563f, 0.5598f);
hitbox.localPosition = new Vector3(0.3297f, -.6333f, 4f);
hitbox.localScale = new Vector3(1, 1.2f, 1);
dividerImage.gameObject.SetActive(false);
var num4 = 0;
var num3 = 0;
var roleGroups = GameSettingMenuPatches.SelectedMod?.InternalRoles.Values.OfType<ICustomRole>()
.ToLookup(x => x.RoleOptionsGroup);
if (roleGroups is null)
{
return false;
return true;
}
// sort the groups by priority
@@ -173,7 +100,7 @@ public static class RoleSettingMenuPatches
foreach (var grouping in sortedRoleGroups)
{
if (!grouping.Any() || grouping.All(x=> x.Configuration.HideSettings || !x.VisibleInSettings()))
if (!grouping.Any() || grouping.All(x=>x.Configuration.HideSettings))
{
continue;
}
@@ -270,11 +197,11 @@ public static class RoleSettingMenuPatches
continue;
}
var option = CreateQuotaOption(__instance, roleBehaviour, num4);
var option = CreateQuotaOption(__instance, roleBehaviour, num3);
if (option is not null)
{
RoleOptionSettings.Add(option);
num4++;
num3++;
}
}
}
@@ -405,7 +332,7 @@ public static class RoleSettingMenuPatches
}
catch (Exception e)
{
Warning(e);
Logger<MiraApiPlugin>.Warning(e);
}
roleSetting.UpdateValuesAndText(GameOptionsManager.Instance.CurrentGameOptions.RoleOptions);
@@ -479,14 +406,16 @@ public static class RoleSettingMenuPatches
{
if (role is not ICustomRole customRole)
{
Error($"Role {role.NiceName} is not a custom role.");
Logger<MiraApiPlugin>.Error($"Role {role.NiceName} is not a custom role.");
return;
}
RolePositions[GameSettingMenuPatches.SelectedModIdx] = __instance.scrollBar.Inner.localPosition;
__instance.roleDescriptionText.text = customRole.RoleLongDescription;
__instance.roleTitleText.text = role.GetRoleName();
__instance.roleTitleText.text = TranslationController.Instance.GetString(
role.StringName,
new Il2CppReferenceArray<Il2CppSystem.Object>(0));
var imgBg = __instance.AdvancedRolesSettings.transform.FindChild("Imagebackground");
var labelBg = __instance.AdvancedRolesSettings.transform.FindChild("InfoLabelBackground");
@@ -573,7 +502,7 @@ public static class RoleSettingMenuPatches
{
if (role is not ICustomRole customRole)
{
Error($"Role {role.NiceName} is not a custom role.");
Logger<MiraApiPlugin>.Error($"Role {role.NiceName} is not a custom role.");
return null;
}
@@ -590,7 +519,6 @@ public static class RoleSettingMenuPatches
roleOptionSetting.transform.localPosition = new Vector3(-0.1f, ScrollerNum, -2f);
roleOptionSetting.SetRole(GameOptionsManager.Instance.CurrentGameOptions.RoleOptions, role, 20);
roleOptionSetting.titleText.text = role.GetRoleName();
roleOptionSetting.labelSprite.color = customRole.OptionsMenuColor;
roleOptionSetting.OnValueChanged = new Action<OptionBehaviour>(ValueChanged);
roleOptionSetting.SetClickMask(__instance.ButtonClickMask);
@@ -604,19 +532,6 @@ public static class RoleSettingMenuPatches
roleOptionSetting.titleText.color = customRole.OptionsMenuColor.FindAlternateColor();
roleOptionSetting.titleText.horizontalAlignment = HorizontalAlignmentOptions.Left;
if (customRole.Configuration.Icon != null)
{
var roleIcon = new GameObject("RoleIcon");
roleIcon.transform.parent = roleOptionSetting.transform;
roleIcon.transform.localScale = new(.25f, .25f, 1);
roleIcon.layer = LayerMask.NameToLayer("UI");
roleIcon.transform.localPosition = new Vector3(-1.3f, -0.3f, -2f);
var rend = roleIcon.AddComponent<SpriteRenderer>();
rend.sprite = customRole.Configuration.Icon.LoadAsset();
rend.maskInteraction = SpriteMaskInteraction.VisibleInsideMask;
}
if (GameSettingMenuPatches.SelectedMod is null ||
GameSettingMenuPatches.SelectedMod.InternalOptionGroups
.Exists(x => x.GroupVisible() && x.OptionableType == role.GetType() && x.Options.Any(y => y.Visible())))
@@ -636,6 +551,18 @@ public static class RoleSettingMenuPatches
passiveButton.interactableHoveredColor = Color.white;
passiveButton.OnClick.AddListener((UnityAction)(() => { ChangeTab(role, __instance); }));
if (customRole.Configuration.Icon != null)
{
var roleIcon = new GameObject("RoleIcon");
roleIcon.transform.parent = roleOptionSetting.transform;
roleIcon.transform.localScale = new(.25f, .25f, 1);
roleIcon.layer = LayerMask.NameToLayer("UI");
roleIcon.transform.localPosition = new Vector3(-1.3f, -0.3f, -2f);
var rend = roleIcon.AddComponent<SpriteRenderer>();
rend.sprite = customRole.Configuration.Icon.LoadAsset();
rend.maskInteraction = SpriteMaskInteraction.VisibleInsideMask;
}
}
if (customRole.Configuration is { MaxRoleCount: 0 })
+1 -30
View File
@@ -1,8 +1,5 @@
using System.Globalization;
using System.Linq;
using HarmonyLib;
using MiraAPI.GameOptions;
using MiraAPI.GameOptions.OptionTypes;
using MiraAPI.Utilities;
using UnityEngine;
@@ -31,33 +28,7 @@ public static class SettingPatches
string result;
var suffix = (MiraNumberSuffixes)__instance.SuffixType;
var custom =
ModdedOptionsManager.ModdedOptions.Values.FirstOrDefault(opt =>
opt.OptionBehaviour != null && opt.OptionBehaviour.Data == __instance);
if (custom is ModdedNumberOption moddedNumberOption)
{
if (moddedNumberOption.NegativeWordValue != "#" && (int)value == -1)
{
result = $"<b>{moddedNumberOption.NegativeWordValue}</b>";
}
else if (moddedNumberOption.ZeroWordValue != "#" && Mathf.Abs(value) < 0.0001f)
{
result = $"<b>{moddedNumberOption.ZeroWordValue}</b>";
}
else
{
result = suffix switch
{
MiraNumberSuffixes.None => value.ToString(__instance.FormatString, NumberFormatInfo.InvariantInfo),
MiraNumberSuffixes.Multiplier => value.ToString(__instance.FormatString, NumberFormatInfo.InvariantInfo) + "x",
MiraNumberSuffixes.Percent => value.ToString(__instance.FormatString, NumberFormatInfo.InvariantInfo) + "%",
_ => TranslationController.Instance.GetString(
StringNames.GameSecondsAbbrev,
(Il2CppSystem.Object[])[value.ToString(__instance.FormatString, CultureInfo.InvariantCulture)]),
};
}
}
else if (__instance.ZeroIsInfinity && Mathf.Abs(value) < 0.0001f)
if (__instance.ZeroIsInfinity && Mathf.Abs(value) < 0.0001f)
{
result = "<b>∞</b>";
}
+9 -50
View File
@@ -1,5 +1,4 @@
using System.Globalization;
using System.Linq;
using System.Linq;
using HarmonyLib;
using MiraAPI.Events;
using MiraAPI.Events.Vanilla.Gameplay;
@@ -68,53 +67,15 @@ internal static class PlayerControlPatches
}
[HarmonyPrefix]
[HarmonyPatch(nameof(PlayerControl.CheckMurder))]
public static void PlayerControlCheckMurderPrefix(PlayerControl __instance, PlayerControl target, ref bool __runOriginal)
[HarmonyPatch(nameof(PlayerControl.RpcMurderPlayer))]
// ReSharper disable once InconsistentNaming
// Note: This method is partially inlined in 2025.5.20 but we already use custom murder RPCs.
public static void PlayerControlMurderPrefix(PlayerControl __instance, PlayerControl target, ref bool didSucceed)
{
__runOriginal = false;
__instance.logger.Debug($"Checking if {__instance.PlayerId} murdered {(target == null ? "null player" : target.PlayerId.ToString(NumberFormatInfo.InvariantInfo))}");
__instance.isKilling = false;
if (AmongUsClient.Instance.IsGameOver || !AmongUsClient.Instance.AmHost)
{
return;
}
if (!target || __instance.Data.IsDead || !__instance.Data.Role.IsImpostor || __instance.Data.Disconnected)
{
int num = target ? target!.PlayerId : -1;
__instance.logger.Warning($"Bad kill from {__instance.PlayerId} to {num}");
__instance.RpcMurderPlayer(target, false);
return;
}
NetworkedPlayerInfo data = target!.Data;
if (data == null || data.IsDead || target.inVent || target.MyPhysics.Animations.IsPlayingEnterVentAnimation() ||
target.MyPhysics.Animations.IsPlayingAnyLadderAnimation() || target.inMovingPlat)
{
__instance.logger.Warning("Invalid target data for kill");
__instance.RpcMurderPlayer(target, false);
return;
}
if (MeetingHud.Instance)
{
__instance.logger.Warning("Tried to kill while a meeting was starting");
__instance.RpcMurderPlayer(target, false);
return;
}
var beforeMurderEvent = new BeforeMurderEvent(__instance, target);
MiraEventManager.InvokeEvent(beforeMurderEvent);
if (beforeMurderEvent.IsCancelled)
{
return;
}
__instance.isKilling = true;
__instance.RpcMurderPlayer(target, true);
didSucceed = beforeMurderEvent.IsCancelled;
}
[HarmonyPostfix]
@@ -127,19 +88,17 @@ internal static class PlayerControlPatches
return;
}
var role = __instance.Data?.Role;
foreach (var button in CustomButtonManager.CustomButtons)
{
if (role == null)
if (__instance.Data?.Role == null)
{
continue;
}
try
{
if (!button.Enabled(role))
if (!button.Enabled(__instance.Data?.Role))
{
button.SetActive(false, role);
continue;
}
@@ -147,7 +106,7 @@ internal static class PlayerControlPatches
}
catch (System.Exception e)
{
Error($"Failed to update custom button {button.GetType().Name}: {e}");
Logger<MiraApiPlugin>.Error($"Failed to update custom button {button.GetType().Name}: {e}");
}
}
}
+2 -2
View File
@@ -33,8 +33,8 @@ internal static class EjectionPatches
__instance.completeString = role.GetCustomEjectionMessage(__instance.initData.networkedPlayer);
}
[HarmonyPatch(typeof(ExileController), nameof(ExileController.ReEnableGameplay))]
public static class ReEnableGameplayPatch
[HarmonyPatch(typeof(ExileController), nameof(ExileController.WrapUp))]
public static class WrapUpPatch
{
public static void Postfix()
{
+2 -3
View File
@@ -1,12 +1,11 @@
using HarmonyLib;
using MiraAPI.Roles;
using MiraAPI.Utilities;
using UnityEngine;
namespace MiraAPI.Patches.Roles;
/// <summary>
/// Patches the haunt menu to show the actual role name rather than team.
/// Patches the haunt menu to show the actual role name rather then team.
/// </summary>
[HarmonyPatch(typeof(HauntMenuMinigame), nameof(HauntMenuMinigame.SetHauntTarget))]
// SetFilterText was INLINED
@@ -23,7 +22,7 @@ public static class HauntMenuMinigamePatch
var role = __instance.HauntTarget.Data.Role;
var color = role is ICustomRole custom ? custom.RoleColor : role.TeamColor;
__instance.FilterText.text = role.GetRoleName();
__instance.FilterText.text = role.NiceName;
__instance.FilterText.color = color;
}
}
-49
View File
@@ -1,49 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using AmongUs.GameOptions;
using HarmonyLib;
using MiraAPI.Roles;
using UnityEngine;
namespace MiraAPI.Patches.Roles;
[HarmonyPatch(typeof(HowToPlayScene), nameof(HowToPlayScene.OpenRolesSelectionMenu))]
internal static class HowToPlayPatch
{
// yes i patched the entire method
private static void Prefix(HowToPlayScene __instance)
{
__instance.sceneIndex = 0;
__instance.category = HowToPlayScene.HowToPlayCategory.RolesSelection;
__instance.startPage.SetActive(false);
if (__instance.roleButtonsParent.childCount == 0)
{
using (IEnumerator<RoleBehaviour> enumerator = RoleManager.Instance.AllRoles.ToArray().Where(x => !x.IsCustomRole()).GetEnumerator())
{
while (enumerator.MoveNext())
{
RoleBehaviour role = enumerator.Current;
if (!role.IsSimpleRole && role.Role != RoleTypes.CrewmateGhost && role.Role != RoleTypes.ImpostorGhost)
{
HowToPlayRoleButton component = Object.Instantiate(__instance.roleButtonPrefab, __instance.roleButtonsParent).GetComponent<HowToPlayRoleButton>();
Sprite roleIcon = __instance.rolesScenes.ToArray().First(r => r.role == role.Role).roleIcon;
component.SetRoleInfo(role, roleIcon);
component.SetButtonAction((Il2CppSystem.Action)(() =>
{
__instance.OpenRolePage(role.Role);
}));
__instance.controllerSelectables.Add(component.GetComponent<PassiveButton>());
}
}
}
foreach (UiElement uiElement in __instance.controllerSelectables)
{
uiElement.ReceiveMouseOut();
}
ControllerManager.Instance.NewScene(__instance.name, __instance.closeButton, __instance.defaultButtonSelected, __instance.controllerSelectables, false);
}
__instance.DisableAllScenes();
__instance.roleSelectionScene.SetActive(true);
ControllerManager.Instance.SetDefaultSelection(__instance.defaultButtonSelected, null);
}
}
+1 -1
View File
@@ -46,7 +46,7 @@ public static class KillButtonPatches
}
/// <summary>
/// Use Custom Murder instead of Murder Player from vanilla.
/// Use Custom Murder if player is custom role.
/// </summary>
[HarmonyPrefix]
[HarmonyPatch(nameof(KillButton.DoClick))]
@@ -1,183 +0,0 @@
using AmongUs.GameOptions;
using HarmonyLib;
using Il2CppSystem.Collections.Generic;
using System.Linq;
using MiraAPI.Roles;
namespace MiraAPI.Patches.Roles;
[HarmonyPatch(typeof(LogicRoleSelectionHnS))]
public static class LogicRoleSelectionHnsPatch
{
[HarmonyPrefix]
[HarmonyPatch(nameof(LogicRoleSelectionHnS.AssignRolesForTeam))]
public static bool AssignRolesForTeam(
LogicRoleSelectionHnS __instance,
List<NetworkedPlayerInfo> players,
IGameOptions opts,
RoleTeamTypes team,
int teamMax,
Il2CppSystem.Nullable<RoleTypes> defaultRole)
{
Error($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: Team: {team}, Max: {teamMax}, Players: {players.Count}, DefaultRole: {defaultRole}");
int num = 0;
IRoleOptionsCollection roleOptions = opts.RoleOptions;
var source = RoleManager.Instance.AllRoles.ToArray()
.Where(role => role.TeamType == team && !RoleManager.IsGhostRole(role.Role) &&
CustomRoleUtils.CanSpawnOnCurrentMode(role));
var assignmentData = source.Where(x => !x.IsDead).Select(role =>
new RoleManager.RoleAssignmentData(
role,
roleOptions.GetNumPerGame(role.Role),
roleOptions.GetChancePerGame(role.Role))).ToList();
var source2 = CustomRoleUtils.GetPossibleRoles(assignmentData, x => x.Chance == 100);
var guaranteedRoles = source.Where(x => source2.Contains(((ushort)x.Role, 100)));
List<RoleTypes> list = new List<RoleTypes>();
if (team == RoleTeamTypes.Crewmate)
{
Error($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: Before Guaranteed Assignment");
foreach (RoleManager.RoleAssignmentData roleAssignmentData in guaranteedRoles.Select((x) =>
new RoleManager.RoleAssignmentData(x, roleOptions.GetNumPerGame(x.Role), 100)))
{
while (true)
{
RoleManager.RoleAssignmentData roleAssignmentData2 = roleAssignmentData;
int count = roleAssignmentData2.Count;
roleAssignmentData2.Count = count - 1;
if (count <= 0)
{
break;
}
list.Add(roleAssignmentData.Role.Role);
}
}
Error($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: After Guaranteed Assignment");
AssignRolesFromList(players, teamMax, list, ref num);
var list2 = source.Where(x => !x.IsDead).Select(role =>
new RoleManager.RoleAssignmentData(
role,
roleOptions.GetNumPerGame(role.Role),
roleOptions.GetChancePerGame(role.Role))).ToList();
list.Clear();
foreach (RoleManager.RoleAssignmentData roleAssignmentData3 in list2)
{
for (int i = 0; i < roleAssignmentData3.Count; i++)
{
if (HashRandom.Next(101) < roleAssignmentData3.Chance)
{
list.Add(roleAssignmentData3.Role.Role);
}
}
}
Error($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: After Potential Assignment");
AssignRolesFromList(players, teamMax, list, ref num);
var basicRole = RoleTypes.Engineer;
while (list.Count < players.Count && list.Count + num < teamMax)
{
list.Add(basicRole);
}
AssignRolesFromList(players, teamMax, list, ref num);
Error($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: After Fallback Assignment");
}
else if (team == RoleTeamTypes.Impostor)
{
Error($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: Before Guaranteed Assignment");
var newImpostors = new List<NetworkedPlayerInfo>();
// Specified Seeker
if (__instance.hnsManager.LogicOptionsHnS.HasImpostorPlayerID() &&
__instance.hnsManager.LogicOptionsHnS.ValidateImpostorPlayerID(players) &&
!AmongUsClient.Instance.IsGamePublic)
{
NetworkedPlayerInfo networkedPlayerInfo = players.ToArray()
.First(p => p.PlayerId == __instance.hnsManager.LogicOptionsHnS.ImpostorPlayerID());
players.Remove(networkedPlayerInfo);
newImpostors.Add(networkedPlayerInfo);
Error($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: Seeker is {networkedPlayerInfo.PlayerName}, ID: {networkedPlayerInfo.PlayerId}");
}
// Random Seeker
else
{
int num2 = 0;
while (num2 < teamMax && players.Count > 0)
{
PseudoRandomList<NetworkedPlayerInfo> pseudoRandomList = new PseudoRandomList<NetworkedPlayerInfo>(AmongUsClient.Instance.GameId);
players._items.Do(x => pseudoRandomList.Add(x));
for (int i = 0; i < GameData.RoundsPlayedInSession; i++)
{
pseudoRandomList.PickRandom();
}
NetworkedPlayerInfo networkedPlayerInfo = pseudoRandomList.PickRandom();
players.Remove(networkedPlayerInfo);
newImpostors.Add(networkedPlayerInfo);
num2++;
Error($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: Seeker is {networkedPlayerInfo.PlayerName}, ID: {networkedPlayerInfo.PlayerId}");
}
}
Error($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: After Guaranteed Assignment");
foreach (RoleManager.RoleAssignmentData roleAssignmentData in guaranteedRoles.Select((x) =>
new RoleManager.RoleAssignmentData(x, roleOptions.GetNumPerGame(x.Role), 100)))
{
while (true)
{
RoleManager.RoleAssignmentData roleAssignmentData2 = roleAssignmentData;
int count = roleAssignmentData2.Count;
roleAssignmentData2.Count = count - 1;
if (count <= 0)
{
break;
}
list.Add(roleAssignmentData.Role.Role);
}
}
AssignRolesFromList(newImpostors, teamMax, list, ref num);
var list2 = source.Where(x => !x.IsDead).Select(role =>
new RoleManager.RoleAssignmentData(
role,
roleOptions.GetNumPerGame(role.Role),
roleOptions.GetChancePerGame(role.Role))).ToList();
list.Clear();
foreach (RoleManager.RoleAssignmentData roleAssignmentData3 in list2)
{
for (int i = 0; i < roleAssignmentData3.Count; i++)
{
if (HashRandom.Next(101) < roleAssignmentData3.Chance)
{
list.Add(roleAssignmentData3.Role.Role);
}
}
}
AssignRolesFromList(newImpostors, teamMax, list, ref num);
var basicRole = RoleTypes.Impostor;
while (list.Count < newImpostors.Count && list.Count + num < teamMax)
{
list.Add(basicRole);
}
AssignRolesFromList(newImpostors, teamMax, list, ref num);
}
return false;
}
public static void AssignRolesFromList(List<NetworkedPlayerInfo> players, int teamMax, List<RoleTypes> roleList, ref int rolesAssigned)
{
while (roleList.Count > 0 && players.Count > 0 && rolesAssigned < teamMax)
{
int index = HashRandom.FastNext(roleList.Count);
RoleTypes roleType = roleList[index];
roleList.RemoveAt(index);
int index2 = HashRandom.FastNext(players.Count);
players[index2].Object.RpcSetRole(roleType, false);
players.RemoveAt(index2);
rolesAssigned++;
}
}
}
@@ -1,93 +0,0 @@
using AmongUs.GameOptions;
using HarmonyLib;
using Il2CppSystem.Collections.Generic;
using System.Linq;
using MiraAPI.Roles;
namespace MiraAPI.Patches.Roles;
[HarmonyPatch(typeof(LogicRoleSelectionNormal))]
public static class LogicRoleSelectionNormalPatch
{
[HarmonyPrefix]
[HarmonyPatch(nameof(LogicRoleSelectionNormal.AssignRolesForTeam))]
public static bool AssignRolesForTeam(
LogicRoleSelectionNormal __instance,
List<NetworkedPlayerInfo> players,
IGameOptions opts,
RoleTeamTypes team,
int teamMax,
Il2CppSystem.Nullable<RoleTypes> defaultRole)
{
int num = 0;
var source = RoleManager.Instance.AllRoles.ToArray()
.Where(role => role.TeamType == team && !RoleManager.IsGhostRole(role.Role) &&
CustomRoleUtils.CanSpawnOnCurrentMode(role));
List<RoleTypes> list = new List<RoleTypes>();
IRoleOptionsCollection roleOptions = opts.RoleOptions;
var assignmentData = source.Where(x => !x.IsDead).Select(role =>
new RoleManager.RoleAssignmentData(
role,
roleOptions.GetNumPerGame(role.Role),
roleOptions.GetChancePerGame(role.Role))).ToList();
var source2 = CustomRoleUtils.GetPossibleRoles(assignmentData, x => x.Chance == 100);
var guaranteedRoles = source.Where(x => source2.Contains(((ushort)x.Role, 100)));
foreach (RoleManager.RoleAssignmentData roleAssignmentData in guaranteedRoles.Select((x) =>
new RoleManager.RoleAssignmentData(x, roleOptions.GetNumPerGame(x.Role), 100)))
{
while (true)
{
RoleManager.RoleAssignmentData roleAssignmentData2 = roleAssignmentData;
int count = roleAssignmentData2.Count;
roleAssignmentData2.Count = count - 1;
if (count <= 0)
{
break;
}
list.Add(roleAssignmentData.Role.Role);
}
}
__instance.AssignRolesFromList(players, teamMax, list, ref num);
var list2 = source.Where(x => !x.IsDead).Select(role =>
new RoleManager.RoleAssignmentData(
role,
roleOptions.GetNumPerGame(role.Role),
roleOptions.GetChancePerGame(role.Role))).ToList();
list.Clear();
foreach (RoleManager.RoleAssignmentData roleAssignmentData3 in list2)
{
for (int i = 0; i < roleAssignmentData3.Count; i++)
{
if (HashRandom.Next(101) < roleAssignmentData3.Chance)
{
list.Add(roleAssignmentData3.Role.Role);
}
}
}
__instance.AssignRolesFromList(players, teamMax, list, ref num);
var defaultRole2 = team is RoleTeamTypes.Crewmate ? RoleTypes.Crewmate : RoleTypes.Impostor;
try
{
defaultRole2 = defaultRole.Value;
}
catch
{
// Ignored
}
while (list.Count < players.Count && list.Count + num < teamMax)
{
list.Add(defaultRole2);
}
__instance.AssignRolesFromList(players, teamMax, list, ref num);
return false;
}
}
+2 -2
View File
@@ -20,7 +20,7 @@ public static class NameTagPatch
return false;
}
if (PlayerControl.LocalPlayer.Data?.Role?.IsImpostor == true && otherPlayerRole.IsImpostor)
if (PlayerControl.LocalPlayer.Data.Role.IsImpostor && otherPlayerRole.IsImpostor)
{
return true;
}
@@ -30,7 +30,7 @@ public static class NameTagPatch
return true;
}
if (!PlayerControl.LocalPlayer || !PlayerControl.LocalPlayer.Data || !PlayerControl.LocalPlayer.Data?.Role || !otherPlayerRole)
if (!PlayerControl.LocalPlayer || !PlayerControl.LocalPlayer.Data || !PlayerControl.LocalPlayer.Data.Role || !otherPlayerRole)
{
__result = Color.white;
}
+2 -3
View File
@@ -1,7 +1,6 @@
using System.Linq;
using AmongUs.GameOptions;
using HarmonyLib;
using InnerNet;
using MiraAPI.Events;
using MiraAPI.Events.Vanilla.Gameplay;
using MiraAPI.Modifiers;
@@ -26,7 +25,7 @@ public static class RoleManagerPatches
var data = targetPlayer.Data;
if (data == null)
{
Error("It shouldn't be possible, but " + targetPlayer.name + " still doesn't have PlayerData during role selection.");
Debug.LogError("It shouldn't be possible, but " + targetPlayer.name + " still doesn't have PlayerData during role selection.");
return false;
}
if (data.Role)
@@ -34,7 +33,7 @@ public static class RoleManagerPatches
data.Role.Deinitialize(targetPlayer);
Object.Destroy(data.Role.gameObject);
}
var roleBehaviour = Object.Instantiate<RoleBehaviour>(__instance.AllRoles.ToArray().First(r => r.Role == roleType), data.gameObject.transform);
var roleBehaviour = Object.Instantiate<RoleBehaviour>(__instance.AllRoles.First(r => r.Role == roleType), data.gameObject.transform);
roleBehaviour.Initialize(targetPlayer);
targetPlayer.Data.Role = roleBehaviour;
targetPlayer.Data.RoleType = roleType;
@@ -9,17 +9,17 @@ namespace MiraAPI.Patches.Roles;
/// <summary>
/// Patches to return the correct role counts.
/// </summary>
[HarmonyPatch(typeof(RoleOptionsCollectionV10))]
[HarmonyPatch(typeof(RoleOptionsCollectionV09))]
public static class RoleOptionsCollectionPatch
{
/// <summary>
/// This patch fixes GetNumPerGame being inlined (2025.9.9) in the original code.
/// This patch fixes GetNumPerGame being inlined (2025.5.20) in the original code.
/// </summary>
[HarmonyPrefix]
[HarmonyPatch(nameof(RoleOptionsCollectionV10.AnyRolesEnabled))]
public static bool AnyRolesEnabledPrefix(RoleOptionsCollectionV10 __instance, ref bool __result)
[HarmonyPatch(nameof(RoleOptionsCollectionV09.AnyRolesEnabled))]
public static bool AnyRolesEnabledPrefix(RoleOptionsCollectionV09 __instance, ref bool __result)
{
foreach (KeyValuePair<RoleTypes, RoleDataV10> keyValuePair in __instance.roles)
foreach (KeyValuePair<RoleTypes, RoleDataV09> keyValuePair in __instance.roles)
{
if (__instance.GetNumPerGame(keyValuePair.Key) > 0)
{
@@ -34,7 +34,7 @@ public static class RoleOptionsCollectionPatch
/// </summary>
/// <returns>Return false to skip original method, true to not.</returns>
[HarmonyPrefix]
[HarmonyPatch(nameof(RoleOptionsCollectionV10.GetChancePerGame))]
[HarmonyPatch(nameof(RoleOptionsCollectionV09.GetChancePerGame))]
public static bool GetChancePrefix(RoleTypes role, ref int __result)
{
if (!CustomRoleManager.GetCustomRoleBehaviour(role, out var customRole) || customRole == null)
@@ -51,7 +51,7 @@ public static class RoleOptionsCollectionPatch
var chance = customRole.GetChance();
if (chance == null)
{
Error($"Chance is null, defaulting to zero.");
Logger<MiraApiPlugin>.Error($"Chance is null, defaulting to zero.");
chance = 0;
}
@@ -64,7 +64,7 @@ public static class RoleOptionsCollectionPatch
/// </summary>
/// <returns>Return false to skip original method, true to not.</returns>
[HarmonyPrefix]
[HarmonyPatch(nameof(RoleOptionsCollectionV10.GetNumPerGame))]
[HarmonyPatch(nameof(RoleOptionsCollectionV09.GetNumPerGame))]
public static bool GetNumPrefix(RoleTypes role, ref int __result)
{
if (!CustomRoleManager.GetCustomRoleBehaviour(role, out var customRole) || customRole == null)
@@ -81,7 +81,7 @@ public static class RoleOptionsCollectionPatch
var count = customRole.GetCount();
if (count == null)
{
Error($"Count is null, defaulting to zero.");
Logger<MiraApiPlugin>.Error($"Count is null, defaulting to zero.");
count = 0;
}
+8 -19
View File
@@ -1,6 +1,4 @@
using System.Linq;
using HarmonyLib;
using Reactor.Utilities;
using HarmonyLib;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
@@ -10,29 +8,20 @@ namespace MiraAPI.Patches;
[HarmonyPatch(typeof(ServerDropdown), nameof(ServerDropdown.FillServerOptions))]
public static class ServerDropdownPatch
{
public static bool RegionEquals(this IRegionInfo region, IRegionInfo other)
{
return region.Name == other.Name &&
region.TranslateName == other.TranslateName &&
region.PingServer == other.PingServer &&
region.TargetServer == other.TargetServer &&
region.Servers.All(s=>other.Servers.Any(x=>x.Equals(s)));
}
public static bool Prefix(ServerDropdown __instance)
{
var num = 0;
__instance.background.size = new Vector2(8.4f, 4.8f);
foreach (var regionInfo in ServerManager.Instance.AvailableRegions)
foreach (var regionInfo in DestroyableSingleton<ServerManager>.Instance.AvailableRegions)
{
var findingGame = SceneManager.GetActiveScene().name is "FindAGame";
if (ServerManager.Instance.CurrentRegion.RegionEquals(regionInfo))
if (DestroyableSingleton<ServerManager>.Instance.CurrentRegion.Equals(regionInfo))
{
__instance.defaultButtonSelected = __instance.firstOption;
__instance.firstOption.ChangeButtonText(
TranslationController.Instance.GetStringWithDefault(
DestroyableSingleton<TranslationController>.Instance.GetStringWithDefault(
regionInfo.TranslateName,
regionInfo.Name));
}
@@ -45,11 +34,11 @@ public static class ServerDropdownPatch
{
x += 2;
}
var y = -0.55f * (num / 2f);
var y = -0.55f * (num / 2);
serverListButton.transform.localPosition = new Vector3(x, __instance.y_posButton + y, -1f);
serverListButton.transform.localScale = Vector3.one;
serverListButton.Text.text =
TranslationController.Instance.GetStringWithDefault(
DestroyableSingleton<TranslationController>.Instance.GetStringWithDefault(
regionInfo.TranslateName,
regionInfo.Name);
serverListButton.Text.ForceMeshUpdate();
@@ -58,9 +47,9 @@ public static class ServerDropdownPatch
__instance.controllerSelectable.Add(serverListButton.Button);
__instance.background.transform.localPosition = new Vector3(
findingGame ? 2f : 0f,
__instance.initialYPos + -0.3f * (num / 2f),
__instance.initialYPos + (-0.3f * (num / 2)),
0f);
__instance.background.size = new Vector2(__instance.background.size.x, 1.2f + 0.6f * (num / 2f));
__instance.background.size = new Vector2(__instance.background.size.x, 1.2f + (0.6f * (num / 2)));
num++;
}
}
@@ -1,6 +1,5 @@
using System.Runtime.CompilerServices;
using HarmonyLib;
using Hazel;
using Il2CppSystem.Text;
namespace MiraAPI.Patches.Stubs;
@@ -230,76 +229,4 @@ public static class RoleBehaviourStubs
{
// nothing needed
}
/// <summary>
/// Stub method for RoleBehaviour.UseSecondaryAbility.
/// </summary>
/// <param name="instance">The RoleBehaviour object.</param>
[HarmonyReversePatch]
[HarmonyPatch(typeof(RoleBehaviour), nameof(RoleBehaviour.UseSecondaryAbility))]
[MethodImpl(MethodImplOptions.NoInlining)]
public static void UseSecondaryAbility(RoleBehaviour instance)
{
// nothing needed
}
/// <summary>
/// Stub method for RoleBehaviour.OnRoleSet.
/// </summary>
/// <param name="instance">The RoleBehaviour object.</param>
[HarmonyReversePatch]
[HarmonyPatch(typeof(RoleBehaviour), nameof(RoleBehaviour.OnRoleSet))]
[MethodImpl(MethodImplOptions.NoInlining)]
public static void OnRoleSet(RoleBehaviour instance)
{
// nothing needed
}
/// <summary>
/// Stub method for RoleBehaviour.InitializeMeetingAbilityButton.
/// </summary>
/// <param name="instance">The RoleBehaviour object.</param>
[HarmonyReversePatch]
[HarmonyPatch(typeof(RoleBehaviour), nameof(RoleBehaviour.InitializeMeetingAbilityButton))]
[MethodImpl(MethodImplOptions.NoInlining)]
public static void InitializeMeetingAbilityButton(RoleBehaviour instance)
{
// nothing needed
}
/// <summary>
/// Stub method for RoleBehaviour.FindClosestBody.
/// </summary>
/// <param name="instance">The RoleBehaviour object.</param>
[HarmonyReversePatch]
[HarmonyPatch(typeof(RoleBehaviour), nameof(RoleBehaviour.FindClosestBody))]
[MethodImpl(MethodImplOptions.NoInlining)]
public static DeadBody FindClosestBody(RoleBehaviour instance)
{
return null!;
}
/// <summary>
/// Stub method for RoleBehaviour.HandleRoleRpc.
/// </summary>
/// <param name="instance">The RoleBehaviour object.</param>
[HarmonyReversePatch]
[HarmonyPatch(typeof(RoleBehaviour), nameof(RoleBehaviour.HandleRoleRpc))]
[MethodImpl(MethodImplOptions.NoInlining)]
public static void HandleRoleRpc(RoleBehaviour instance, byte callId, MessageReader reader)
{
// nothing needed
}
/// <summary>
/// Stub method for RoleBehaviour.KillAnimSpecialSetup.
/// </summary>
/// <param name="instance">The RoleBehaviour object.</param>
[HarmonyReversePatch]
[HarmonyPatch(typeof(RoleBehaviour), nameof(RoleBehaviour.KillAnimSpecialSetup))]
[MethodImpl(MethodImplOptions.NoInlining)]
public static void KillAnimSpecialSetup(RoleBehaviour instance, DeadBody deadBody, PlayerControl killer, PlayerControl victim)
{
// nothing needed
}
}
-25
View File
@@ -12,31 +12,6 @@ public interface IMiraPlugin
/// </summary>
string OptionsTitleText { get; }
/// <summary>
/// Gets the name for the first custom category in the game options menu, if any.
/// </summary>
public virtual string CustomOptionMenuNameOne => "Custom Category 1";
/// <summary>
/// Gets the name for the second custom category in the game options menu, if any.
/// </summary>
public virtual string CustomOptionMenuNameTwo => "Custom Category 2";
/// <summary>
/// Gets the description for the second custom category in the game options menu, if any.
/// </summary>
public virtual string ModifierMenuDescription => "Configure modifiers and their settings here!";
/// <summary>
/// Gets the description for the first custom category in the game options menu, if any.
/// </summary>
public virtual string CustomOptionMenuOneDescription => "Apply game settings for this mod!";
/// <summary>
/// Gets the description for the second custom category in the game options menu, if any.
/// </summary>
public virtual string CustomOptionMenuTwoDescription => "Apply game settings for this mod!";
/// <summary>
/// Gets the BepInEx configuration file for the plugin.
/// </summary>
+1 -1
View File
@@ -51,7 +51,7 @@ public class MiraPluginInfo
public IReadOnlyCollection<CustomActionButton> Buttons { get; private set; } = null!;
/// <summary>
/// Gets a read only dictionary of this plugin's custom presets.
/// Gets a read only dictionary of this plugin's custom game modes.
/// </summary>
public IReadOnlyCollection<OptionPreset> Presets { get; internal set; } = null!;
+23 -23
View File
@@ -70,14 +70,14 @@ public sealed class MiraPluginManager
if (!method.IsStatic)
{
Error($"Event method {method.Name} in {type.Name} must be static.");
Logger<MiraApiPlugin>.Error($"Event method {method.Name} in {type.Name} must be static.");
continue;
}
var parameters = method.GetParameters();
if (parameters.Length != 1 || !parameters[0].ParameterType.IsSubclassOf(typeof(MiraEvent)))
{
Error($"Invalid event registration method {method.Name} in {type.Name}");
Logger<MiraApiPlugin>.Error($"Invalid event registration method {method.Name} in {type.Name}");
continue;
}
@@ -131,7 +131,7 @@ public sealed class MiraPluginManager
info.SavePublicCollections();
PresetManager.CreateDefaultPreset(info);
PresetManager.LoadPresets(info);
Info($"Registering mod {pluginInfo.Metadata.GUID} with Mira API.");
Logger<MiraApiPlugin>.Info($"Registering mod {pluginInfo.Metadata.GUID} with Mira API.");
};
IL2CPPChainloader.Instance.Finished += PaletteManager.RegisterAllColors;
IL2CPPChainloader.Instance.Finished += () =>
@@ -167,7 +167,7 @@ public sealed class MiraPluginManager
}
catch (Exception e)
{
Error($"Failed to register game over {type.Name}: {e}");
Logger<MiraApiPlugin>.Error($"Failed to register game over {type.Name}: {e}");
return false;
}
}
@@ -190,7 +190,7 @@ public sealed class MiraPluginManager
{
if (property.GetMethod?.IsStatic == true)
{
Error($"Option property {property.Name} in {type.Name} must not be static.");
Logger<MiraApiPlugin>.Error($"Option property {property.Name} in {type.Name} must not be static.");
continue;
}
@@ -211,14 +211,14 @@ public sealed class MiraPluginManager
foreach (var field in type.GetFields().Where(f => f.FieldType.IsAssignableTo(typeof(IModdedOption))))
{
Error($"{field.Name} is a field, not a property. Use properties for options.");
Logger<MiraApiPlugin>.Error($"{field.Name} is a field, not a property. Use properties for options.");
}
return true;
}
catch (Exception e)
{
Error($"Failed to register options for {type.Name}: {e.ToString()}");
Logger<MiraApiPlugin>.Error($"Failed to register options for {type.Name}: {e.ToString()}");
}
return false;
}
@@ -235,7 +235,7 @@ public sealed class MiraPluginManager
if (!ModList.GetById(pluginInfo.PluginId).IsRequiredOnAllClients)
{
Error("Custom roles are only supported on all clients.");
Logger<MiraApiPlugin>.Error("Custom roles are only supported on all clients.");
return false;
}
@@ -244,7 +244,7 @@ public sealed class MiraPluginManager
}
catch (Exception e)
{
Error($"Failed to register role for {type.Name}: {e}");
Logger<MiraApiPlugin>.Error($"Failed to register role for {type.Name}: {e}");
}
return false;
}
@@ -260,7 +260,7 @@ public sealed class MiraPluginManager
if (!type.IsStatic())
{
Error($"Color class {type.Name} must be static.");
Logger<MiraApiPlugin>.Error($"Color class {type.Name} must be static.");
return;
}
@@ -273,7 +273,7 @@ public sealed class MiraPluginManager
if (property.GetValue(null) is not CustomColor color)
{
Error($"Color property {property.Name} in {type.Name} is not a CustomColor.");
Logger<MiraApiPlugin>.Error($"Color property {property.Name} in {type.Name} is not a CustomColor.");
continue;
}
@@ -282,12 +282,12 @@ public sealed class MiraPluginManager
foreach (var field in type.GetFields().Where(f => f.FieldType.IsAssignableTo(typeof(CustomColor))))
{
Error($"{field.Name} is a field, not a property. Use properties for colors.");
Logger<MiraApiPlugin>.Error($"{field.Name} is a field, not a property. Use properties for colors.");
}
}
catch (Exception e)
{
Error($"Failed to register color class {type.Name}: {e}");
Logger<MiraApiPlugin>.Error($"Failed to register color class {type.Name}: {e}");
}
}
@@ -299,7 +299,7 @@ public sealed class MiraPluginManager
}
catch (Exception e)
{
Error($"Failed to register modifier {type.Name}: {e}");
Logger<MiraApiPlugin>.Error($"Failed to register modifier {type.Name}: {e}");
return false;
}
}
@@ -312,7 +312,7 @@ public sealed class MiraPluginManager
}
catch (Exception e)
{
Error($"Failed to register button {type.Name}: {e}");
Logger<MiraApiPlugin>.Error($"Failed to register button {type.Name}: {e}");
}
return false;
@@ -355,13 +355,13 @@ public sealed class MiraPluginManager
if (property.GetMethod?.IsStatic == true)
{
Error($"Option property {property.Name} in {type.Name} must not be static.");
Logger<MiraApiPlugin>.Error($"Option property {property.Name} in {type.Name} must not be static.");
continue;
}
if (property.GetValue(tabInstance) is not ConfigEntryBase configEntry)
{
Error($"Option property {property.Name} in {type.Name} has to be a config entry.");
Logger<MiraApiPlugin>.Error($"Option property {property.Name} in {type.Name} has to be a config entry.");
continue;
}
@@ -376,14 +376,14 @@ public sealed class MiraPluginManager
foreach (var field in type.GetFields().Where(f => f.FieldType.IsAssignableTo(typeof(ConfigEntryBase)) && f.GetCustomAttribute<LocalSettingAttribute>() != null))
{
Error($"{field.Name} is a field, not a property. Use properties for local settings.");
Logger<MiraApiPlugin>.Error($"{field.Name} is a field, not a property. Use properties for local settings.");
}
return true;
}
catch (Exception e)
{
Error($"Failed to register options for {type.Name}: {e.ToString()}");
Logger<MiraApiPlugin>.Error($"Failed to register options for {type.Name}: {e.ToString()}");
}
return false;
@@ -400,7 +400,7 @@ public sealed class MiraPluginManager
if (!type.IsStatic())
{
Error($"Keybinds class {type.Name} must be static.");
Logger<MiraApiPlugin>.Error($"Keybinds class {type.Name} must be static.");
return;
}
@@ -413,7 +413,7 @@ public sealed class MiraPluginManager
if (property.GetValue(null) is not MiraKeybind keybind)
{
Error($"Keybind property {property.Name} in {type.Name} is not a MiraKeybind.");
Logger<MiraApiPlugin>.Error($"Keybind property {property.Name} in {type.Name} is not a MiraKeybind.");
continue;
}
@@ -430,12 +430,12 @@ public sealed class MiraPluginManager
foreach (var field in type.GetFields().Where(f => f.FieldType.IsAssignableTo(typeof(MiraKeybind))))
{
Error($"{field.Name} is a field, not a property. Use properties for keybinds.");
Logger<MiraApiPlugin>.Error($"{field.Name} is a field, not a property. Use properties for keybinds.");
}
}
catch (Exception e)
{
Error($"Failed to register keybind class {type.Name}: {e}");
Logger<MiraApiPlugin>.Error($"Failed to register keybind class {type.Name}: {e}");
}
}
}
+2 -64
View File
@@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.IO;
using System.IO;
using System.Linq;
using BepInEx.Configuration;
using MiraAPI.PluginLoading;
@@ -19,13 +18,6 @@ public static class PresetManager
/// </summary>
public static string PresetDirectory { get; } = Path.GetFullPath("mira_presets", Application.persistentDataPath);
internal static List<OptionPreset> InternalMasterPresets { get; } = [];
/// <summary>
/// Gets a read only dictionary of this plugin's custom presets.
/// </summary>
public static IReadOnlyCollection<OptionPreset> MasterPresets { get; internal set; } = null!;
internal static void CreateDefaultPreset(MiraPluginInfo plugin)
{
var presetPath = Path.Combine(PresetDirectory, plugin.PluginId);
@@ -78,7 +70,7 @@ public static class PresetManager
foreach (var file in Directory.GetFiles(pluginPresetPath, "*.cfg"))
{
var fileName = Path.GetFileName(file);
Info($"Loading preset file {fileName}");
Logger<MiraApiPlugin>.Info($"Loading preset file {fileName}");
var presetName = Path.GetFileNameWithoutExtension(file);
var presetConfig = new ConfigFile(file, false)
{
@@ -101,58 +93,4 @@ public static class PresetManager
}
plugin.Presets = [..plugin.InternalPresets];
}
/// <summary>
/// Loads the master presets by reading the configuration files from the preset directory.
/// </summary>
public static void LoadMasterPreset()
{
/*foreach (var btn in plugin.InternalPresets.Select(x=>x.PresetButton))
{
if (btn != null)
{
Object.DestroyImmediate(btn);
}
}
plugin.InternalPresets.Clear();*/
if (!Directory.Exists(PresetDirectory))
{
Directory.CreateDirectory(PresetDirectory);
}
var pluginPresetPath = Path.Combine(PresetDirectory, "MasterPresets");
if (!Directory.Exists(pluginPresetPath))
{
Directory.CreateDirectory(pluginPresetPath);
}
foreach (var file in Directory.GetFiles(pluginPresetPath, "*.cfg"))
{
var fileName = Path.GetFileName(file);
Info($"Loading preset file {fileName}");
var presetName = Path.GetFileNameWithoutExtension(file);
var presetConfig = new ConfigFile(file, false)
{
SaveOnConfigSet = false,
};
foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins)
{
foreach (var option in plugin.InternalOptions.Where(x=>x.IncludeInPreset))
{
option.Bind(presetConfig);
}
foreach (var role in plugin.InternalRoles.Values.OfType<ICustomRole>().Where(x=>!x.Configuration.HideSettings))
{
role.BindConfig(presetConfig);
}
}
presetConfig.Save();
// InternalMasterPresets.Add(new OptionPreset(presetName, plugin, presetConfig));
}
MasterPresets = [..InternalMasterPresets];
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.
+2 -28
View File
@@ -1,9 +1,7 @@
using AmongUs.GameOptions;
using System;
using Il2CppInterop.Runtime.Attributes;
using MiraAPI.Patches.Freeplay;
using AmongUs.GameOptions;
using MiraAPI.Utilities.Assets;
using UnityEngine;
namespace MiraAPI.Roles;
/// <summary>
@@ -11,16 +9,6 @@ namespace MiraAPI.Roles;
/// </summary>
public record struct CustomRoleConfiguration
{
#pragma warning disable S1133
[Obsolete("Default constructor is not supported. Please use the constructor that takes an ICustomRole parameter.")]
#pragma warning restore S1133
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public CustomRoleConfiguration()
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
{
throw new NotImplementedException("Default constructor is not supported. Please use the constructor that takes an ICustomRole parameter.");
}
/// <summary>
/// Initializes a new instance of the <see cref="CustomRoleConfiguration"/> struct.
/// </summary>
@@ -44,12 +32,6 @@ public record struct CustomRoleConfiguration
TasksCountForProgress = role.Team is ModdedRoleTeams.Crewmate;
HideSettings = roleBehaviour?.IsDead == true;
ShowInFreeplay = roleBehaviour?.IsDead == false;
FreeplayFolder = role.Team switch
{
ModdedRoleTeams.Crewmate => TranslationController.Instance.GetString(StringNames.Crewmate),
ModdedRoleTeams.Impostor => TranslationController.Instance.GetString(StringNames.Impostor),
_ => TaskAdderPatches.NeutralName,
};
IntroSound = role.Team is ModdedRoleTeams.Crewmate
? CustomRoleManager.CrewmateIntroSound
: CustomRoleManager.ImpostorIntroSound;
@@ -78,19 +60,16 @@ public record struct CustomRoleConfiguration
/// <summary>
/// Gets or sets the Sprite used for the Role Options menu screenshot.
/// </summary>
[HideFromIl2Cpp]
public LoadableAsset<Sprite>? OptionsScreenshot { get; set; } = null;
/// <summary>
/// Gets or sets the Sprite used for the Role Icon.
/// </summary>
[HideFromIl2Cpp]
public LoadableAsset<Sprite>? Icon { get; set; } = null;
/// <summary>
/// Gets or sets the Intro sound for the Role.
/// </summary>
[HideFromIl2Cpp]
public LoadableAsset<AudioClip>? IntroSound { get; set; } = null;
/// <summary>
@@ -133,11 +112,6 @@ 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>
+10 -35
View File
@@ -4,7 +4,6 @@ using System.Linq;
using AmongUs.GameOptions;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using MiraAPI.Networking;
using MiraAPI.PluginLoading;
using MiraAPI.Utilities;
@@ -36,11 +35,6 @@ 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>
@@ -54,9 +48,6 @@ public static class CustomRoleManager
internal static readonly Dictionary<ushort, RoleBehaviour> CustomRoles = [];
internal static readonly Dictionary<Type, ushort> RoleIds = [];
private static Il2CppSystem.Collections.Generic.List<BaseGameSetting>? _emptySettings;
private static Il2CppReferenceArray<OverlayKillAnimation>? _emptyKillAnimations;
private static ushort _roleId = 100;
private static ushort GetNextRoleId()
@@ -66,10 +57,7 @@ public static class CustomRoleManager
internal static void RegisterInRoleManager()
{
foreach (var role in CustomRoles.Values)
{
RoleManager.Instance.AllRoles.Add(role);
}
RoleManager.Instance.AllRoles = RoleManager.Instance.AllRoles.Concat(CustomRoles.Values).ToArray();
foreach (var role in CustomRoles.Values.Where(x => x.IsDead))
{
@@ -89,22 +77,15 @@ public static class CustomRoleManager
foreach (var roleType in roles)
{
try
{
ClassInjector.RegisterTypeInIl2Cpp(roleType);
ClassInjector.RegisterTypeInIl2Cpp(roleType);
var role = RegisterRole(roleType, pluginInfo);
if (role is null)
{
continue;
}
pluginInfo.InternalRoles.Add((ushort)role.Role, role);
}
catch (Exception ex)
var role = RegisterRole(roleType, pluginInfo);
if (role is null)
{
Error($"Failed to register role type {roleType.Name}: {ex}");
continue;
}
pluginInfo.InternalRoles.Add((ushort)role.Role, role);
}
pluginInfo.PluginConfig.Save();
@@ -115,7 +96,7 @@ public static class CustomRoleManager
{
if (!(typeof(RoleBehaviour).IsAssignableFrom(roleType) && typeof(ICustomRole).IsAssignableFrom(roleType)))
{
Error($"{roleType.Name} does not inherit from RoleBehaviour or ICustomRole.");
Logger<MiraApiPlugin>.Error($"{roleType.Name} does not inherit from RoleBehaviour or ICustomRole.");
return null;
}
@@ -144,12 +125,6 @@ public static class CustomRoleManager
roleBehaviour.MaxCount = customRole.Configuration.MaxRoleCount;
roleBehaviour.RoleScreenshot = customRole.Configuration.OptionsScreenshot?.LoadAsset();
_emptySettings ??= new(0);
_emptyKillAnimations ??= new(0);
roleBehaviour.AllGameSettings = _emptySettings;
roleBehaviour.CustomKillAnimations = _emptyKillAnimations;
if (customRole.Configuration.Icon != null)
{
var asset = customRole.Configuration.Icon.LoadAsset();
@@ -175,7 +150,7 @@ public static class CustomRoleManager
if (useTaskHint && !overridesTaskText)
{
Error($"Role {customRole.RoleName} is using RoleHintType.TaskHint but does not override SpawnTaskHeader!");
Logger<MiraApiPlugin>.Error($"Role {customRole.RoleName} is using RoleHintType.TaskHint but does not override SpawnTaskHeader!");
}
CustomRoles.Add(roleId, roleBehaviour);
@@ -305,7 +280,7 @@ public static class CustomRoleManager
}
catch (Exception e)
{
Error(e);
Logger<MiraApiPlugin>.Error(e);
}
}
+3 -59
View File
@@ -1,10 +1,10 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using AmongUs.GameOptions;
using MiraAPI.Utilities.Assets;
using Reactor.Utilities;
using UnityEngine;
namespace MiraAPI.Roles;
@@ -14,62 +14,6 @@ namespace MiraAPI.Roles;
/// </summary>
public static class CustomRoleUtils
{
/// <summary>
/// Determines whether the specified role can spawn in general, accounting for gamemodes and everything else.
/// </summary>
/// <param name="role">The role you would like to check for.</param>
/// <returns>True if the role is able to spawn, otherwise false.</returns>
public static bool CanSpawnOnCurrentMode(RoleBehaviour role)
{
if (role is ICustomRole custom)
{
return custom.CanSpawnOnCurrentMode();
}
if (GameManager.Instance.IsHideAndSeek())
{
return role.Role is RoleTypes.Engineer || role.Role is RoleTypes.Impostor;
}
return true;
}
public static List<(ushort RoleType, int Chance)> GetPossibleRoles(
List<RoleManager.RoleAssignmentData> assignmentData,
Func<RoleManager.RoleAssignmentData, bool>? predicate = null)
{
var roles = new List<(ushort, int)>();
assignmentData.Where(x => predicate == null || predicate(x)).ToList().ForEach(x =>
{
for (var i = 0; i < x.Count; i++)
{
roles.Add(((ushort)x.Role.Role, x.Chance));
}
});
return roles;
}
public static RoleManager.RoleAssignmentData GetAssignData(RoleTypes roleType)
{
var currentGameOptions = GameOptionsManager.Instance.CurrentGameOptions;
var roleOptions = currentGameOptions.RoleOptions;
var role = GetRegisteredRole(roleType);
var assignmentData = new RoleManager.RoleAssignmentData(role, roleOptions.GetNumPerGame(role!.Role),
roleOptions.GetChancePerGame(role.Role));
return assignmentData;
}
public static RoleBehaviour? GetRegisteredRole(RoleTypes roleType)
{
// we want to prioritize the custom roles because the role has the right RoleColour/TeamColor
var role = CustomRoleManager.AllRoles.FirstOrDefault(x => x.Role == roleType);
return role;
}
/// <summary>
/// Gets all active in-game roles.
/// </summary>
@@ -112,7 +56,7 @@ public static class CustomRoleUtils
/// <returns>The intro sound.</returns>
public static LoadableAsset<AudioClip>? GetIntroSound(RoleTypes roleType)
{
var role = CustomRoleManager.AllRoles.FirstOrDefault(role => role.Role == roleType);
var role = RoleManager.Instance.AllRoles.FirstOrDefault(role => role.Role == roleType);
if (role is ICustomRole customRole)
{
return customRole.Configuration.IntroSound;
Loaded 100 of 113 files, more files were not shown because too many files have changed in this diff. Show more