mirror of
https://github.com/All-Of-Us-Mods/MiraAPI.git
synced 2026-10-20 01:46:15 +00:00
Compare commits
60
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83be29d6a2 | ||
|
|
e3b0eb68c0 | ||
|
|
90921ff786 | ||
|
|
c1d0bb5d26 | ||
|
|
c59ed8eaa6 | ||
|
|
d2fafc3650 | ||
|
|
0c0157fed2 | ||
|
|
90bf402cbc | ||
|
|
fed3abafb2 | ||
|
|
925d47a275 | ||
|
|
45a3597ab2 | ||
|
|
faeb39d3de | ||
|
|
6b46cffe5e | ||
|
|
00a60bb7ad | ||
|
|
d89bce6956 | ||
|
|
7f92e94293 | ||
|
|
6199e55110 | ||
|
|
eac442443b | ||
|
|
6321753ec0 | ||
|
|
e66d45bd21 | ||
|
|
1e00181220 | ||
|
|
fcd7856571 | ||
|
|
5680d27db1 | ||
|
|
49d77fcf40 | ||
|
|
dcb4a3876f | ||
|
|
9270de408d | ||
|
|
8940de76bb | ||
|
|
1813fe5a84 | ||
|
|
272b14f0e0 | ||
|
|
6cc1d4b762 | ||
|
|
9dfad78df6 | ||
|
|
7f506000a4 | ||
|
|
795b6a81d7 | ||
|
|
bd8d7a3208 | ||
|
|
e8536e5d4b | ||
|
|
090db766b2 | ||
|
|
1f0027108e | ||
|
|
665724010d | ||
|
|
2d58c91289 | ||
|
|
b3ed6d67f9 | ||
|
|
64ef78e3a2 | ||
|
|
5e9e4012ef | ||
|
|
fe04a7033a | ||
|
|
ba0ee49255 | ||
|
|
97528b4380 | ||
|
|
50030c113b | ||
|
|
20e51cc993 | ||
|
|
64b5e01ba1 | ||
|
|
d4469153ab | ||
|
|
ab67c233d8 | ||
|
|
c72eacc7a2 | ||
|
|
13072d6834 | ||
|
|
c6342750ef | ||
|
|
5c2e95376d | ||
|
|
067859b9fd | ||
|
|
fe3bfcdbc4 | ||
|
|
5d01462a32 | ||
|
|
a9b6f40ed0 | ||
|
|
eae0024ec3 | ||
|
|
2907564d6a |
No files matched your search
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Reactor" Version="2.3.1" />
|
<PackageReference Include="Reactor" Version="2.5.0-ci.371" />
|
||||||
<PackageReference Include="BepInEx.Unity.IL2CPP" Version="6.0.0-be.735" Private="false" ExcludeAssets="runtime;native" />
|
<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="2026.3.31" PrivateAssets="all" />
|
||||||
|
|
||||||
<PackageReference Include="BepInEx.AutoPlugin" Version="1.1.0" 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" />
|
<PackageReference Include="BepInEx.IL2CPP.MSBuild" Version="2.1.0-rc.1" PrivateAssets="all" ExcludeAssets="runtime" />
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||||
|
|
||||||
<VersionPrefix>0.3.4</VersionPrefix>
|
<VersionPrefix>0.4.0</VersionPrefix>
|
||||||
<VersionSuffix>dev</VersionSuffix>
|
<VersionSuffix>dev</VersionSuffix>
|
||||||
|
|
||||||
<Authors>All Of Us, XtraCube, Angxl</Authors>
|
<Authors>All Of Us, XtraCube, Angxl</Authors>
|
||||||
@@ -42,4 +42,4 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)\stylecop.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)\stylecop.ruleset</CodeAnalysisRuleSet>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Linq;
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
using MiraAPI.Events;
|
using MiraAPI.Events;
|
||||||
using MiraAPI.Events.Mira;
|
using MiraAPI.Events.Mira;
|
||||||
using MiraAPI.Events.Vanilla.Gameplay;
|
using MiraAPI.Events.Vanilla.Gameplay;
|
||||||
@@ -17,7 +18,7 @@ public static class ExampleEventHandlers
|
|||||||
{
|
{
|
||||||
public static void Initialize()
|
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 =>
|
var handle = MiraEventManager.RegisterEventHandler<BeforeMurderEvent>(@event =>
|
||||||
{
|
{
|
||||||
Logger<ExamplePlugin>.Info($"{@event.Source.Data.PlayerName} is about to kill {@event.Target.Data.PlayerName}");
|
Logger<ExamplePlugin>.Info($"{@event.Source.Data.PlayerName} is about to kill {@event.Target.Data.PlayerName}");
|
||||||
@@ -37,6 +38,21 @@ public static class ExampleEventHandlers
|
|||||||
MiraEventManager.UnregisterEventHandler(handle);
|
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.
|
// If you want to add extra votes to a player, do something like this.
|
||||||
[RegisterEvent]
|
[RegisterEvent]
|
||||||
public static void StartMeetingEvent(StartMeetingEvent _)
|
public static void StartMeetingEvent(StartMeetingEvent _)
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace MiraAPI.Events.Vanilla.Gameplay;
|
using MiraAPI.Networking;
|
||||||
|
|
||||||
|
namespace MiraAPI.Events.Vanilla.Gameplay;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event that is invoked before a player is murdered. This event is cancelable.
|
/// Event that is invoked before a player is murdered. This event is cancelable.
|
||||||
@@ -15,6 +17,24 @@ public sealed class BeforeMurderEvent : MiraCancelableEvent
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public PlayerControl Target { get; }
|
public PlayerControl Target { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets whether the murder was meant to be done in a meeting via an enum.
|
||||||
|
/// </summary>
|
||||||
|
public MeetingCheck InMeeting { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="BeforeMurderEvent"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source">The player that is killing the target.</param>
|
||||||
|
/// <param name="target">The player that is being killed.</param>
|
||||||
|
/// <param name="inMeeting">Whether the murder is intended to be triggered in a meeting.</param>
|
||||||
|
public BeforeMurderEvent(PlayerControl source, PlayerControl target, MeetingCheck inMeeting)
|
||||||
|
{
|
||||||
|
Source = source;
|
||||||
|
Target = target;
|
||||||
|
InMeeting = inMeeting;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="BeforeMurderEvent"/> class.
|
/// Initializes a new instance of the <see cref="BeforeMurderEvent"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -24,5 +44,6 @@ public sealed class BeforeMurderEvent : MiraCancelableEvent
|
|||||||
{
|
{
|
||||||
Source = source;
|
Source = source;
|
||||||
Target = target;
|
Target = target;
|
||||||
|
InMeeting = MeetingCheck.Ignore;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -69,4 +69,4 @@ public class BaseKeybind
|
|||||||
{
|
{
|
||||||
Handler -= action;
|
Handler -= action;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ namespace MiraAPI.Keybinds;
|
|||||||
public class MiraKeybind : BaseKeybind
|
public class MiraKeybind : BaseKeybind
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets name of the keybind
|
/// Gets name of the keybind.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
global using static Reactor.Utilities.Logger<MiraAPI.MiraApiPlugin>;
|
global using static Reactor.Utilities.Logger<MiraAPI.MiraApiPlugin>;
|
||||||
|
using System;
|
||||||
using BepInEx;
|
using BepInEx;
|
||||||
using BepInEx.Unity.IL2CPP;
|
using BepInEx.Unity.IL2CPP;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
@@ -21,6 +22,11 @@ namespace MiraAPI;
|
|||||||
[ReactorModFlags(ModFlags.RequireOnAllClients)]
|
[ReactorModFlags(ModFlags.RequireOnAllClients)]
|
||||||
public partial class MiraApiPlugin : BasePlugin
|
public partial class MiraApiPlugin : BasePlugin
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether the current device is running Starlight (on mobile).
|
||||||
|
/// </summary>
|
||||||
|
public static bool IsMobile => Constants.GetPlatformType() is Platforms.Android or Platforms.IPhone;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the branding Mira API color.
|
/// Gets the branding Mira API color.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -31,6 +37,11 @@ public partial class MiraApiPlugin : BasePlugin
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static Color DefaultHeaderColor { get; } = new Color32(77, 77, 77, 255);
|
public static Color DefaultHeaderColor { get; } = new Color32(77, 77, 77, 255);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether the current build is a development build or not. This mostly avoids confusion for users asking why the mod appears red.
|
||||||
|
/// </summary>
|
||||||
|
public static bool IsDevBuild => Version.Contains("ci", StringComparison.OrdinalIgnoreCase) || Version.Contains("dev", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
private static MiraPluginManager? PluginManager { get; set; }
|
private static MiraPluginManager? PluginManager { get; set; }
|
||||||
internal Harmony Harmony { get; } = new(Id);
|
internal Harmony Harmony { get; } = new(Id);
|
||||||
|
|
||||||
@@ -39,7 +50,7 @@ public partial class MiraApiPlugin : BasePlugin
|
|||||||
{
|
{
|
||||||
Harmony.PatchAll();
|
Harmony.PatchAll();
|
||||||
|
|
||||||
ReactorCredits.Register("Mira API", Version, true, ReactorCredits.AlwaysShow);
|
ReactorCredits.Register("Mira API", Version, IsDevBuild, ReactorCredits.AlwaysShow);
|
||||||
|
|
||||||
PluginManager = new MiraPluginManager();
|
PluginManager = new MiraPluginManager();
|
||||||
PluginManager.Initialize();
|
PluginManager.Initialize();
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
using BepInEx.Configuration;
|
using BepInEx.Configuration;
|
||||||
using MiraAPI.LocalSettings;
|
using MiraAPI.LocalSettings;
|
||||||
using MiraAPI.LocalSettings.Attributes;
|
using MiraAPI.LocalSettings.Attributes;
|
||||||
|
using MiraAPI.Patches;
|
||||||
|
using MiraAPI.Utilities;
|
||||||
using MiraAPI.Utilities.Assets;
|
using MiraAPI.Utilities.Assets;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
namespace MiraAPI;
|
namespace MiraAPI;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Mira API Config File Handler
|
/// Mira API Config File Handler.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class MiraApiSettings(ConfigFile config) : LocalSettingsTab(config)
|
public class MiraApiSettings(ConfigFile config) : LocalSettingsTab(config)
|
||||||
{
|
{
|
||||||
@@ -21,22 +24,78 @@ public class MiraApiSettings(ConfigFile config) : LocalSettingsTab(config)
|
|||||||
HideIconOnHover = false,
|
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>
|
/// <summary>
|
||||||
/// Gets whether the modifiers hud should be on the left side of the screen (under roles/task tab). Recommended for streamers.
|
/// Gets whether the modifiers hud should be on the left side of the screen (under roles/task tab). Recommended for streamers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[LocalToggleSetting]
|
[LocalToggleSetting]
|
||||||
public ConfigEntry<bool> ModifiersHudLeftSide { get; private set; } = config.Bind("Displays", "Show Modifiers HUD on Left Side", false);
|
public ConfigEntry<bool> ModifiersHudLeftSide { get; private set; } = config.Bind("Visuals/UI", "Show Modifiers HUD on Left Side", false);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets whether to show keybinds on buttons.
|
/// Gets whether to show keybinds on buttons.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[LocalToggleSetting]
|
[LocalToggleSetting]
|
||||||
public ConfigEntry<bool> ShowKeybinds { get; private set; } = config.Bind("Keybinds", "Show Keybinds on Buttons", true);
|
public ConfigEntry<bool> ShowKeybinds { get; private set; } = config.Bind("Visuals/UI", "Show Keybinds on Buttons", true);
|
||||||
// This would be placed in the keybinds menu, but it crashes for Epic Games users. - Atony
|
|
||||||
|
/// <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", "Max FPS", 120f);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets whether to apply cosmetic changes to the TaskAdder.
|
/// Gets whether to apply cosmetic changes to the TaskAdder.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[LocalToggleSetting]
|
[LocalToggleSetting]
|
||||||
public ConfigEntry<bool> PrettyTaskAdder { get; private set; } = config.Bind("Freeplay", "Pretty Task Laptop", true);
|
public ConfigEntry<bool> PrettyTaskAdder { get; private set; } = config.Bind("Freeplay", "Pretty Task Laptop", true);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets whether to show the red flash from sabotages.
|
||||||
|
/// </summary>
|
||||||
|
[LocalToggleSetting]
|
||||||
|
public ConfigEntry<bool> EnableSabotageFlashes { get; private set; } = config.Bind("Accessibility", "Enable Sabotage Flashes", true);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets whether to enable the sabotage sound effects or not.
|
||||||
|
/// </summary>
|
||||||
|
[LocalToggleSetting]
|
||||||
|
public ConfigEntry<bool> EnableSabotageBlares { get; private set; } = config.Bind("Accessibility", "Enable Sabotage Blare", true);
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ using System.Linq;
|
|||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using Il2CppInterop.Runtime.Attributes;
|
using Il2CppInterop.Runtime.Attributes;
|
||||||
using MiraAPI.LocalSettings;
|
using MiraAPI.LocalSettings;
|
||||||
|
using MiraAPI.Utilities;
|
||||||
using MiraAPI.Utilities.Assets;
|
using MiraAPI.Utilities.Assets;
|
||||||
using Reactor.Utilities.Attributes;
|
using Reactor.Utilities.Attributes;
|
||||||
using Reactor.Utilities.Extensions;
|
using Reactor.Utilities.Extensions;
|
||||||
@@ -137,7 +138,7 @@ public class ModifierDisplayComponent(nint cppPtr) : MonoBehaviour(cppPtr)
|
|||||||
internal void DestroyComponent(ModifierUiComponent component)
|
internal void DestroyComponent(ModifierUiComponent component)
|
||||||
{
|
{
|
||||||
_modifiers.Remove(component.Modifier!);
|
_modifiers.Remove(component.Modifier!);
|
||||||
component.gameObject.DestroyImmediate();
|
component.gameObject.DeepDestroy();
|
||||||
RefreshModifiers();
|
RefreshModifiers();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +208,7 @@ public class ModifierDisplayComponent(nint cppPtr) : MonoBehaviour(cppPtr)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
mod.Value.gameObject.Destroy();
|
mod.Value.gameObject.DeepDestroy();
|
||||||
_modifiers.Remove(mod.Key);
|
_modifiers.Remove(mod.Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,10 +7,12 @@ using Assets.CoreScripts;
|
|||||||
using BepInEx.Unity.IL2CPP.Utils;
|
using BepInEx.Unity.IL2CPP.Utils;
|
||||||
using MiraAPI.Events;
|
using MiraAPI.Events;
|
||||||
using MiraAPI.Events.Vanilla.Gameplay;
|
using MiraAPI.Events.Vanilla.Gameplay;
|
||||||
|
using MiraAPI.Utilities;
|
||||||
using Reactor.Networking.Attributes;
|
using Reactor.Networking.Attributes;
|
||||||
using Reactor.Networking.Rpc;
|
using Reactor.Networking.Rpc;
|
||||||
using Reactor.Utilities;
|
using Reactor.Utilities;
|
||||||
using Reactor.Utilities.Extensions;
|
using Reactor.Utilities.Extensions;
|
||||||
|
using Rewired;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using Object = UnityEngine.Object;
|
using Object = UnityEngine.Object;
|
||||||
|
|
||||||
@@ -22,7 +24,7 @@ namespace MiraAPI.Networking;
|
|||||||
public static class CustomMurderRpc
|
public static class CustomMurderRpc
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Networked Custom Murder method.
|
/// Networked Custom Murder method, ran by clients for the host to confirm kills.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="source">The killer.</param>
|
/// <param name="source">The killer.</param>
|
||||||
/// <param name="target">The player to murder.</param>
|
/// <param name="target">The player to murder.</param>
|
||||||
@@ -32,7 +34,6 @@ public static class CustomMurderRpc
|
|||||||
/// <param name="teleportMurderer">Should the killer be snapped to the dead player.</param>
|
/// <param name="teleportMurderer">Should the killer be snapped to the dead player.</param>
|
||||||
/// <param name="showKillAnim">Should the kill animation be shown.</param>
|
/// <param name="showKillAnim">Should the kill animation be shown.</param>
|
||||||
/// <param name="playKillSound">Should the kill sound be played.</param>
|
/// <param name="playKillSound">Should the kill sound be played.</param>
|
||||||
[MethodRpc((uint)MiraRpc.CustomMurder, LocalHandling = RpcLocalHandling.Before)]
|
|
||||||
public static void RpcCustomMurder(
|
public static void RpcCustomMurder(
|
||||||
this PlayerControl source,
|
this PlayerControl source,
|
||||||
PlayerControl target,
|
PlayerControl target,
|
||||||
@@ -43,16 +44,117 @@ public static class CustomMurderRpc
|
|||||||
bool showKillAnim = true,
|
bool showKillAnim = true,
|
||||||
bool playKillSound = true)
|
bool playKillSound = true)
|
||||||
{
|
{
|
||||||
|
source.RpcCustomMurder(
|
||||||
|
target,
|
||||||
|
MeetingCheck.Ignore,
|
||||||
|
didSucceed,
|
||||||
|
resetKillTimer,
|
||||||
|
createDeadBody,
|
||||||
|
teleportMurderer,
|
||||||
|
showKillAnim,
|
||||||
|
playKillSound);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Networked Custom Murder method, which checks for meetings as well, ran by clients for the host to confirm kills.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source">The killer.</param>
|
||||||
|
/// <param name="target">The player to murder.</param>
|
||||||
|
/// <param name="inMeeting">Whether the murder is intended to be triggered in a meeting.</param>
|
||||||
|
/// <param name="didSucceed">Whether the murder was successful or not.</param>
|
||||||
|
/// <param name="resetKillTimer">Should the kill timer be reset.</param>
|
||||||
|
/// <param name="createDeadBody">Should a dead body be created.</param>
|
||||||
|
/// <param name="teleportMurderer">Should the killer be snapped to the dead player.</param>
|
||||||
|
/// <param name="showKillAnim">Should the kill animation be shown.</param>
|
||||||
|
/// <param name="playKillSound">Should the kill sound be played.</param>
|
||||||
|
[MethodRpc((uint)MiraRpc.CustomMurder, LocalHandling = RpcLocalHandling.Before)]
|
||||||
|
public static void RpcCustomMurder(
|
||||||
|
this PlayerControl source,
|
||||||
|
PlayerControl target,
|
||||||
|
MeetingCheck inMeeting,
|
||||||
|
bool didSucceed = true,
|
||||||
|
bool resetKillTimer = true,
|
||||||
|
bool createDeadBody = true,
|
||||||
|
bool teleportMurderer = true,
|
||||||
|
bool showKillAnim = true,
|
||||||
|
bool playKillSound = true)
|
||||||
|
{
|
||||||
|
if (LobbyBehaviour.Instance)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
var murderResultFlags = didSucceed ? MurderResultFlags.Succeeded : MurderResultFlags.FailedError;
|
var murderResultFlags = didSucceed ? MurderResultFlags.Succeeded : MurderResultFlags.FailedError;
|
||||||
|
|
||||||
var beforeMurderEvent = new BeforeMurderEvent(source, target);
|
var beforeMurderEvent = new BeforeMurderEvent(source, target, inMeeting);
|
||||||
MiraEventManager.InvokeEvent(beforeMurderEvent);
|
MiraEventManager.InvokeEvent(beforeMurderEvent);
|
||||||
|
var isMeetingActive = MeetingHud.Instance != null || ExileController.Instance != null;
|
||||||
|
if ((inMeeting is MeetingCheck.ForMeeting && !isMeetingActive) || (inMeeting is MeetingCheck.OutsideMeeting && isMeetingActive))
|
||||||
|
{
|
||||||
|
beforeMurderEvent.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
if (beforeMurderEvent.IsCancelled)
|
if (target.ProtectedByGa())
|
||||||
|
{
|
||||||
|
beforeMurderEvent.Cancel();
|
||||||
|
murderResultFlags = MurderResultFlags.FailedProtected;
|
||||||
|
}
|
||||||
|
else if (beforeMurderEvent.IsCancelled)
|
||||||
{
|
{
|
||||||
murderResultFlags = MurderResultFlags.FailedError;
|
murderResultFlags = MurderResultFlags.FailedError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (beforeMurderEvent.IsCancelled && source.AmOwner)
|
||||||
|
{
|
||||||
|
source.isKilling = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PlayerControl.LocalPlayer.IsHost())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RpcConfirmCustomMurder(
|
||||||
|
PlayerControl.LocalPlayer,
|
||||||
|
source,
|
||||||
|
target,
|
||||||
|
murderResultFlags,
|
||||||
|
resetKillTimer,
|
||||||
|
createDeadBody,
|
||||||
|
teleportMurderer,
|
||||||
|
showKillAnim,
|
||||||
|
playKillSound);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Networked Custom Murder method, which checks for meetings as well, ran by clients for the host to confirm kills.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="host">The game host.</param>
|
||||||
|
/// <param name="source">The killer.</param>
|
||||||
|
/// <param name="target">The player to murder.</param>
|
||||||
|
/// <param name="murderResultFlags">End result for the murder.</param>
|
||||||
|
/// <param name="resetKillTimer">Should the kill timer be reset.</param>
|
||||||
|
/// <param name="createDeadBody">Should a dead body be created.</param>
|
||||||
|
/// <param name="teleportMurderer">Should the killer be snapped to the dead player.</param>
|
||||||
|
/// <param name="showKillAnim">Should the kill animation be shown.</param>
|
||||||
|
/// <param name="playKillSound">Should the kill sound be played.</param>
|
||||||
|
[MethodRpc((uint)MiraRpc.ConfirmCustomMurder, LocalHandling = RpcLocalHandling.After)]
|
||||||
|
public static void RpcConfirmCustomMurder(
|
||||||
|
this PlayerControl host,
|
||||||
|
PlayerControl source,
|
||||||
|
PlayerControl target,
|
||||||
|
MurderResultFlags murderResultFlags,
|
||||||
|
bool resetKillTimer = true,
|
||||||
|
bool createDeadBody = true,
|
||||||
|
bool teleportMurderer = true,
|
||||||
|
bool showKillAnim = true,
|
||||||
|
bool playKillSound = true)
|
||||||
|
{
|
||||||
|
if (LobbyBehaviour.Instance || !host.IsHost() || target.Data.IsDead || target.Data.Disconnected)
|
||||||
|
{
|
||||||
|
source.isKilling = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var murderResultFlags2 = MurderResultFlags.DecisionByHost | murderResultFlags;
|
var murderResultFlags2 = MurderResultFlags.DecisionByHost | murderResultFlags;
|
||||||
|
|
||||||
source.CustomMurder(
|
source.CustomMurder(
|
||||||
@@ -87,11 +189,10 @@ public static class CustomMurderRpc
|
|||||||
bool playKillSound = true)
|
bool playKillSound = true)
|
||||||
{
|
{
|
||||||
source.isKilling = false;
|
source.isKilling = false;
|
||||||
if (resultFlags.HasFlag(MurderResultFlags.FailedError))
|
if (resultFlags.HasFlag(MurderResultFlags.FailedError) || LobbyBehaviour.Instance)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var data = target.Data;
|
|
||||||
|
|
||||||
if (resultFlags.HasFlag(MurderResultFlags.FailedProtected) ||
|
if (resultFlags.HasFlag(MurderResultFlags.FailedProtected) ||
|
||||||
(resultFlags.HasFlag(MurderResultFlags.DecisionByHost) && target.protectedByGuardianId > -1))
|
(resultFlags.HasFlag(MurderResultFlags.DecisionByHost) && target.protectedByGuardianId > -1))
|
||||||
@@ -174,7 +275,7 @@ public static class CustomMurderRpc
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
HudManager.Instance.KillOverlay.ShowKillAnimation(source.Data, data);
|
HudManager.Instance.KillOverlay.ShowKillAnimation(source.Data, target.Data);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -192,7 +293,7 @@ public static class CustomMurderRpc
|
|||||||
source.CurrentOutfitType == PlayerOutfitType.Shapeshifted,
|
source.CurrentOutfitType == PlayerOutfitType.Shapeshifted,
|
||||||
source.shapeshiftTargetPlayerId,
|
source.shapeshiftTargetPlayerId,
|
||||||
target.PlayerId);
|
target.PlayerId);
|
||||||
source.MyPhysics.StartCoroutine(source.KillAnimations.Random()?.CoPerformCustomKill(source, target, createDeadBody, teleportMurderer));
|
source.MyPhysics.StartCoroutine(source.KillAnimations.RandomSnapshot().CoPerformCustomKill(source, target, createDeadBody, teleportMurderer));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -211,6 +312,10 @@ public static class CustomMurderRpc
|
|||||||
bool createDeadBody = true,
|
bool createDeadBody = true,
|
||||||
bool teleportMurderer = true)
|
bool teleportMurderer = true)
|
||||||
{
|
{
|
||||||
|
if (LobbyBehaviour.Instance)
|
||||||
|
{
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
var cam = Camera.main?.GetComponent<FollowerCamera>();
|
var cam = Camera.main?.GetComponent<FollowerCamera>();
|
||||||
var isParticipant = source.AmOwner || target.AmOwner;
|
var isParticipant = source.AmOwner || target.AmOwner;
|
||||||
var sourcePhys = source.MyPhysics;
|
var sourcePhys = source.MyPhysics;
|
||||||
@@ -298,3 +403,24 @@ public static class CustomMurderRpc
|
|||||||
source.isKilling = false;
|
source.isKilling = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks for custom murders.
|
||||||
|
/// </summary>
|
||||||
|
public enum MeetingCheck
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Checks for meetings.
|
||||||
|
/// </summary>
|
||||||
|
ForMeeting,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks for a meeting to not be active.
|
||||||
|
/// </summary>
|
||||||
|
OutsideMeeting,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Doesn't check for a meeting at all.
|
||||||
|
/// </summary>
|
||||||
|
Ignore,
|
||||||
|
}
|
||||||
@@ -35,6 +35,11 @@ public enum MiraRpc : uint
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
CustomMurder,
|
CustomMurder,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Custom Murder RPC Confirmation.
|
||||||
|
/// </summary>
|
||||||
|
ConfirmCustomMurder,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Custom Game Over RPC.
|
/// Custom Game Over RPC.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -276,8 +276,9 @@ public static class TaskAdderPatches
|
|||||||
__instance.transform.FindChild("TitleText_TMP")?.gameObject.DestroyImmediate();
|
__instance.transform.FindChild("TitleText_TMP")?.gameObject.DestroyImmediate();
|
||||||
}
|
}
|
||||||
|
|
||||||
__instance.ActiveItems.ToArray().Do(x => x.gameObject.Destroy());
|
__instance.ActiveItems.ToArray().Do(x => x.gameObject.DeepDestroy(false));
|
||||||
__instance.ActiveItems.Clear();
|
__instance.ActiveItems.Clear();
|
||||||
|
Utilities.Extensions.ClearGarbageCollector();
|
||||||
|
|
||||||
float num = 0f;
|
float num = 0f;
|
||||||
float num2 = 0f;
|
float num2 = 0f;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections;
|
||||||
using System.Linq;
|
using System.Collections.Generic;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using MiraAPI.Hud;
|
using MiraAPI.Hud;
|
||||||
using MiraAPI.Keybinds;
|
using MiraAPI.Keybinds;
|
||||||
@@ -25,9 +25,87 @@ public static class HudManagerPatches
|
|||||||
public static Transform? BottomRight { get; private set; }
|
public static Transform? BottomRight { get; private set; }
|
||||||
public static Transform? Buttons { get; private set; }
|
public static Transform? Buttons { get; private set; }
|
||||||
|
|
||||||
private static Dictionary<TextMeshPro, int> vanillaKeybindIcons = new();
|
private static Dictionary<TextMeshPro, int> vanillaKeybindIcons = [];
|
||||||
|
|
||||||
internal static List<TextMeshPro> ModdedKeybindIcons = new();
|
internal static List<TextMeshPro> ModdedKeybindIcons = [];
|
||||||
|
|
||||||
|
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.name.Contains("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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arrange.gameObject.name.Contains("TopRight"))
|
||||||
|
{
|
||||||
|
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>
|
/// <summary>
|
||||||
@@ -65,8 +143,9 @@ public static class HudManagerPatches
|
|||||||
|
|
||||||
foreach (var t in BottomLeft.GetComponentsInChildren<ActionButton>(true))
|
foreach (var t in BottomLeft.GetComponentsInChildren<ActionButton>(true))
|
||||||
{
|
{
|
||||||
t.gameObject.Destroy();
|
t.gameObject.DeepDestroy(false);
|
||||||
}
|
}
|
||||||
|
Utilities.Extensions.ClearGarbageCollector();
|
||||||
|
|
||||||
var gridArrange = BottomLeft.GetComponent<GridArrange>();
|
var gridArrange = BottomLeft.GetComponent<GridArrange>();
|
||||||
var aspectPosition = BottomLeft.GetComponent<AspectPosition>();
|
var aspectPosition = BottomLeft.GetComponent<AspectPosition>();
|
||||||
@@ -146,9 +225,15 @@ public static class HudManagerPatches
|
|||||||
}
|
}
|
||||||
var icon = Helpers.CreateKeybindIcon(buttonObj, key, keybindIconPos);
|
var icon = Helpers.CreateKeybindIcon(buttonObj, key, keybindIconPos);
|
||||||
vanillaKeybindIcons.Add(icon.transform.GetChild(0).GetComponent<TextMeshPro>(), actionId);
|
vanillaKeybindIcons.Add(icon.transform.GetChild(0).GetComponent<TextMeshPro>(), actionId);
|
||||||
var comp = buttonObj.GetComponent<ActionButton>();
|
if (!MiraApiPlugin.IsMobile)
|
||||||
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>
|
/// <summary>
|
||||||
@@ -187,31 +272,54 @@ public static class HudManagerPatches
|
|||||||
{
|
{
|
||||||
var canSeeBinds = ActiveInputManager.currentControlType == ActiveInputManager.InputType.Keyboard &&
|
var canSeeBinds = ActiveInputManager.currentControlType == ActiveInputManager.InputType.Keyboard &&
|
||||||
LocalSettingsTabSingleton<MiraApiSettings>.Instance.ShowKeybinds.Value;
|
LocalSettingsTabSingleton<MiraApiSettings>.Instance.ShowKeybinds.Value;
|
||||||
|
|
||||||
foreach (var btnIcon in vanillaKeybindIcons)
|
foreach (var btnIcon in vanillaKeybindIcons)
|
||||||
{
|
{
|
||||||
btnIcon.Key.text = KeybindUtils.GetKeycodeByActionId(btnIcon.Value).ToString();
|
btnIcon.Key.text = KeybindUtils.GetKeycodeByActionId(btnIcon.Value).ToString();
|
||||||
btnIcon.Key.transform.parent.gameObject.SetActive(canSeeBinds);
|
btnIcon.Key.transform.parent.gameObject.SetActive(canSeeBinds);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var btnIcon in ModdedKeybindIcons)
|
foreach (var btnIcon in ModdedKeybindIcons)
|
||||||
{
|
{
|
||||||
btnIcon.transform.parent.gameObject.SetActive(canSeeBinds);
|
btnIcon.transform.parent.gameObject.SetActive(canSeeBinds);
|
||||||
}
|
}
|
||||||
|
|
||||||
var player = ReInput.players.GetPlayer(0);
|
var player = ReInput.players.GetPlayer(0);
|
||||||
|
var keyboard = player.controllers.Keyboard;
|
||||||
foreach (var entry in KeybindManager.Keybinds)
|
foreach (var entry in KeybindManager.Keybinds)
|
||||||
{
|
{
|
||||||
var keyboard = player.controllers.Keyboard;
|
var modKeys = entry.ModifierKeys;
|
||||||
bool modifierKeysPressed = entry.ModifierKeys.All(k => keyboard.GetModifierKey(k)) ||
|
bool modifierKeysPressed;
|
||||||
entry.ModifierKeys.Length <= 0;
|
if (modKeys.Length == 0)
|
||||||
|
{
|
||||||
|
modifierKeysPressed = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
modifierKeysPressed = true;
|
||||||
|
foreach (var key in modKeys)
|
||||||
|
{
|
||||||
|
if (keyboard.GetModifierKey(key)) continue;
|
||||||
|
modifierKeysPressed = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (player.GetButtonDown(entry.Id) && modifierKeysPressed)
|
if (player.GetButtonDown(entry.Id) && modifierKeysPressed)
|
||||||
{
|
{
|
||||||
entry.Invoke();
|
entry.Invoke();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var entry in KeybindManager.VanillaKeybinds.Values.Where(x => player.GetButtonDown(x.Id)))
|
// suppressed warning because we want minimum allocations here
|
||||||
|
#pragma warning disable S3267
|
||||||
|
foreach (var entry in KeybindManager.VanillaKeybinds.Values)
|
||||||
|
#pragma warning restore S3267
|
||||||
{
|
{
|
||||||
entry.Invoke();
|
if (player.GetButtonDown(entry.Id))
|
||||||
|
{
|
||||||
|
entry.Invoke();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,8 +16,15 @@ public static class OptionsMenuBehaviourPatch
|
|||||||
private static void OpenPostfix(OptionsMenuBehaviour __instance)
|
private static void OpenPostfix(OptionsMenuBehaviour __instance)
|
||||||
{
|
{
|
||||||
_remap_rollover = GameObject.Find("Remap Controls")?.GetComponent<ButtonRolloverHandler>()!;
|
_remap_rollover = GameObject.Find("Remap Controls")?.GetComponent<ButtonRolloverHandler>()!;
|
||||||
_remap_background = _remap_rollover.transform.FindChild("Background").GetComponent<SpriteRenderer>();
|
try
|
||||||
_remap_background.color = _conflicts ? Color.red : Color.white;
|
{
|
||||||
|
_remap_background = _remap_rollover.transform.FindChild("Background").GetComponent<SpriteRenderer>();
|
||||||
|
_remap_background.color = _conflicts ? Color.red : Color.white;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored, this normally breaks when on mobile since there's no remap button screen.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[HarmonyPostfix]
|
[HarmonyPostfix]
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ using Reactor.Utilities;
|
|||||||
using Reactor.Utilities.Extensions;
|
using Reactor.Utilities.Extensions;
|
||||||
using TMPro;
|
using TMPro;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.Events;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using Object = UnityEngine.Object;
|
using Object = UnityEngine.Object;
|
||||||
|
|
||||||
namespace MiraAPI.Patches.Menu;
|
namespace MiraAPI.Patches.Menu;
|
||||||
@@ -41,6 +39,10 @@ public static class HatsTabPatches
|
|||||||
[HarmonyPrefix]
|
[HarmonyPrefix]
|
||||||
public static bool OnEnablePrefix(HatsTab __instance)
|
public static bool OnEnablePrefix(HatsTab __instance)
|
||||||
{
|
{
|
||||||
|
if (!AddressablesLoader.AddressableHatsExist)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
__instance.currentHat = HatManager.Instance.GetHatById(DataManager.Player.Customization.Hat);
|
__instance.currentHat = HatManager.Instance.GetHatById(DataManager.Player.Customization.Hat);
|
||||||
var allHats = HatManager.Instance.GetUnlockedHats().ToImmutableList();
|
var allHats = HatManager.Instance.GetUnlockedHats().ToImmutableList();
|
||||||
|
|
||||||
@@ -66,6 +68,10 @@ public static class HatsTabPatches
|
|||||||
[HarmonyPrefix]
|
[HarmonyPrefix]
|
||||||
public static void UpdatePrefix(HatsTab __instance)
|
public static void UpdatePrefix(HatsTab __instance)
|
||||||
{
|
{
|
||||||
|
if (!AddressablesLoader.AddressableHatsExist)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (sortedHats.Count == 0) return;
|
if (sortedHats.Count == 0) return;
|
||||||
|
|
||||||
if (Input.GetKeyDown(KeyCode.LeftControl) || Input.GetKeyDown(KeyCode.RightControl) || Input.GetKeyDown(KeyCode.LeftArrow))
|
if (Input.GetKeyDown(KeyCode.LeftControl) || Input.GetKeyDown(KeyCode.RightControl) || Input.GetKeyDown(KeyCode.LeftArrow))
|
||||||
@@ -86,9 +92,10 @@ public static class HatsTabPatches
|
|||||||
if (loadRoutine != null) Coroutines.Stop(loadRoutine);
|
if (loadRoutine != null) Coroutines.Stop(loadRoutine);
|
||||||
|
|
||||||
hatIndex = 0;
|
hatIndex = 0;
|
||||||
foreach (var instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.Destroy();
|
foreach (var instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.DeepDestroy(false);
|
||||||
__instance.ColorChips.Clear();
|
__instance.ColorChips.Clear();
|
||||||
__instance.scroller.Inner.GetComponentsInChildren<TextMeshPro>().Do(x => x.gameObject.Destroy());
|
__instance.scroller.Inner.GetComponentsInChildren<TextMeshPro>().Do(x => x.gameObject.DeepDestroy(false));
|
||||||
|
Utilities.Extensions.ClearGarbageCollector();
|
||||||
|
|
||||||
var groupNameText = __instance.GetComponentInChildren<TextMeshPro>(false);
|
var groupNameText = __instance.GetComponentInChildren<TextMeshPro>(false);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
using HarmonyLib;
|
using System.Collections;
|
||||||
|
using HarmonyLib;
|
||||||
|
using MiraAPI.LocalSettings;
|
||||||
using MiraAPI.Utilities.Assets;
|
using MiraAPI.Utilities.Assets;
|
||||||
|
using Reactor.Utilities;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
namespace MiraAPI.Patches.Menu;
|
namespace MiraAPI.Patches.Menu;
|
||||||
|
|
||||||
@@ -17,5 +21,14 @@ public static class MainMenuManagerPatches
|
|||||||
public static void AwakePostfix()
|
public static void AwakePostfix()
|
||||||
{
|
{
|
||||||
AddressablesLoader.LoadAll();
|
AddressablesLoader.LoadAll();
|
||||||
|
Coroutines.Start(SetFps());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerator SetFps()
|
||||||
|
{
|
||||||
|
Application.targetFrameRate = (int)LocalSettingsTabSingleton<MiraApiSettings>.Instance.SetFpsSlider.Value;
|
||||||
|
yield return new WaitForSeconds(1f);
|
||||||
|
|
||||||
|
Application.targetFrameRate = (int)LocalSettingsTabSingleton<MiraApiSettings>.Instance.SetFpsSlider.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,7 @@ using Reactor.Utilities.Extensions;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using MiraAPI.Utilities.Assets;
|
||||||
using TMPro;
|
using TMPro;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using Object = UnityEngine.Object;
|
using Object = UnityEngine.Object;
|
||||||
@@ -44,6 +45,10 @@ public static class NameplatesTabPatches
|
|||||||
[HarmonyPrefix]
|
[HarmonyPrefix]
|
||||||
public static bool OnEnablePrefix(NameplatesTab __instance)
|
public static bool OnEnablePrefix(NameplatesTab __instance)
|
||||||
{
|
{
|
||||||
|
if (!AddressablesLoader.AddressableNameplatesExist)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
__instance.plateId = HatManager.Instance.GetNamePlateById(DataManager.Player.Customization.namePlate).ProdId;
|
__instance.plateId = HatManager.Instance.GetNamePlateById(DataManager.Player.Customization.namePlate).ProdId;
|
||||||
|
|
||||||
if (!SortedNameplates.ContainsKey("Vanilla")) AddRange(DestroyableSingleton<HatManager>.Instance.GetUnlockedNamePlates().Select(x => ("Vanilla", x)));
|
if (!SortedNameplates.ContainsKey("Vanilla")) AddRange(DestroyableSingleton<HatManager>.Instance.GetUnlockedNamePlates().Select(x => ("Vanilla", x)));
|
||||||
@@ -60,6 +65,10 @@ public static class NameplatesTabPatches
|
|||||||
|
|
||||||
public static void UpdatePrefix(NameplatesTab __instance)
|
public static void UpdatePrefix(NameplatesTab __instance)
|
||||||
{
|
{
|
||||||
|
if (!AddressablesLoader.AddressableNameplatesExist)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (Input.GetKeyDown(KeyCode.LeftControl) || Input.GetKeyDown(KeyCode.RightControl) || Input.GetKeyDown(KeyCode.LeftArrow))
|
if (Input.GetKeyDown(KeyCode.LeftControl) || Input.GetKeyDown(KeyCode.RightControl) || Input.GetKeyDown(KeyCode.LeftArrow))
|
||||||
{
|
{
|
||||||
PreviousPage(__instance);
|
PreviousPage(__instance);
|
||||||
@@ -72,9 +81,10 @@ public static class NameplatesTabPatches
|
|||||||
|
|
||||||
private static void GenerateHats(NameplatesTab __instance, int page)
|
private static void GenerateHats(NameplatesTab __instance, int page)
|
||||||
{
|
{
|
||||||
foreach (ColorChip instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.Destroy();
|
foreach (var instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.DeepDestroy(false);
|
||||||
__instance.ColorChips.Clear();
|
__instance.ColorChips.Clear();
|
||||||
__instance.scroller.Inner.GetComponentsInChildren<TextMeshPro>().Do(x => x.gameObject.Destroy());
|
__instance.scroller.Inner.GetComponentsInChildren<TextMeshPro>().Do(x => x.gameObject.DeepDestroy(false));
|
||||||
|
Utilities.Extensions.ClearGarbageCollector();
|
||||||
|
|
||||||
var groupNameText = __instance.GetComponentInChildren<TextMeshPro>(false);
|
var groupNameText = __instance.GetComponentInChildren<TextMeshPro>(false);
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using MiraAPI.Utilities.Assets;
|
||||||
using TMPro;
|
using TMPro;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using Object = UnityEngine.Object;
|
using Object = UnityEngine.Object;
|
||||||
@@ -36,6 +37,10 @@ public static class SkinsTabPatches
|
|||||||
[HarmonyPrefix]
|
[HarmonyPrefix]
|
||||||
public static bool OnEnablePrefix(SkinsTab __instance)
|
public static bool OnEnablePrefix(SkinsTab __instance)
|
||||||
{
|
{
|
||||||
|
if (!AddressablesLoader.AddressableSkinsExist)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
__instance.skinId = HatManager.Instance.GetSkinById(DataManager.Player.Customization.Skin).ProdId;
|
__instance.skinId = HatManager.Instance.GetSkinById(DataManager.Player.Customization.Skin).ProdId;
|
||||||
var allSkins = HatManager.Instance.GetUnlockedSkins().ToImmutableList();
|
var allSkins = HatManager.Instance.GetUnlockedSkins().ToImmutableList();
|
||||||
|
|
||||||
@@ -62,6 +67,10 @@ public static class SkinsTabPatches
|
|||||||
|
|
||||||
public static void UpdatePrefix(SkinsTab __instance)
|
public static void UpdatePrefix(SkinsTab __instance)
|
||||||
{
|
{
|
||||||
|
if (!AddressablesLoader.AddressableSkinsExist)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (Input.GetKeyDown(KeyCode.LeftControl) || Input.GetKeyDown(KeyCode.RightControl) || Input.GetKeyDown(KeyCode.LeftArrow))
|
if (Input.GetKeyDown(KeyCode.LeftControl) || Input.GetKeyDown(KeyCode.RightControl) || Input.GetKeyDown(KeyCode.LeftArrow))
|
||||||
{
|
{
|
||||||
PreviousPage(__instance);
|
PreviousPage(__instance);
|
||||||
@@ -74,9 +83,10 @@ public static class SkinsTabPatches
|
|||||||
|
|
||||||
private static void GenerateHats(SkinsTab __instance, int page)
|
private static void GenerateHats(SkinsTab __instance, int page)
|
||||||
{
|
{
|
||||||
foreach (ColorChip instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.Destroy();
|
foreach (var instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.DeepDestroy(false);
|
||||||
__instance.ColorChips.Clear();
|
__instance.ColorChips.Clear();
|
||||||
__instance.scroller.Inner.GetComponentsInChildren<TextMeshPro>().Do(x => x.gameObject.Destroy());
|
__instance.scroller.Inner.GetComponentsInChildren<TextMeshPro>().Do(x => x.gameObject.DeepDestroy(false));
|
||||||
|
Utilities.Extensions.ClearGarbageCollector();
|
||||||
|
|
||||||
var groupNameText = __instance.GetComponentInChildren<TextMeshPro>(false);
|
var groupNameText = __instance.GetComponentInChildren<TextMeshPro>(false);
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using Reactor.Utilities.Extensions;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using MiraAPI.Utilities.Assets;
|
||||||
using TMPro;
|
using TMPro;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using Object = UnityEngine.Object;
|
using Object = UnityEngine.Object;
|
||||||
@@ -44,6 +45,10 @@ public static class VisorsTabPatches
|
|||||||
[HarmonyPrefix]
|
[HarmonyPrefix]
|
||||||
public static bool OnEnablePrefix(VisorsTab __instance)
|
public static bool OnEnablePrefix(VisorsTab __instance)
|
||||||
{
|
{
|
||||||
|
if (!AddressablesLoader.AddressableVisorsExist)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
__instance.visorId = HatManager.Instance.GetVisorById(DataManager.Player.Customization.Visor).ProdId;
|
__instance.visorId = HatManager.Instance.GetVisorById(DataManager.Player.Customization.Visor).ProdId;
|
||||||
|
|
||||||
if (!SortedVisors.ContainsKey("Vanilla")) AddRange(DestroyableSingleton<HatManager>.Instance.GetUnlockedVisors().Select(x => ("Vanilla", x)));
|
if (!SortedVisors.ContainsKey("Vanilla")) AddRange(DestroyableSingleton<HatManager>.Instance.GetUnlockedVisors().Select(x => ("Vanilla", x)));
|
||||||
@@ -60,6 +65,10 @@ public static class VisorsTabPatches
|
|||||||
|
|
||||||
public static void UpdatePrefix(VisorsTab __instance)
|
public static void UpdatePrefix(VisorsTab __instance)
|
||||||
{
|
{
|
||||||
|
if (!AddressablesLoader.AddressableVisorsExist)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (Input.GetKeyDown(KeyCode.LeftControl) || Input.GetKeyDown(KeyCode.RightControl) || Input.GetKeyDown(KeyCode.LeftArrow))
|
if (Input.GetKeyDown(KeyCode.LeftControl) || Input.GetKeyDown(KeyCode.RightControl) || Input.GetKeyDown(KeyCode.LeftArrow))
|
||||||
{
|
{
|
||||||
PreviousPage(__instance);
|
PreviousPage(__instance);
|
||||||
@@ -72,9 +81,10 @@ public static class VisorsTabPatches
|
|||||||
|
|
||||||
private static void GenerateHats(VisorsTab __instance, int page)
|
private static void GenerateHats(VisorsTab __instance, int page)
|
||||||
{
|
{
|
||||||
foreach (ColorChip instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.Destroy();
|
foreach (var instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.DeepDestroy(false);
|
||||||
__instance.ColorChips.Clear();
|
__instance.ColorChips.Clear();
|
||||||
__instance.scroller.Inner.GetComponentsInChildren<TextMeshPro>().Do(x => x.gameObject.Destroy());
|
__instance.scroller.Inner.GetComponentsInChildren<TextMeshPro>().Do(x => x.gameObject.DeepDestroy(false));
|
||||||
|
Utilities.Extensions.ClearGarbageCollector();
|
||||||
|
|
||||||
var groupNameText = __instance.GetComponentInChildren<TextMeshPro>(false);
|
var groupNameText = __instance.GetComponentInChildren<TextMeshPro>(false);
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using AmongUs.GameOptions;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
|
using Il2CppInterop.Runtime.Attributes;
|
||||||
using MiraAPI.GameOptions;
|
using MiraAPI.GameOptions;
|
||||||
using MiraAPI.Modifiers;
|
using MiraAPI.Modifiers;
|
||||||
|
using MiraAPI.Presets;
|
||||||
using MiraAPI.Utilities;
|
using MiraAPI.Utilities;
|
||||||
using MiraAPI.Utilities.Assets;
|
using MiraAPI.Utilities.Assets;
|
||||||
using Reactor.Localization.Utilities;
|
using Reactor.Localization.Utilities;
|
||||||
@@ -10,6 +13,7 @@ using Reactor.Utilities.Extensions;
|
|||||||
using TMPro;
|
using TMPro;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.Events;
|
using UnityEngine.Events;
|
||||||
|
using UnityEngine.UI;
|
||||||
using Object = UnityEngine.Object;
|
using Object = UnityEngine.Object;
|
||||||
|
|
||||||
namespace MiraAPI.Patches.Options;
|
namespace MiraAPI.Patches.Options;
|
||||||
@@ -17,6 +21,20 @@ namespace MiraAPI.Patches.Options;
|
|||||||
[HarmonyPatch(typeof(GameOptionsMenu))]
|
[HarmonyPatch(typeof(GameOptionsMenu))]
|
||||||
internal static class GameOptionsMenuPatch
|
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]
|
[HarmonyPostfix]
|
||||||
[HarmonyPatch(nameof(GameOptionsMenu.Update))]
|
[HarmonyPatch(nameof(GameOptionsMenu.Update))]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
@@ -121,11 +139,25 @@ internal static class GameOptionsMenuPatch
|
|||||||
|
|
||||||
private static void CustomMenuOneCreate(GameOptionsMenu menu)
|
private static void CustomMenuOneCreate(GameOptionsMenu menu)
|
||||||
{
|
{
|
||||||
var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
|
var existing = GameSettingMenuPatches.CachedGameSettings.FirstOrDefault(x => x.ModId == GameSettingMenuPatches.SelectedModIdx && x.Category == MenuCategory.CustomOne);
|
||||||
.Where(x => x.ParentMenu == MenuCategory.CustomOne) ?? [];
|
if (existing != null)
|
||||||
foreach (var group in groups)
|
|
||||||
{
|
{
|
||||||
CreateGroup(menu, group);
|
GameSettingMenuPatches.ShowGameOptions(existing, menu);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
|
||||||
|
.Where(x => x.ParentMenu == MenuCategory.CustomOne) ?? [];
|
||||||
|
foreach (var group in groups)
|
||||||
|
{
|
||||||
|
CreateGroup(menu, group);
|
||||||
|
}
|
||||||
|
GameSettingMenuPatches.AddGameSettings(
|
||||||
|
MenuCategory.CustomOne,
|
||||||
|
menu.Children,
|
||||||
|
menu.settingsContainer
|
||||||
|
.GetComponentsInChildren<CategoryHeaderMasked>()
|
||||||
|
.ToList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,18 +174,32 @@ internal static class GameOptionsMenuPatch
|
|||||||
|
|
||||||
private static void CustomMenuTwoCreate(GameOptionsMenu menu)
|
private static void CustomMenuTwoCreate(GameOptionsMenu menu)
|
||||||
{
|
{
|
||||||
var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
|
var existing = GameSettingMenuPatches.CachedGameSettings.FirstOrDefault(x => x.ModId == GameSettingMenuPatches.SelectedModIdx && x.Category == MenuCategory.CustomTwo);
|
||||||
.Where(x => x.ParentMenu == MenuCategory.CustomTwo) ?? [];
|
if (existing != null)
|
||||||
foreach (var group in groups)
|
|
||||||
{
|
{
|
||||||
CreateGroup(menu, group);
|
GameSettingMenuPatches.ShowGameOptions(existing, menu);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
|
||||||
|
.Where(x => x.ParentMenu == MenuCategory.CustomTwo) ?? [];
|
||||||
|
foreach (var group in groups)
|
||||||
|
{
|
||||||
|
CreateGroup(menu, group);
|
||||||
|
}
|
||||||
|
GameSettingMenuPatches.AddGameSettings(
|
||||||
|
MenuCategory.CustomTwo,
|
||||||
|
menu.Children,
|
||||||
|
menu.settingsContainer
|
||||||
|
.GetComponentsInChildren<CategoryHeaderMasked>()
|
||||||
|
.ToList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ModifiersUpdate(ref float num)
|
private static void ModifiersUpdate(ref float num)
|
||||||
{
|
{
|
||||||
var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
|
var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
|
||||||
.Where(x => x.ShowInModifiersMenu || x.OptionableType?.IsAssignableTo(typeof(BaseModifier))==true) ?? [];
|
.Where(x => x.ShowInModifiersMenu || x.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true) ?? [];
|
||||||
|
|
||||||
foreach (var modGroup in groups)
|
foreach (var modGroup in groups)
|
||||||
{
|
{
|
||||||
@@ -163,11 +209,25 @@ internal static class GameOptionsMenuPatch
|
|||||||
|
|
||||||
private static void ModifiersCreate(GameOptionsMenu menu)
|
private static void ModifiersCreate(GameOptionsMenu menu)
|
||||||
{
|
{
|
||||||
var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
|
var existing = GameSettingMenuPatches.CachedGameSettings.FirstOrDefault(x => x.ModId == GameSettingMenuPatches.SelectedModIdx && x.Category == MenuCategory.Modifiers);
|
||||||
.Where(x => x.ShowInModifiersMenu || x.OptionableType?.IsAssignableTo(typeof(BaseModifier))==true) ?? [];
|
if (existing != null)
|
||||||
foreach (var group in groups)
|
|
||||||
{
|
{
|
||||||
CreateGroup(menu, group);
|
GameSettingMenuPatches.ShowGameOptions(existing, menu);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
|
||||||
|
.Where(x => x.ShowInModifiersMenu || x.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true) ?? [];
|
||||||
|
foreach (var group in groups)
|
||||||
|
{
|
||||||
|
CreateGroup(menu, group);
|
||||||
|
}
|
||||||
|
GameSettingMenuPatches.AddGameSettings(
|
||||||
|
MenuCategory.Modifiers,
|
||||||
|
menu.Children,
|
||||||
|
menu.settingsContainer
|
||||||
|
.GetComponentsInChildren<CategoryHeaderMasked>()
|
||||||
|
.ToList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,12 +259,20 @@ internal static class GameOptionsMenuPatch
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var filteredGroups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
|
var existing = GameSettingMenuPatches.CachedGameSettings.FirstOrDefault(x => x.ModId == GameSettingMenuPatches.SelectedModIdx && x.Category == MenuCategory.Game);
|
||||||
.Where(x => x.OptionableType == null && !x.ShowInModifiersMenu && x.ParentMenu == MenuCategory.Roles) ?? [];
|
if (existing != null)
|
||||||
|
|
||||||
foreach (var group in filteredGroups)
|
|
||||||
{
|
{
|
||||||
CreateGroup(__instance, group);
|
GameSettingMenuPatches.ShowGameOptions(existing, __instance);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var filteredGroups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
|
||||||
|
.Where(x => x.OptionableType == null && !x.ShowInModifiersMenu && x.ParentMenu == MenuCategory.Roles) ?? [];
|
||||||
|
|
||||||
|
foreach (var group in filteredGroups)
|
||||||
|
{
|
||||||
|
CreateGroup(__instance, group);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -257,7 +325,9 @@ internal static class GameOptionsMenuPatch
|
|||||||
categoryHeaderMasked.SetHeader(CustomStringName.CreateAndRegister(group.GroupName), 20);
|
categoryHeaderMasked.SetHeader(CustomStringName.CreateAndRegister(group.GroupName), 20);
|
||||||
categoryHeaderMasked.Background.color = group.GroupColor;
|
categoryHeaderMasked.Background.color = group.GroupColor;
|
||||||
categoryHeaderMasked.Divider.color = group.GroupColor;
|
categoryHeaderMasked.Divider.color = group.GroupColor;
|
||||||
categoryHeaderMasked.Title.color = group.GroupColor.Equals(MiraApiPlugin.DefaultHeaderColor) ? Color.white : group.GroupColor.FindAlternateColor();
|
categoryHeaderMasked.Title.color = group.GroupColor.Equals(MiraApiPlugin.DefaultHeaderColor)
|
||||||
|
? Color.white
|
||||||
|
: group.GroupColor.FindAlternateColor();
|
||||||
|
|
||||||
categoryHeaderMasked.Background.sprite = MiraAssets.CategoryHeader.LoadAsset();
|
categoryHeaderMasked.Background.sprite = MiraAssets.CategoryHeader.LoadAsset();
|
||||||
categoryHeaderMasked.Background.sprite.texture.filterMode = FilterMode.Bilinear;
|
categoryHeaderMasked.Background.sprite.texture.filterMode = FilterMode.Bilinear;
|
||||||
@@ -276,13 +346,20 @@ internal static class GameOptionsMenuPatch
|
|||||||
newText.transform.localPosition = new Vector3(2.6249f, -0.165f, 0f);
|
newText.transform.localPosition = new Vector3(2.6249f, -0.165f, 0f);
|
||||||
newText.gameObject.GetComponent<TextTranslatorTMP>().Destroy();
|
newText.gameObject.GetComponent<TextTranslatorTMP>().Destroy();
|
||||||
|
|
||||||
var options = group.Options.Select(
|
var options = group.Options.Select(opt => opt.CreateOption(
|
||||||
opt => opt.CreateOption(
|
menu.checkboxOrigin,
|
||||||
menu.checkboxOrigin,
|
menu.numberOptionOrigin,
|
||||||
menu.numberOptionOrigin,
|
menu.stringOptionOrigin,
|
||||||
menu.stringOptionOrigin,
|
menu.playerOptionOrigin,
|
||||||
menu.playerOptionOrigin,
|
menu.settingsContainer));
|
||||||
menu.settingsContainer));
|
|
||||||
|
OptionPreset? defaultPreset = null;
|
||||||
|
if (GameSettingMenuPatches.SelectedMod != null && PresetManager.DefaultPresets.TryGetValue(
|
||||||
|
GameSettingMenuPatches.SelectedMod,
|
||||||
|
out var preset))
|
||||||
|
{
|
||||||
|
defaultPreset = preset;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var newOpt in options)
|
foreach (var newOpt in options)
|
||||||
{
|
{
|
||||||
@@ -318,14 +395,79 @@ internal static class GameOptionsMenuPatch
|
|||||||
if (newOpt is ToggleOption toggle)
|
if (newOpt is ToggleOption toggle)
|
||||||
{
|
{
|
||||||
toggle.CheckMark.sprite = MiraAssets.Checkmark.LoadAsset();
|
toggle.CheckMark.sprite = MiraAssets.Checkmark.LoadAsset();
|
||||||
toggle.CheckMark.color = group.GroupColor != MiraApiPlugin.DefaultHeaderColor ? group.GroupColor : MiraAssets.AcceptedTeal;
|
toggle.CheckMark.color = group.GroupColor != MiraApiPlugin.DefaultHeaderColor
|
||||||
|
? group.GroupColor
|
||||||
|
: MiraAssets.AcceptedTeal;
|
||||||
var rend = toggle.CheckMark.transform.parent.FindChild("ActiveSprite")
|
var rend = toggle.CheckMark.transform.parent.FindChild("ActiveSprite")
|
||||||
.GetComponent<SpriteRenderer>();
|
.GetComponent<SpriteRenderer>();
|
||||||
rend.sprite = MiraAssets.CheckmarkBox.LoadAsset();
|
rend.sprite = MiraAssets.CheckmarkBox.LoadAsset();
|
||||||
rend.color = group.GroupColor != MiraApiPlugin.DefaultHeaderColor ? group.GroupColor : MiraAssets.AcceptedTeal;
|
rend.color = group.GroupColor != MiraApiPlugin.DefaultHeaderColor
|
||||||
|
? group.GroupColor
|
||||||
|
: MiraAssets.AcceptedTeal;
|
||||||
}
|
}
|
||||||
|
|
||||||
menu.Children.Add(newOpt);
|
menu.Children.Add(newOpt);
|
||||||
|
var resetBtn = new GameObject("ResetOption");
|
||||||
|
resetBtn.transform.parent = newOpt.transform;
|
||||||
|
resetBtn.transform.localScale = new(.5f, .5f, 1);
|
||||||
|
resetBtn.layer = LayerMask.NameToLayer("UI");
|
||||||
|
resetBtn.transform.localPosition = new Vector3(-3.1f, 0f, -2f);
|
||||||
|
var resetRend = resetBtn.AddComponent<SpriteRenderer>();
|
||||||
|
resetRend.sprite = MiraAssets.ResetButton.LoadAsset();
|
||||||
|
resetRend.maskInteraction = SpriteMaskInteraction.VisibleInsideMask;
|
||||||
|
resetRend.color = group.GroupColor.Equals(MiraApiPlugin.DefaultHeaderColor)
|
||||||
|
? Color.white
|
||||||
|
: group.GroupColor.FindAlternateColor();
|
||||||
|
var resetBoxCol = resetBtn.gameObject.AddComponent<BoxCollider2D>();
|
||||||
|
resetBoxCol.size = new Vector2(1f, 1f);
|
||||||
|
resetBoxCol.offset = new Vector2(0, 0);
|
||||||
|
var passiveButton = resetBtn.AddComponent<PassiveButton>();
|
||||||
|
passiveButton.OnClick = new Button.ButtonClickedEvent();
|
||||||
|
passiveButton.ClickSound = menu.BackButton.GetComponent<PassiveButton>().ClickSound;
|
||||||
|
passiveButton.OnMouseOver = new UnityEvent();
|
||||||
|
passiveButton.OnMouseOver.AddListener(
|
||||||
|
(UnityAction)(() =>
|
||||||
|
{
|
||||||
|
resetRend.color = group.GroupColor != MiraApiPlugin.DefaultHeaderColor
|
||||||
|
? group.GroupColor
|
||||||
|
: MiraAssets.AcceptedTeal;
|
||||||
|
}));
|
||||||
|
passiveButton.OnMouseOut = new UnityEvent();
|
||||||
|
passiveButton.OnMouseOut.AddListener(
|
||||||
|
(UnityAction)(() =>
|
||||||
|
{
|
||||||
|
resetRend.color = group.GroupColor.Equals(MiraApiPlugin.DefaultHeaderColor)
|
||||||
|
? Color.white
|
||||||
|
: group.GroupColor.FindAlternateColor();
|
||||||
|
}));
|
||||||
|
if (newOpt is ToggleOption toggleOpt)
|
||||||
|
{
|
||||||
|
passiveButton.OnClick.AddListener(
|
||||||
|
(UnityAction)(() =>
|
||||||
|
{
|
||||||
|
defaultPreset!.ResetOption(toggleOpt);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
else if (newOpt is NumberOption numOpt)
|
||||||
|
{
|
||||||
|
passiveButton.OnClick.AddListener(
|
||||||
|
(UnityAction)(() =>
|
||||||
|
{
|
||||||
|
defaultPreset!.ResetOption(numOpt);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
else if (newOpt is StringOption strOpt)
|
||||||
|
{
|
||||||
|
passiveButton.OnClick.AddListener(
|
||||||
|
(UnityAction)(() =>
|
||||||
|
{
|
||||||
|
defaultPreset!.ResetOption(strOpt);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
if (!defaultPreset!.IsOptionInPreset(newOpt))
|
||||||
|
{
|
||||||
|
resetBtn.Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
newOpt.Initialize();
|
newOpt.Initialize();
|
||||||
newOpt.gameObject.SetActive(false);
|
newOpt.gameObject.SetActive(false);
|
||||||
|
|||||||
@@ -262,7 +262,7 @@ internal static class GamePresetsTabPatches
|
|||||||
iconSpriteRend.sprite = MiraAssets.RefreshIcon.LoadAsset();
|
iconSpriteRend.sprite = MiraAssets.RefreshIcon.LoadAsset();
|
||||||
|
|
||||||
Helpers.DivideSize(refreshButton.gameObject, 2f);
|
Helpers.DivideSize(refreshButton.gameObject, 2f);
|
||||||
refreshButton.buttonText.gameObject.Destroy();
|
refreshButton.buttonText.gameObject.DeepDestroy();
|
||||||
|
|
||||||
refreshButton.OnClick = new Button.ButtonClickedEvent();
|
refreshButton.OnClick = new Button.ButtonClickedEvent();
|
||||||
refreshButton.OnClick.AddListener((UnityAction)Refresh);
|
refreshButton.OnClick.AddListener((UnityAction)Refresh);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using MiraAPI.GameOptions;
|
|||||||
using MiraAPI.Modifiers;
|
using MiraAPI.Modifiers;
|
||||||
using MiraAPI.PluginLoading;
|
using MiraAPI.PluginLoading;
|
||||||
using MiraAPI.Presets;
|
using MiraAPI.Presets;
|
||||||
|
using MiraAPI.Utilities;
|
||||||
using MiraAPI.Utilities.Assets;
|
using MiraAPI.Utilities.Assets;
|
||||||
using Reactor.Utilities.Extensions;
|
using Reactor.Utilities.Extensions;
|
||||||
using TMPro;
|
using TMPro;
|
||||||
@@ -40,6 +41,140 @@ internal static class GameSettingMenuPatches
|
|||||||
private static PassiveButton? _customTwoButton;
|
private static PassiveButton? _customTwoButton;
|
||||||
private static GameObject? _nextModButton;
|
private static GameObject? _nextModButton;
|
||||||
private static GameObject? _previousModButton;
|
private static GameObject? _previousModButton;
|
||||||
|
public static List<GameSettingsCache> CachedGameSettings { get; } = [];
|
||||||
|
public static List<RoleSettingsCache> CachedRoleSettings { get; } = [];
|
||||||
|
|
||||||
|
public sealed class GameSettingsCache
|
||||||
|
{
|
||||||
|
public int ModId { get; set; }
|
||||||
|
public MenuCategory Category { get; set; } = MenuCategory.Game;
|
||||||
|
public Il2CppSystem.Collections.Generic.List<OptionBehaviour> Options { get; set; }
|
||||||
|
public List<CategoryHeaderMasked> CategoryHeaders { get; set; }
|
||||||
|
public GameObject CacheHolder { get; set; }
|
||||||
|
}
|
||||||
|
public static GameSettingsCache GetCurrentSettings(MenuCategory category)
|
||||||
|
{
|
||||||
|
var existing = CachedGameSettings.FirstOrDefault(x => x.ModId == SelectedModIdx && x.Category == category);
|
||||||
|
if (existing != null)
|
||||||
|
{
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
|
||||||
|
var menu = category switch
|
||||||
|
{
|
||||||
|
MenuCategory.Modifiers => _modifiersTab!,
|
||||||
|
MenuCategory.CustomOne => _customOneTab!,
|
||||||
|
MenuCategory.CustomTwo => _customTwoTab!,
|
||||||
|
_ => GameSettingMenu.Instance.GameSettingsTab,
|
||||||
|
};
|
||||||
|
return AddGameSettings(
|
||||||
|
category,
|
||||||
|
menu.Children,
|
||||||
|
menu.settingsContainer
|
||||||
|
.GetComponentsInChildren<CategoryHeaderMasked>()
|
||||||
|
.ToList());
|
||||||
|
}
|
||||||
|
public static GameSettingsCache AddGameSettings(MenuCategory category, Il2CppSystem.Collections.Generic.List<OptionBehaviour> options, List<CategoryHeaderMasked> categoryHeaders)
|
||||||
|
{
|
||||||
|
var modName = SelectedMod?.MiraPlugin.OptionsTitleText ?? "Vanilla";
|
||||||
|
var newHolder = new GameObject($"{modName} {category}");
|
||||||
|
newHolder.DontUnload().DontDestroy();
|
||||||
|
newHolder.SetActive(false);
|
||||||
|
var newCache = new GameSettingsCache
|
||||||
|
{
|
||||||
|
ModId = SelectedModIdx,
|
||||||
|
Category = category,
|
||||||
|
Options = options,
|
||||||
|
CategoryHeaders = categoryHeaders,
|
||||||
|
CacheHolder = newHolder,
|
||||||
|
};
|
||||||
|
CachedGameSettings.Add(newCache);
|
||||||
|
return newCache;
|
||||||
|
}
|
||||||
|
public static void HideGameOptions(GameSettingsCache cached, GameOptionsMenu gameOptMenu)
|
||||||
|
{
|
||||||
|
foreach (var obj in gameOptMenu.scrollBar.Inner.GetComponentsInChildren<Transform>())
|
||||||
|
{
|
||||||
|
if (obj.gameObject.name == "MapPicker" || !obj.gameObject)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
obj.SetParent(cached.CacheHolder.transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void ShowGameOptions(GameSettingsCache cached, GameOptionsMenu gameOptMenu)
|
||||||
|
{
|
||||||
|
foreach (var obj in cached.CacheHolder.GetComponentsInChildren<Transform>())
|
||||||
|
{
|
||||||
|
if (!obj.gameObject)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
obj.SetParent(gameOptMenu.scrollBar.Inner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public sealed class RoleSettingsCache
|
||||||
|
{
|
||||||
|
public int ModId { get; set; }
|
||||||
|
public Il2CppSystem.Collections.Generic.List<OptionBehaviour> Options { get; set; }
|
||||||
|
public List<CategoryHeaderEditRole> RoleHeaders { get; set; }
|
||||||
|
public List<RoleOptionSetting> RoleOptions { get; set; }
|
||||||
|
public List<CategoryHeaderEditRole> CategoryHeaders { get; set; }
|
||||||
|
public GameObject CacheHolder { get; set; }
|
||||||
|
}
|
||||||
|
public static RoleSettingsCache GetCurrentRoleSettings()
|
||||||
|
{
|
||||||
|
var existing = CachedRoleSettings.FirstOrDefault(x => x.ModId == SelectedModIdx);
|
||||||
|
if (existing != null)
|
||||||
|
{
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
|
||||||
|
var menu = GameSettingMenu.Instance.RoleSettingsTab;
|
||||||
|
return AddRoleSettings(
|
||||||
|
menu.advancedSettingChildren,
|
||||||
|
menu.RoleChancesSettings
|
||||||
|
.GetComponentsInChildren<CategoryHeaderEditRole>()
|
||||||
|
.ToList());
|
||||||
|
}
|
||||||
|
public static RoleSettingsCache AddRoleSettings(Il2CppSystem.Collections.Generic.List<OptionBehaviour> options, List<CategoryHeaderEditRole> categoryHeaders)
|
||||||
|
{
|
||||||
|
var modName = SelectedMod?.MiraPlugin.OptionsTitleText ?? "Vanilla";
|
||||||
|
var newHolder = new GameObject($"{modName} Roles");
|
||||||
|
newHolder.DontUnload().DontDestroy();
|
||||||
|
newHolder.SetActive(false);
|
||||||
|
var newCache = new RoleSettingsCache
|
||||||
|
{
|
||||||
|
ModId = SelectedModIdx,
|
||||||
|
Options = options,
|
||||||
|
CategoryHeaders = categoryHeaders,
|
||||||
|
CacheHolder = newHolder,
|
||||||
|
};
|
||||||
|
CachedRoleSettings.Add(newCache);
|
||||||
|
return newCache;
|
||||||
|
}
|
||||||
|
public static void HideRoleOptions(RoleSettingsCache cached, RolesSettingsMenu gameOptMenu)
|
||||||
|
{
|
||||||
|
foreach (var obj in gameOptMenu.scrollBar.Inner.GetComponentsInChildren<Transform>())
|
||||||
|
{
|
||||||
|
if (obj.gameObject.name == "MapPicker" || !obj.gameObject)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
obj.SetParent(cached.CacheHolder.transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void ShowRoleOptions(RoleSettingsCache cached, RolesSettingsMenu gameOptMenu)
|
||||||
|
{
|
||||||
|
foreach (var obj in cached.CacheHolder.GetComponentsInChildren<Transform>())
|
||||||
|
{
|
||||||
|
if (!obj.gameObject)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
obj.SetParent(gameOptMenu.scrollBar.Inner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static Dictionary<int, Vector3> OptionsPositions { get; } = [];
|
private static Dictionary<int, Vector3> OptionsPositions { get; } = [];
|
||||||
private static Dictionary<int, Vector3> ModifiersPositions { get; } = [];
|
private static Dictionary<int, Vector3> ModifiersPositions { get; } = [];
|
||||||
@@ -564,21 +699,6 @@ internal static class GameSettingMenuPatches
|
|||||||
CleanupTab(settings, roles);
|
CleanupTab(settings, roles);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ClearOptions(Il2CppSystem.Collections.Generic.List<OptionBehaviour> options)
|
|
||||||
{
|
|
||||||
foreach (var child in options)
|
|
||||||
{
|
|
||||||
if (child.TryCast<GameOptionsMapPicker>() || !child.gameObject)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
child.gameObject.DestroyImmediate();
|
|
||||||
}
|
|
||||||
|
|
||||||
options.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void CleanupTab(GameOptionsMenu settings, RolesSettingsMenu roles)
|
private static void CleanupTab(GameOptionsMenu settings, RolesSettingsMenu roles)
|
||||||
{
|
{
|
||||||
if (roles.roleChances != null)
|
if (roles.roleChances != null)
|
||||||
@@ -608,24 +728,10 @@ internal static class GameSettingMenuPatches
|
|||||||
|
|
||||||
void CleanupRoleSettings(RolesSettingsMenu rolesMenu)
|
void CleanupRoleSettings(RolesSettingsMenu rolesMenu)
|
||||||
{
|
{
|
||||||
if (rolesMenu.advancedSettingChildren != null)
|
HideRoleOptions(GetCurrentRoleSettings(), rolesMenu);
|
||||||
{
|
rolesMenu.advancedSettingChildren = null;
|
||||||
ClearOptions(rolesMenu.advancedSettingChildren);
|
|
||||||
rolesMenu.advancedSettingChildren = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
rolesMenu.RoleChancesSettings
|
rolesMenu.roleChances.Clear();
|
||||||
.transform
|
|
||||||
.GetComponentsInChildren<CategoryHeaderEditRole>()
|
|
||||||
.ToList()
|
|
||||||
.ForEach(header => header.gameObject.DestroyImmediate());
|
|
||||||
|
|
||||||
foreach (var role in rolesMenu.roleChances)
|
|
||||||
{
|
|
||||||
role.gameObject.DestroyImmediate();
|
|
||||||
}
|
|
||||||
|
|
||||||
rolesMenu.roleChances?.Clear();
|
|
||||||
|
|
||||||
rolesMenu.AdvancedRolesSettings.gameObject.SetActive(false);
|
rolesMenu.AdvancedRolesSettings.gameObject.SetActive(false);
|
||||||
rolesMenu.RoleChancesSettings.gameObject.SetActive(true);
|
rolesMenu.RoleChancesSettings.gameObject.SetActive(true);
|
||||||
@@ -643,14 +749,16 @@ internal static class GameSettingMenuPatches
|
|||||||
|
|
||||||
void CleanupSettings(GameOptionsMenu gameOptMenu, MenuCategory menuCategory = MenuCategory.Roles)
|
void CleanupSettings(GameOptionsMenu gameOptMenu, MenuCategory menuCategory = MenuCategory.Roles)
|
||||||
{
|
{
|
||||||
ClearOptions(gameOptMenu.Children);
|
if (menuCategory is MenuCategory.Roles)
|
||||||
|
{
|
||||||
|
HideRoleOptions(GetCurrentRoleSettings(), gameOptMenu.RolesMenu);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
HideGameOptions(GetCurrentSettings(menuCategory), gameOptMenu);
|
||||||
|
}
|
||||||
gameOptMenu.Children = null;
|
gameOptMenu.Children = null;
|
||||||
|
|
||||||
gameOptMenu.settingsContainer
|
|
||||||
.GetComponentsInChildren<CategoryHeaderMasked>()
|
|
||||||
.ToList()
|
|
||||||
.ForEach(header => header.gameObject.DestroyImmediate());
|
|
||||||
|
|
||||||
gameOptMenu.Initialize();
|
gameOptMenu.Initialize();
|
||||||
var positions = OptionsPositions;
|
var positions = OptionsPositions;
|
||||||
switch (menuCategory)
|
switch (menuCategory)
|
||||||
|
|||||||
@@ -210,12 +210,19 @@ public static class LobbyViewPanePatches
|
|||||||
list.Add(roleBehaviour);
|
list.Add(roleBehaviour);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var color = (i == 0) ? Palette.CrewmateRoleBlue : Palette.ImpostorRoleRed;
|
||||||
|
if (roleBehaviour is ICustomRole custom && (custom.Team is not ModdedRoleTeams.Crewmate &&
|
||||||
|
custom.Team is not ModdedRoleTeams.Impostor))
|
||||||
|
{
|
||||||
|
color = Color.grey;
|
||||||
|
}
|
||||||
|
|
||||||
viewSettingsInfoPanelRoleVariant.SetInfo(
|
viewSettingsInfoPanelRoleVariant.SetInfo(
|
||||||
roleBehaviour.NiceName,
|
roleBehaviour.NiceName,
|
||||||
numPerGame,
|
numPerGame,
|
||||||
chancePerGame,
|
chancePerGame,
|
||||||
61,
|
61,
|
||||||
(i == 0) ? Palette.CrewmateRoleBlue : Palette.ImpostorRoleRed,
|
color,
|
||||||
roleBehaviour.RoleIconSolid,
|
roleBehaviour.RoleIconSolid,
|
||||||
i == 0,
|
i == 0,
|
||||||
flag);
|
flag);
|
||||||
@@ -513,6 +520,34 @@ public static class LobbyViewPanePatches
|
|||||||
instance.scrollBar.SetYBoundsMax(-num);
|
instance.scrollBar.SetYBoundsMax(-num);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void SetModdedHeader(this CategoryHeaderRoleVariant header, StringNames roleName, int maskLayer, ModdedRoleTeams team, Sprite? roleIcon = null)
|
||||||
|
{
|
||||||
|
header.SetHeader(roleName, maskLayer);
|
||||||
|
if (team is ModdedRoleTeams.Crewmate)
|
||||||
|
{
|
||||||
|
header.Background.color = Palette.CrewmateRoleHeaderBlue;
|
||||||
|
header.Divider.color = Palette.CrewmateRoleHeaderBlue;
|
||||||
|
header.Title.color = Palette.CrewmateRoleHeaderTextBlue;
|
||||||
|
}
|
||||||
|
else if (team is ModdedRoleTeams.Impostor)
|
||||||
|
{
|
||||||
|
header.Background.color = Palette.ImpostorRoleHeaderRed;
|
||||||
|
header.Divider.color = Palette.ImpostorRoleHeaderRed;
|
||||||
|
header.Title.color = Palette.ImpostorRoleHeaderTextRed;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
header.Background.color = Color.grey;
|
||||||
|
header.Divider.color = Color.grey;
|
||||||
|
header.Title.color = new Color32(50, 50, 50, 255);
|
||||||
|
}
|
||||||
|
if (roleIcon != null && header.icon != null)
|
||||||
|
{
|
||||||
|
header.icon.material.SetInt(PlayerMaterial.MaskLayer, maskLayer);
|
||||||
|
header.icon.sprite = roleIcon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static float SetUpAdvancedRoleViewPanel(
|
private static float SetUpAdvancedRoleViewPanel(
|
||||||
AdvancedRoleViewPanel viewPanel,
|
AdvancedRoleViewPanel viewPanel,
|
||||||
Type roleType,
|
Type roleType,
|
||||||
@@ -536,10 +571,10 @@ public static class LobbyViewPanePatches
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
viewPanel.header.SetHeader(
|
viewPanel.header.SetModdedHeader(
|
||||||
role.StringName,
|
role.StringName,
|
||||||
maskLayer,
|
maskLayer,
|
||||||
role.TeamType == RoleTeamTypes.Crewmate,
|
customRole.Team,
|
||||||
customRole.Configuration.Icon != null ? customRole.Configuration.Icon.LoadAsset() : MiraAssets.Empty.LoadAsset());
|
customRole.Configuration.Icon != null ? customRole.Configuration.Icon.LoadAsset() : MiraAssets.Empty.LoadAsset());
|
||||||
viewPanel.header.icon.transform.localScale = new Vector3(0.465f, 0.465f, 1f);
|
viewPanel.header.icon.transform.localScale = new Vector3(0.465f, 0.465f, 1f);
|
||||||
viewPanel.divider.material.SetInt(PlayerMaterial.MaskLayer, maskLayer);
|
viewPanel.divider.material.SetInt(PlayerMaterial.MaskLayer, maskLayer);
|
||||||
|
|||||||
@@ -35,6 +35,13 @@ public static class RoleSettingMenuPatches
|
|||||||
private static RoleBehaviour? CurrentRole { get; set; }
|
private static RoleBehaviour? CurrentRole { get; set; }
|
||||||
private static List<IModdedOption>? CurrentRoleOptions { 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]
|
[HarmonyPostfix]
|
||||||
[HarmonyPatch(nameof(RolesSettingsMenu.SetQuotaTab))]
|
[HarmonyPatch(nameof(RolesSettingsMenu.SetQuotaTab))]
|
||||||
public static void SetQuotaTabPostfix(RolesSettingsMenu __instance)
|
public static void SetQuotaTabPostfix(RolesSettingsMenu __instance)
|
||||||
@@ -46,9 +53,11 @@ public static class RoleSettingMenuPatches
|
|||||||
[HarmonyPatch(nameof(RolesSettingsMenu.SetQuotaTab))]
|
[HarmonyPatch(nameof(RolesSettingsMenu.SetQuotaTab))]
|
||||||
public static bool SetQuotaTabPatch(RolesSettingsMenu __instance)
|
public static bool SetQuotaTabPatch(RolesSettingsMenu __instance)
|
||||||
{
|
{
|
||||||
Headers.ForEach(Object.Destroy);
|
GameSettingMenuPatches.HideRoleOptions(GameSettingMenuPatches.GetCurrentRoleSettings(), __instance);
|
||||||
RoleOptionSettings.ForEach(Object.Destroy);
|
__instance.advancedSettingChildren = null;
|
||||||
|
|
||||||
Headers.Clear();
|
Headers.Clear();
|
||||||
|
__instance.roleChances.Clear();
|
||||||
RoleOptionSettings.Clear();
|
RoleOptionSettings.Clear();
|
||||||
CurrentRole = null;
|
CurrentRole = null;
|
||||||
CurrentRoleOptions = null;
|
CurrentRoleOptions = null;
|
||||||
@@ -126,6 +135,7 @@ public static class RoleSettingMenuPatches
|
|||||||
num3++;
|
num3++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Utilities.Extensions.ClearGarbageCollector();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,6 +157,7 @@ public static class RoleSettingMenuPatches
|
|||||||
|
|
||||||
if (roleGroups is null)
|
if (roleGroups is null)
|
||||||
{
|
{
|
||||||
|
Utilities.Extensions.ClearGarbageCollector();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,9 +208,9 @@ public static class RoleSettingMenuPatches
|
|||||||
var blankLabel = quotaInst.transform.FindChild("BlankLabel").gameObject;
|
var blankLabel = quotaInst.transform.FindChild("BlankLabel").gameObject;
|
||||||
var chanceLabel = quotaInst.transform.FindChild("Chance Label").gameObject;
|
var chanceLabel = quotaInst.transform.FindChild("Chance Label").gameObject;
|
||||||
var countLabel = quotaInst.transform.FindChild("# Label").gameObject;
|
var countLabel = quotaInst.transform.FindChild("# Label").gameObject;
|
||||||
blankLabel.Destroy();
|
blankLabel.DeepDestroy(false);
|
||||||
chanceLabel.Destroy();
|
chanceLabel.DeepDestroy(false);
|
||||||
countLabel.Destroy();
|
countLabel.DeepDestroy(false);
|
||||||
|
|
||||||
categoryHeaderMasked.Background.sprite = MiraAssets.CategoryHeader.LoadAsset();
|
categoryHeaderMasked.Background.sprite = MiraAssets.CategoryHeader.LoadAsset();
|
||||||
categoryHeaderMasked.Background.sprite.texture.filterMode = FilterMode.Bilinear;
|
categoryHeaderMasked.Background.sprite.texture.filterMode = FilterMode.Bilinear;
|
||||||
@@ -282,15 +293,10 @@ public static class RoleSettingMenuPatches
|
|||||||
{
|
{
|
||||||
RoleGroupHidden[group] = !groupHidden;
|
RoleGroupHidden[group] = !groupHidden;
|
||||||
}
|
}
|
||||||
foreach (var header in Headers)
|
GameSettingMenuPatches.HideRoleOptions(GameSettingMenuPatches.GetCurrentRoleSettings(), __instance);
|
||||||
{
|
__instance.advancedSettingChildren = null;
|
||||||
header.Destroy();
|
|
||||||
}
|
|
||||||
Headers.Clear();
|
Headers.Clear();
|
||||||
foreach (var option in RoleOptionSettings)
|
__instance.roleChances.Clear();
|
||||||
{
|
|
||||||
option.gameObject.Destroy();
|
|
||||||
}
|
|
||||||
RoleOptionSettings.Clear();
|
RoleOptionSettings.Clear();
|
||||||
__instance.SetQuotaTab();
|
__instance.SetQuotaTab();
|
||||||
}));
|
}));
|
||||||
@@ -301,6 +307,7 @@ public static class RoleSettingMenuPatches
|
|||||||
ScrollerNum -= 0.4f;
|
ScrollerNum -= 0.4f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Utilities.Extensions.ClearGarbageCollector();
|
||||||
|
|
||||||
__instance.scrollBar.SetScrollBounds(__instance);
|
__instance.scrollBar.SetScrollBounds(__instance);
|
||||||
return false;
|
return false;
|
||||||
@@ -415,8 +422,9 @@ public static class RoleSettingMenuPatches
|
|||||||
{
|
{
|
||||||
foreach (var optBehaviour in __instance.AdvancedRolesSettings.GetComponentsInChildren<OptionBehaviour>())
|
foreach (var optBehaviour in __instance.AdvancedRolesSettings.GetComponentsInChildren<OptionBehaviour>())
|
||||||
{
|
{
|
||||||
optBehaviour.gameObject.DestroyImmediate();
|
optBehaviour.gameObject.DeepDestroy(false);
|
||||||
}
|
}
|
||||||
|
Utilities.Extensions.ClearGarbageCollector();
|
||||||
|
|
||||||
CurrentRole = role;
|
CurrentRole = role;
|
||||||
__instance.advancedSettingChildren.Clear();
|
__instance.advancedSettingChildren.Clear();
|
||||||
@@ -611,7 +619,7 @@ public static class RoleSettingMenuPatches
|
|||||||
var newButton = Object.Instantiate(roleOptionSetting.buttons[0], roleOptionSetting.transform);
|
var newButton = Object.Instantiate(roleOptionSetting.buttons[0], roleOptionSetting.transform);
|
||||||
newButton.name = "ConfigButton";
|
newButton.name = "ConfigButton";
|
||||||
newButton.transform.localPosition = new Vector3(0.4473f, -0.3f, -2f);
|
newButton.transform.localPosition = new Vector3(0.4473f, -0.3f, -2f);
|
||||||
newButton.transform.FindChild("Text_TMP").gameObject.DestroyImmediate();
|
newButton.transform.FindChild("Text_TMP").gameObject.DeepDestroy();
|
||||||
newButton.activeSprites.Destroy();
|
newButton.activeSprites.Destroy();
|
||||||
|
|
||||||
var btnRend = newButton.transform.FindChild("ButtonSprite").GetComponent<SpriteRenderer>();
|
var btnRend = newButton.transform.FindChild("ButtonSprite").GetComponent<SpriteRenderer>();
|
||||||
|
|||||||
@@ -117,6 +117,20 @@ internal static class PlayerControlPatches
|
|||||||
__instance.RpcMurderPlayer(target, true);
|
__instance.RpcMurderPlayer(target, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HarmonyPrefix]
|
||||||
|
[HarmonyPatch(nameof(PlayerControl.RpcMurderPlayer))]
|
||||||
|
[HarmonyPatch(nameof(PlayerControl.MurderPlayer))]
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
public static bool MurderPlayerPrefix(PlayerControl __instance)
|
||||||
|
{
|
||||||
|
if (LobbyBehaviour.Instance)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
[HarmonyPostfix]
|
[HarmonyPostfix]
|
||||||
[HarmonyPatch(nameof(PlayerControl.FixedUpdate))]
|
[HarmonyPatch(nameof(PlayerControl.FixedUpdate))]
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
|
|||||||
@@ -13,27 +13,27 @@ internal static class HowToPlayPatch
|
|||||||
// yes i patched the entire method
|
// yes i patched the entire method
|
||||||
private static void Prefix(HowToPlayScene __instance)
|
private static void Prefix(HowToPlayScene __instance)
|
||||||
{
|
{
|
||||||
|
if (RoleManager.Instance.AllRoles.ToArray().All(x => !x.IsCustomRole()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
__instance.sceneIndex = 0;
|
__instance.sceneIndex = 0;
|
||||||
__instance.category = HowToPlayScene.HowToPlayCategory.RolesSelection;
|
__instance.category = HowToPlayScene.HowToPlayCategory.RolesSelection;
|
||||||
__instance.startPage.SetActive(false);
|
__instance.startPage.SetActive(false);
|
||||||
if (__instance.roleButtonsParent.childCount == 0)
|
if (__instance.roleButtonsParent.childCount == 0)
|
||||||
{
|
{
|
||||||
using (IEnumerator<RoleBehaviour> enumerator = RoleManager.Instance.AllRoles.ToArray().Where(x => !x.IsCustomRole()).GetEnumerator())
|
foreach (var role in RoleManager.Instance.AllRoles.ToArray().Where(x => !x.IsCustomRole()))
|
||||||
{
|
{
|
||||||
while (enumerator.MoveNext())
|
if (!role.IsSimpleRole && role.Role != RoleTypes.CrewmateGhost && role.Role != RoleTypes.ImpostorGhost)
|
||||||
{
|
{
|
||||||
RoleBehaviour role = enumerator.Current;
|
HowToPlayRoleButton component = Object.Instantiate(__instance.roleButtonPrefab, __instance.roleButtonsParent).GetComponent<HowToPlayRoleButton>();
|
||||||
if (!role.IsSimpleRole && role.Role != RoleTypes.CrewmateGhost && role.Role != RoleTypes.ImpostorGhost)
|
Sprite roleIcon = __instance.rolesScenes.ToArray().First(r => r.role == role.Role).roleIcon;
|
||||||
|
component.SetRoleInfo(role, roleIcon);
|
||||||
|
component.SetButtonAction((Il2CppSystem.Action)(() =>
|
||||||
{
|
{
|
||||||
HowToPlayRoleButton component = Object.Instantiate(__instance.roleButtonPrefab, __instance.roleButtonsParent).GetComponent<HowToPlayRoleButton>();
|
OpenRolePage(__instance, role.Role);
|
||||||
Sprite roleIcon = __instance.rolesScenes.ToArray().First(r => r.role == role.Role).roleIcon;
|
}));
|
||||||
component.SetRoleInfo(role, roleIcon);
|
__instance.controllerSelectables.Add(component.GetComponent<PassiveButton>());
|
||||||
component.SetButtonAction((Il2CppSystem.Action)(() =>
|
|
||||||
{
|
|
||||||
__instance.OpenRolePage(role.Role);
|
|
||||||
}));
|
|
||||||
__instance.controllerSelectables.Add(component.GetComponent<PassiveButton>());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach (UiElement uiElement in __instance.controllerSelectables)
|
foreach (UiElement uiElement in __instance.controllerSelectables)
|
||||||
@@ -46,4 +46,23 @@ internal static class HowToPlayPatch
|
|||||||
__instance.roleSelectionScene.SetActive(true);
|
__instance.roleSelectionScene.SetActive(true);
|
||||||
ControllerManager.Instance.SetDefaultSelection(__instance.defaultButtonSelected, null);
|
ControllerManager.Instance.SetDefaultSelection(__instance.defaultButtonSelected, null);
|
||||||
}
|
}
|
||||||
|
public static void OpenRolePage(HowToPlayScene instance, RoleTypes roleType)
|
||||||
|
{
|
||||||
|
instance.category = HowToPlayScene.HowToPlayCategory.Roles;
|
||||||
|
var newList = instance.rolesScenes.ToArray().ToList();
|
||||||
|
var buttonList = instance.roleButtons;
|
||||||
|
instance.sceneIndex = newList.FindIndex(r => r.role == roleType);
|
||||||
|
if (roleType != RoleTypes.Crewmate)
|
||||||
|
{
|
||||||
|
foreach (var button in buttonList)
|
||||||
|
{
|
||||||
|
if (button.GetRole().Role == roleType)
|
||||||
|
{
|
||||||
|
instance.previouslySelectedRoleButton = button.GetComponent<PassiveButton>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
instance.SetupDots(instance.rolesScenes[instance.sceneIndex].rolePages.Count);
|
||||||
|
instance.ChangeScene(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using InnerNet;
|
using InnerNet;
|
||||||
using MiraAPI.Roles;
|
using MiraAPI.Roles;
|
||||||
|
using MiraAPI.Utilities;
|
||||||
using Reactor.Utilities.Extensions;
|
using Reactor.Utilities.Extensions;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
namespace MiraAPI.Patches.Roles;
|
namespace MiraAPI.Patches.Roles;
|
||||||
|
|
||||||
@@ -69,9 +71,9 @@ public static class HudManagerPatches
|
|||||||
|
|
||||||
CustomRoleManager.UpdateRoleTab(RoleTab, customRole);
|
CustomRoleManager.UpdateRoleTab(RoleTab, customRole);
|
||||||
}
|
}
|
||||||
else if (RoleTab)
|
else if (RoleTab != null)
|
||||||
{
|
{
|
||||||
RoleTab?.gameObject.Destroy();
|
RoleTab.gameObject.DeepDestroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,7 +58,7 @@ public static class KillButtonPatches
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerControl.LocalPlayer.RpcCustomMurder(__instance.currentTarget);
|
PlayerControl.LocalPlayer.RpcCustomMurder(__instance.currentTarget, MeetingCheck.OutsideMeeting);
|
||||||
__instance.SetTarget(null);
|
__instance.SetTarget(null);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public static class LogicRoleSelectionHnsPatch
|
|||||||
foreach (RoleManager.RoleAssignmentData roleAssignmentData in guaranteedRoles.Select((x) =>
|
foreach (RoleManager.RoleAssignmentData roleAssignmentData in guaranteedRoles.Select((x) =>
|
||||||
new RoleManager.RoleAssignmentData(x, roleOptions.GetNumPerGame(x.Role), 100)))
|
new RoleManager.RoleAssignmentData(x, roleOptions.GetNumPerGame(x.Role), 100)))
|
||||||
{
|
{
|
||||||
for (;;)
|
while (true)
|
||||||
{
|
{
|
||||||
RoleManager.RoleAssignmentData roleAssignmentData2 = roleAssignmentData;
|
RoleManager.RoleAssignmentData roleAssignmentData2 = roleAssignmentData;
|
||||||
int count = roleAssignmentData2.Count;
|
int count = roleAssignmentData2.Count;
|
||||||
@@ -122,7 +122,7 @@ public static class LogicRoleSelectionHnsPatch
|
|||||||
foreach (RoleManager.RoleAssignmentData roleAssignmentData in guaranteedRoles.Select((x) =>
|
foreach (RoleManager.RoleAssignmentData roleAssignmentData in guaranteedRoles.Select((x) =>
|
||||||
new RoleManager.RoleAssignmentData(x, roleOptions.GetNumPerGame(x.Role), 100)))
|
new RoleManager.RoleAssignmentData(x, roleOptions.GetNumPerGame(x.Role), 100)))
|
||||||
{
|
{
|
||||||
for (;;)
|
while (true)
|
||||||
{
|
{
|
||||||
RoleManager.RoleAssignmentData roleAssignmentData2 = roleAssignmentData;
|
RoleManager.RoleAssignmentData roleAssignmentData2 = roleAssignmentData;
|
||||||
int count = roleAssignmentData2.Count;
|
int count = roleAssignmentData2.Count;
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public static class LogicRoleSelectionNormalPatch
|
|||||||
foreach (RoleManager.RoleAssignmentData roleAssignmentData in guaranteedRoles.Select((x) =>
|
foreach (RoleManager.RoleAssignmentData roleAssignmentData in guaranteedRoles.Select((x) =>
|
||||||
new RoleManager.RoleAssignmentData(x, roleOptions.GetNumPerGame(x.Role), 100)))
|
new RoleManager.RoleAssignmentData(x, roleOptions.GetNumPerGame(x.Role), 100)))
|
||||||
{
|
{
|
||||||
for (;;)
|
while (true)
|
||||||
{
|
{
|
||||||
RoleManager.RoleAssignmentData roleAssignmentData2 = roleAssignmentData;
|
RoleManager.RoleAssignmentData roleAssignmentData2 = roleAssignmentData;
|
||||||
int count = roleAssignmentData2.Count;
|
int count = roleAssignmentData2.Count;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using MiraAPI.Events;
|
|||||||
using MiraAPI.Events.Vanilla.Gameplay;
|
using MiraAPI.Events.Vanilla.Gameplay;
|
||||||
using MiraAPI.Modifiers;
|
using MiraAPI.Modifiers;
|
||||||
using MiraAPI.Roles;
|
using MiraAPI.Roles;
|
||||||
|
using MiraAPI.Utilities;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace MiraAPI.Patches.Roles;
|
namespace MiraAPI.Patches.Roles;
|
||||||
@@ -32,7 +33,7 @@ public static class RoleManagerPatches
|
|||||||
if (data.Role)
|
if (data.Role)
|
||||||
{
|
{
|
||||||
data.Role.Deinitialize(targetPlayer);
|
data.Role.Deinitialize(targetPlayer);
|
||||||
Object.Destroy(data.Role.gameObject);
|
data.Role.gameObject.DeepDestroy();
|
||||||
}
|
}
|
||||||
var roleBehaviour = Object.Instantiate<RoleBehaviour>(__instance.AllRoles.ToArray().First(r => r.Role == roleType), data.gameObject.transform);
|
var roleBehaviour = Object.Instantiate<RoleBehaviour>(__instance.AllRoles.ToArray().First(r => r.Role == roleType), data.gameObject.transform);
|
||||||
roleBehaviour.Initialize(targetPlayer);
|
roleBehaviour.Initialize(targetPlayer);
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ internal static class VentCanUsePatch
|
|||||||
var @object = pc.Object;
|
var @object = pc.Object;
|
||||||
var role = @object.Data.Role;
|
var role = @object.Data.Role;
|
||||||
|
|
||||||
var canVent = role is ICustomRole customRole ? customRole.Configuration.CanUseVent : role.CanVent;
|
var canVent = role is ICustomRole customRole ? customRole.Configuration.CanUseVent || customRole.Configuration.GetsVentData : role.CanVent;
|
||||||
couldUse = canVent;
|
couldUse = canVent;
|
||||||
|
|
||||||
var modifiers = @object.GetModifierComponent().ActiveModifiers;
|
var modifiers = @object.GetModifierComponent().ActiveModifiers;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using System.Linq;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using BepInEx.Configuration;
|
using BepInEx.Configuration;
|
||||||
using BepInEx.Unity.IL2CPP;
|
using BepInEx.Unity.IL2CPP;
|
||||||
|
using HarmonyLib;
|
||||||
using MiraAPI.Colors;
|
using MiraAPI.Colors;
|
||||||
using MiraAPI.Events;
|
using MiraAPI.Events;
|
||||||
using MiraAPI.GameEnd;
|
using MiraAPI.GameEnd;
|
||||||
@@ -21,7 +22,6 @@ using MiraAPI.Roles;
|
|||||||
using MiraAPI.Utilities;
|
using MiraAPI.Utilities;
|
||||||
using Reactor.Networking;
|
using Reactor.Networking;
|
||||||
using Reactor.Utilities;
|
using Reactor.Utilities;
|
||||||
using Rewired;
|
|
||||||
|
|
||||||
namespace MiraAPI.PluginLoading;
|
namespace MiraAPI.PluginLoading;
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ public sealed class MiraPluginManager
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var method in type.GetMethods())
|
foreach (var method in AccessTools.GetDeclaredMethods(type))
|
||||||
{
|
{
|
||||||
var eventAttribute = method.GetCustomAttribute<RegisterEventAttribute>();
|
var eventAttribute = method.GetCustomAttribute<RegisterEventAttribute>();
|
||||||
if (eventAttribute == null)
|
if (eventAttribute == null)
|
||||||
|
|||||||
@@ -71,4 +71,26 @@ public class OptionPreset
|
|||||||
|
|
||||||
CustomRoleManager.SyncAllRoleSettings();
|
CustomRoleManager.SyncAllRoleSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resets the specified option with the preset provided.
|
||||||
|
/// </summary>
|
||||||
|
public void ResetOption(OptionBehaviour baseOption)
|
||||||
|
{
|
||||||
|
var selectedOpt = Plugin.InternalOptions.First(x => x.OptionBehaviour == baseOption);
|
||||||
|
selectedOpt.LoadFromPreset(PresetConfig);
|
||||||
|
/*ModdedOptionsManager.SyncAllOptions();
|
||||||
|
|
||||||
|
CustomRoleManager.SyncAllRoleSettings();*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets whether the specified option is included in the preset.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>>The value of whether the option is in the preset.</returns>
|
||||||
|
public bool IsOptionInPreset(OptionBehaviour baseOption)
|
||||||
|
{
|
||||||
|
var selectedOpt = Plugin.InternalOptions.First(x => x.OptionBehaviour == baseOption);
|
||||||
|
return selectedOpt.ConfigDefinition != null && PresetConfig.ContainsKey(selectedOpt.ConfigDefinition);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ using System.Linq;
|
|||||||
using BepInEx.Configuration;
|
using BepInEx.Configuration;
|
||||||
using MiraAPI.PluginLoading;
|
using MiraAPI.PluginLoading;
|
||||||
using MiraAPI.Roles;
|
using MiraAPI.Roles;
|
||||||
|
using MiraAPI.Utilities;
|
||||||
using Reactor.Utilities;
|
using Reactor.Utilities;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
@@ -19,6 +20,7 @@ public static class PresetManager
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static string PresetDirectory { get; } = Path.GetFullPath("mira_presets", Application.persistentDataPath);
|
public static string PresetDirectory { get; } = Path.GetFullPath("mira_presets", Application.persistentDataPath);
|
||||||
|
|
||||||
|
internal static Dictionary<MiraPluginInfo, OptionPreset> DefaultPresets { get; } = [];
|
||||||
internal static List<OptionPreset> InternalMasterPresets { get; } = [];
|
internal static List<OptionPreset> InternalMasterPresets { get; } = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -47,6 +49,7 @@ public static class PresetManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
presetConfig.Save();
|
presetConfig.Save();
|
||||||
|
DefaultPresets.Add(plugin, new OptionPreset("Default", plugin, presetConfig));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -59,9 +62,10 @@ public static class PresetManager
|
|||||||
{
|
{
|
||||||
if (btn != null)
|
if (btn != null)
|
||||||
{
|
{
|
||||||
Object.DestroyImmediate(btn);
|
btn.DeepDestroy(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Utilities.Extensions.ClearGarbageCollector();
|
||||||
|
|
||||||
plugin.InternalPresets.Clear();
|
plugin.InternalPresets.Clear();
|
||||||
if (!Directory.Exists(PresetDirectory))
|
if (!Directory.Exists(PresetDirectory))
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public class SavePresetPopup(nint cppPtr) : Minigame(cppPtr)
|
|||||||
// Cleanup holder object
|
// Cleanup holder object
|
||||||
public void OnDestroy()
|
public void OnDestroy()
|
||||||
{
|
{
|
||||||
transform.parent.gameObject.Destroy();
|
transform.parent.gameObject.DeepDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Close()
|
public override void Close()
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
@@ -1,5 +1,6 @@
|
|||||||
using AmongUs.GameOptions;
|
using AmongUs.GameOptions;
|
||||||
using System;
|
using System;
|
||||||
|
using Il2CppInterop.Runtime.Attributes;
|
||||||
using MiraAPI.Patches.Freeplay;
|
using MiraAPI.Patches.Freeplay;
|
||||||
using MiraAPI.Utilities.Assets;
|
using MiraAPI.Utilities.Assets;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
@@ -77,16 +78,19 @@ public record struct CustomRoleConfiguration
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the Sprite used for the Role Options menu screenshot.
|
/// Gets or sets the Sprite used for the Role Options menu screenshot.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[HideFromIl2Cpp]
|
||||||
public LoadableAsset<Sprite>? OptionsScreenshot { get; set; } = null;
|
public LoadableAsset<Sprite>? OptionsScreenshot { get; set; } = null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the Sprite used for the Role Icon.
|
/// Gets or sets the Sprite used for the Role Icon.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[HideFromIl2Cpp]
|
||||||
public LoadableAsset<Sprite>? Icon { get; set; } = null;
|
public LoadableAsset<Sprite>? Icon { get; set; } = null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the Intro sound for the Role.
|
/// Gets or sets the Intro sound for the Role.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[HideFromIl2Cpp]
|
||||||
public LoadableAsset<AudioClip>? IntroSound { get; set; } = null;
|
public LoadableAsset<AudioClip>? IntroSound { get; set; } = null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -105,10 +109,15 @@ public record struct CustomRoleConfiguration
|
|||||||
public bool UseVanillaKillButton { get; set; }
|
public bool UseVanillaKillButton { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether the role can use vents.
|
/// Gets or sets a value indicating whether the role can use vents. Also sends vanilla vent data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool CanUseVent { get; set; }
|
public bool CanUseVent { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the role can receive vanilla vent data.
|
||||||
|
/// </summary>
|
||||||
|
public bool GetsVentData { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether the role can use the sabotage button.
|
/// Gets or sets a value indicating whether the role can use the sabotage button.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -89,15 +89,22 @@ public static class CustomRoleManager
|
|||||||
|
|
||||||
foreach (var roleType in roles)
|
foreach (var roleType in roles)
|
||||||
{
|
{
|
||||||
ClassInjector.RegisterTypeInIl2Cpp(roleType);
|
try
|
||||||
|
|
||||||
var role = RegisterRole(roleType, pluginInfo);
|
|
||||||
if (role is null)
|
|
||||||
{
|
{
|
||||||
continue;
|
ClassInjector.RegisterTypeInIl2Cpp(roleType);
|
||||||
}
|
|
||||||
|
|
||||||
pluginInfo.InternalRoles.Add((ushort)role.Role, role);
|
var role = RegisterRole(roleType, pluginInfo);
|
||||||
|
if (role is null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginInfo.InternalRoles.Add((ushort)role.Role, role);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Error($"Failed to register role type {roleType.Name}: {ex}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pluginInfo.PluginConfig.Save();
|
pluginInfo.PluginConfig.Save();
|
||||||
@@ -116,7 +123,7 @@ public static class CustomRoleManager
|
|||||||
|
|
||||||
if (roleBehaviour is not ICustomRole customRole)
|
if (roleBehaviour is not ICustomRole customRole)
|
||||||
{
|
{
|
||||||
roleBehaviour.gameObject.Destroy();
|
roleBehaviour.gameObject.DeepDestroy();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,7 +139,7 @@ public static class CustomRoleManager
|
|||||||
roleBehaviour.CanBeKilled = customRole.Configuration.CanGetKilled;
|
roleBehaviour.CanBeKilled = customRole.Configuration.CanGetKilled;
|
||||||
roleBehaviour.CanUseKillButton = customRole.Configuration.UseVanillaKillButton;
|
roleBehaviour.CanUseKillButton = customRole.Configuration.UseVanillaKillButton;
|
||||||
roleBehaviour.TasksCountTowardProgress = customRole.Configuration.TasksCountForProgress;
|
roleBehaviour.TasksCountTowardProgress = customRole.Configuration.TasksCountForProgress;
|
||||||
roleBehaviour.CanVent = customRole.Configuration.CanUseVent;
|
roleBehaviour.CanVent = customRole.Configuration.CanUseVent || customRole.Configuration.GetsVentData;
|
||||||
roleBehaviour.DefaultGhostRole = customRole.Configuration.GhostRole;
|
roleBehaviour.DefaultGhostRole = customRole.Configuration.GhostRole;
|
||||||
roleBehaviour.MaxCount = customRole.Configuration.MaxRoleCount;
|
roleBehaviour.MaxCount = customRole.Configuration.MaxRoleCount;
|
||||||
roleBehaviour.RoleScreenshot = customRole.Configuration.OptionsScreenshot?.LoadAsset();
|
roleBehaviour.RoleScreenshot = customRole.Configuration.OptionsScreenshot?.LoadAsset();
|
||||||
|
|||||||
@@ -24,6 +24,26 @@ public static class AddressablesLoader
|
|||||||
private static readonly List<(string, string)> RegisteredNameplateKeys = [];
|
private static readonly List<(string, string)> RegisteredNameplateKeys = [];
|
||||||
private static readonly List<string> RegisteredSkinKeys = [];
|
private static readonly List<string> RegisteredSkinKeys = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether hats have been loaded by the addressables system.
|
||||||
|
/// </summary>
|
||||||
|
public static bool AddressableHatsExist { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether visors have been loaded by the addressables system.
|
||||||
|
/// </summary>
|
||||||
|
public static bool AddressableVisorsExist { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether nameplates have been loaded by the addressables system.
|
||||||
|
/// </summary>
|
||||||
|
public static bool AddressableNameplatesExist { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether skins have been loaded by the addressables system.
|
||||||
|
/// </summary>
|
||||||
|
public static bool AddressableSkinsExist { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers a specific addressables package to load asynchronously at the start of the game, when possible.
|
/// Registers a specific addressables package to load asynchronously at the start of the game, when possible.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -52,6 +72,7 @@ public static class AddressablesLoader
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AddressableHatsExist = true;
|
||||||
RegisteredHatKeys.Add(addressablesKey);
|
RegisteredHatKeys.Add(addressablesKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,6 +88,7 @@ public static class AddressablesLoader
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AddressableSkinsExist = true;
|
||||||
RegisteredSkinKeys.Add(addressablesKey);
|
RegisteredSkinKeys.Add(addressablesKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,6 +105,7 @@ public static class AddressablesLoader
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AddressableNameplatesExist = true;
|
||||||
RegisteredNameplateKeys.Add((addressablesKey, groupTitle));
|
RegisteredNameplateKeys.Add((addressablesKey, groupTitle));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,6 +122,7 @@ public static class AddressablesLoader
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AddressableVisorsExist = true;
|
||||||
RegisteredVisorKeys.Add((addressablesKey, groupTitle));
|
RegisteredVisorKeys.Add((addressablesKey, groupTitle));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -90,6 +90,11 @@ public static class MiraAssets
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static LoadableResourceAsset CheckmarkBox { get; } = new("MiraAPI.Resources.CheckMarkBox.png");
|
public static LoadableResourceAsset CheckmarkBox { get; } = new("MiraAPI.Resources.CheckMarkBox.png");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the Reset Box used in the Settings Menu.
|
||||||
|
/// </summary>
|
||||||
|
public static LoadableResourceAsset ResetButton { get; } = new("MiraAPI.Resources.ResetButton.png");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the Checkmark used in the Settings Menu.
|
/// Gets the Checkmark used in the Settings Menu.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -53,7 +54,7 @@ public static class Extensions
|
|||||||
bool isComplete;
|
bool isComplete;
|
||||||
if (self.amClosing == Minigame.CloseState.Closing)
|
if (self.amClosing == Minigame.CloseState.Closing)
|
||||||
{
|
{
|
||||||
UnityEngine.Object.Destroy(self.gameObject);
|
self.gameObject.DeepDestroy();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (self.CloseSound && Constants.ShouldPlaySfx())
|
if (self.CloseSound && Constants.ShouldPlaySfx())
|
||||||
@@ -132,6 +133,31 @@ public static class Extensions
|
|||||||
button.SetCooldownFill(num);
|
button.SetCooldownFill(num);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a random element from the specified sequence.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">
|
||||||
|
/// The sequence to select an element from.
|
||||||
|
/// </param>
|
||||||
|
/// <typeparam name="T">
|
||||||
|
/// The type of elements in the sequence.
|
||||||
|
/// </typeparam>
|
||||||
|
/// <returns>
|
||||||
|
/// A randomly selected element from <paramref name="input"/>.
|
||||||
|
/// </returns>
|
||||||
|
/// <exception cref="InvalidOperationException">
|
||||||
|
/// Thrown when <paramref name="input"/> is empty.
|
||||||
|
/// </exception>
|
||||||
|
public static T RandomSnapshot<T>(this IEnumerable<T> input)
|
||||||
|
{
|
||||||
|
var list = input.ToList();
|
||||||
|
|
||||||
|
if (list.Count == 0)
|
||||||
|
throw new InvalidOperationException("Cannot get random element from an empty collection.");
|
||||||
|
|
||||||
|
return list[UnityEngine.Random.Range(0, list.Count)];
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a PlayerControl from their PlayerVoteArea in a meeting.
|
/// Gets a PlayerControl from their PlayerVoteArea in a meeting.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -156,6 +182,16 @@ public static class Extensions
|
|||||||
return TutorialManager.InstanceExists || AmongUsClient.Instance.HostId == playerControl.OwnerId;
|
return TutorialManager.InstanceExists || AmongUsClient.Instance.HostId == playerControl.OwnerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if a PlayerControl is protected by a Guardian Angel.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="playerControl">The player you're checking for.</param>
|
||||||
|
/// <returns>If the player is protected, true, else false.</returns>
|
||||||
|
public static bool ProtectedByGa(this PlayerControl playerControl)
|
||||||
|
{
|
||||||
|
return playerControl.protectedByGuardianId > -1;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used to convert a System.Collections.Generic.List to Il2cppSystem.
|
/// Used to convert a System.Collections.Generic.List to Il2cppSystem.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -184,6 +220,121 @@ public static class Extensions
|
|||||||
return Mathf.Approximately(number, Mathf.Round(number));
|
return Mathf.Approximately(number, Mathf.Round(number));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Destroys the object properly.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to destroy.</param>
|
||||||
|
/// <param name="clearGc">Whether to run the garbage collector immediately.</param>
|
||||||
|
public static void DeepDestroy(this GameObject? obj, bool clearGc = true)
|
||||||
|
{
|
||||||
|
Coroutines.Start(Nuke(obj, clearGc));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerator Nuke(GameObject? go, bool clearGc)
|
||||||
|
{
|
||||||
|
if (go == null)
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
go.transform.SetParent(null, false);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
go.SetActive(false);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var mb in go.GetComponentsInChildren<MonoBehaviour>(true))
|
||||||
|
{
|
||||||
|
if (mb == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mb.StopAllCoroutines();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mb.enabled = false;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var renderer in go.GetComponentsInChildren<Renderer>(true))
|
||||||
|
{
|
||||||
|
if (renderer == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var mat in renderer.materials)
|
||||||
|
{
|
||||||
|
if (mat != null)
|
||||||
|
UnityEngine.Object.Destroy(mat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var filter in go.GetComponentsInChildren<MeshFilter>(true))
|
||||||
|
{
|
||||||
|
if (filter == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var mesh = filter.mesh;
|
||||||
|
if (mesh != null)
|
||||||
|
UnityEngine.Object.Destroy(mesh);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UnityEngine.Object.Destroy(go);
|
||||||
|
yield return null;
|
||||||
|
if (clearGc)
|
||||||
|
{
|
||||||
|
yield return CoFreeResources();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ClearGarbageCollector()
|
||||||
|
{
|
||||||
|
Coroutines.Start(CoFreeResources());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerator CoFreeResources()
|
||||||
|
{
|
||||||
|
yield return Resources.UnloadUnusedAssets();
|
||||||
|
|
||||||
|
GC.Collect(0, GCCollectionMode.Forced, blocking: true);
|
||||||
|
GC.WaitForPendingFinalizers();
|
||||||
|
GC.Collect(0, GCCollectionMode.Forced, blocking: true);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a cache of player's vote data components to improve performance.
|
/// Gets a cache of player's vote data components to improve performance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -539,17 +539,13 @@ public static class Helpers
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns whether the role is blacklisted from appearing and spawning. (Only applicable to vanilla roles)
|
/// Returns whether the role is blacklisted from appearing and spawning. Only applicable to vanilla roles.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="role">The role to check.</param>
|
/// <param name="role">The role to check.</param>
|
||||||
/// <returns>The role's blacklist status.</returns>
|
/// <returns>The role's blacklist status.</returns>
|
||||||
public static bool IsRoleBlacklisted(this RoleBehaviour role)
|
public static bool IsRoleBlacklisted(this RoleBehaviour role)
|
||||||
{
|
{
|
||||||
// This should be patchable by mods when a vanilla role is meant to be replaced by a custom role.
|
// This should be patchable by mods when a vanilla role is meant to be replaced by a custom role.
|
||||||
if (role.Role is RoleTypes.Engineer)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using Il2CppInterop.Runtime.InteropTypes;
|
using Il2CppInterop.Runtime.InteropTypes;
|
||||||
|
using Object = Il2CppSystem.Object;
|
||||||
|
|
||||||
namespace MiraAPI.Utilities;
|
namespace MiraAPI.Utilities;
|
||||||
|
|
||||||
@@ -17,6 +18,7 @@ public class StateMachineWrapper<T> where T : Il2CppObjectBase
|
|||||||
// normally it is fields, but IL2CPP turns them into properties
|
// normally it is fields, but IL2CPP turns them into properties
|
||||||
private readonly PropertyInfo _thisProperty;
|
private readonly PropertyInfo _thisProperty;
|
||||||
private readonly PropertyInfo _stateProperty;
|
private readonly PropertyInfo _stateProperty;
|
||||||
|
private readonly PropertyInfo _currentProperty;
|
||||||
private readonly Dictionary<string, PropertyInfo> _propertyCache;
|
private readonly Dictionary<string, PropertyInfo> _propertyCache;
|
||||||
|
|
||||||
private T? _parentInstance;
|
private T? _parentInstance;
|
||||||
@@ -37,8 +39,9 @@ public class StateMachineWrapper<T> where T : Il2CppObjectBase
|
|||||||
var type = _stateMachine.GetType();
|
var type = _stateMachine.GetType();
|
||||||
_thisProperty = AccessTools.Property(type, "__4__this");
|
_thisProperty = AccessTools.Property(type, "__4__this");
|
||||||
_stateProperty = AccessTools.Property(type, "__1__state");
|
_stateProperty = AccessTools.Property(type, "__1__state");
|
||||||
|
_currentProperty = AccessTools.Property(type, "__2__current");
|
||||||
|
|
||||||
if (_thisProperty == null || _stateProperty == null)
|
if (_thisProperty == null || _stateProperty == null || _currentProperty == null)
|
||||||
{
|
{
|
||||||
throw new MissingMemberException($"Could not find required properties in type '{type}'.");
|
throw new MissingMemberException($"Could not find required properties in type '{type}'.");
|
||||||
}
|
}
|
||||||
@@ -52,6 +55,18 @@ public class StateMachineWrapper<T> where T : Il2CppObjectBase
|
|||||||
/// <returns>The current state as an integer.</returns>
|
/// <returns>The current state as an integer.</returns>
|
||||||
public int GetState() => (int)_stateProperty.GetValue(_stateMachine)!;
|
public int GetState() => (int)_stateProperty.GetValue(_stateMachine)!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the current state of the state machine.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="newState">The new state to use.</param>
|
||||||
|
public void SetState(int newState) => _stateProperty.SetValue(_stateMachine, newState);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the newest yield return of the state machine.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="newReturn">The new return to use.</param>
|
||||||
|
public void SetRecentReturn(Object newReturn) => _currentProperty.SetValue(_stateMachine, newReturn);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a parameter from the state machine by its name.
|
/// Gets a parameter from the state machine by its name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
# Mira API
|
# Mira API
|
||||||
|
|
||||||
A thorough, but simple, Among Us modding API and utility library that covers:
|
A thorough, but simple, Among Us modding API and utility library that covers:
|
||||||
|
|
||||||
- Roles
|
- Roles
|
||||||
- Options
|
- Options
|
||||||
- Modifiers
|
- Modifiers
|
||||||
@@ -13,6 +14,9 @@ A thorough, but simple, Among Us modding API and utility library that covers:
|
|||||||
- Events
|
- Events
|
||||||
- Voting
|
- Voting
|
||||||
- Assets
|
- Assets
|
||||||
|
- Keybinds
|
||||||
|
- Local Settings
|
||||||
|
- Custom Game Over Conditions
|
||||||
- Compatibility
|
- Compatibility
|
||||||
- ~~Game Modes~~ (coming soon)
|
- ~~Game Modes~~ (coming soon)
|
||||||
|
|
||||||
@@ -24,9 +28,10 @@ The result is a less intrusive, better modding API that covers general use cases
|
|||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
To start using Mira API, you need to:
|
To start using Mira API, you need to:
|
||||||
|
|
||||||
1. Add a reference to Mira API either through a [DLL](https://github.com/All-Of-Us-Mods/MiraAPI/releases), project reference, or [NuGet package](https://www.nuget.org/packages/AllOfUs.MiraAPI).
|
1. Add a reference to Mira API either through a [DLL](https://github.com/All-Of-Us-Mods/MiraAPI/releases), project reference, or [NuGet package](https://www.nuget.org/packages/AllOfUs.MiraAPI).
|
||||||
2. Add a BepInDependency on your plugin class like this: `[BepInDependency(MiraApiPlugin.Id)]`
|
2. Add a BepInDependency on your plugin class like this: `[BepInDependency(MiraApiPlugin.Id)]`
|
||||||
3. Implement the IMiraPlugin interface in your plugin class.
|
3. Implement the `IMiraPlugin` interface on your plugin class.
|
||||||
|
|
||||||
Mira API also depends on [Reactor](https://github.com/NuclearPowered/Reactor) to function properly!
|
Mira API also depends on [Reactor](https://github.com/NuclearPowered/Reactor) to function properly!
|
||||||
Remember to include it as a reference and `BepInDependency`!
|
Remember to include it as a reference and `BepInDependency`!
|
||||||
@@ -34,8 +39,10 @@ Remember to include it as a reference and `BepInDependency`!
|
|||||||
For a full example, see [this file](https://github.com/All-Of-Us-Mods/MiraAPI/blob/master/MiraAPI.Example/ExamplePlugin.cs).
|
For a full example, see [this file](https://github.com/All-Of-Us-Mods/MiraAPI/blob/master/MiraAPI.Example/ExamplePlugin.cs).
|
||||||
|
|
||||||
## Recommended Project Structure
|
## Recommended Project Structure
|
||||||
|
|
||||||
It is highly recommended to follow this project structure when using Mira API to keep your code clean and organized.
|
It is highly recommended to follow this project structure when using Mira API to keep your code clean and organized.
|
||||||
You can also view the Example Mod in this repository for some guidance.
|
You can also view the Example Mod in this repository for some guidance.
|
||||||
|
|
||||||
```
|
```
|
||||||
MyMiraMod/
|
MyMiraMod/
|
||||||
├── Buttons/
|
├── Buttons/
|
||||||
@@ -60,220 +67,21 @@ MyMiraMod/
|
|||||||
└── MyModAssets.cs
|
└── MyModAssets.cs
|
||||||
```
|
```
|
||||||
|
|
||||||
## Roles
|
# Documentation
|
||||||
Roles are straightforward in Mira API. There are two things you need to do to create a custom role:
|
|
||||||
1. Create a class that inherits from a base game role (like `CrewmateRole`, `ImpostorRole`, etc.)
|
|
||||||
2. Implement the `ICustomRole` interface from Mira API.
|
|
||||||
|
|
||||||
**Disclaimer: Make sure your plugin class has the following attribute `[ReactorModFlags(ModFlags.RequireOnAllClients)]` or else your roles will not register correctly.**
|
Full documentation for every Mira API feature is available on the **[wiki](https://github.com/All-Of-Us-Mods/MiraAPI/wiki)**:
|
||||||
|
|
||||||
Note: For step 1, if you are making neutral roles,
|
- [Get Started / IMiraPlugin](https://github.com/All-Of-Us-Mods/MiraAPI/wiki/Home)
|
||||||
you can choose either `CrewmateRole` or `ImpostorRole`,
|
- [Custom Roles](https://github.com/All-Of-Us-Mods/MiraAPI/wiki/Custom-Roles)
|
||||||
or even `RoleBehaviour` if you are okay with starting from scratch.
|
- [Options](https://github.com/All-Of-Us-Mods/MiraAPI/wiki/Options)
|
||||||
|
- [Modifiers](https://github.com/All-Of-Us-Mods/MiraAPI/wiki/Modifiers)
|
||||||
Mira API handles everything else, from adding the proper options to the settings menu,
|
- [Custom Buttons](https://github.com/All-Of-Us-Mods/MiraAPI/wiki/Custom-Buttons)
|
||||||
to managing the role assignment at the start of the game.
|
- [Custom Colors](https://github.com/All-Of-Us-Mods/MiraAPI/wiki/Colors)
|
||||||
There are no extra steps on the developer's part.
|
- [Events](https://github.com/All-Of-Us-Mods/MiraAPI/wiki/Events)
|
||||||
|
- [Meetings and Voting](https://github.com/All-Of-Us-Mods/MiraAPI/wiki/Meetings-and-Voting)
|
||||||
See [this file](https://github.com/All-Of-Us-Mods/MiraAPI/blob/master/MiraAPI.Example/Roles/CustomRole.cs) for a code example.
|
- [Assets](https://github.com/All-Of-Us-Mods/MiraAPI/wiki/Assets)
|
||||||
|
- [Keybinds](https://github.com/All-Of-Us-Mods/MiraAPI/wiki/Keybinds)
|
||||||
## Modifiers
|
- [Local Settings](https://github.com/All-Of-Us-Mods/MiraAPI/wiki/Local-Settings)
|
||||||
Mira API uses a different definition of 'modifiers' than other Among Us mods.
|
- [Game Modes](https://github.com/All-Of-Us-Mods/MiraAPI/wiki/Game-Modes)
|
||||||
For example, in Town Of Us, a modifier is an extra "ability" that is applied on top of the base role.
|
- [Custom Game Over](https://github.com/All-Of-Us-Mods/MiraAPI/wiki/Game-Over)
|
||||||
However, in Mira API, modifiers are very flexible.
|
- [Utilities](https://github.com/All-Of-Us-Mods/MiraAPI/wiki/Utilities)
|
||||||
A modifier is anything that "modifiers" a player's abilities or interactions.
|
|
||||||
|
|
||||||
Mira provides three classes for working with modifiers:
|
|
||||||
- `BaseModifier`: The base for every modifier. You MUST add and remove this modifier from a player manually!
|
|
||||||
- `TimedModifier`: A modifier that has a time limit. This modifier has to be added manually, but will automatically remove itself after the time limit.
|
|
||||||
- `GameModifier`: This works like the typical TOU modifier, where it is automatically applied at the beginning of the game, then removed at the end.
|
|
||||||
|
|
||||||
Modifiers provide various overridable functions and properties for custom behavior.
|
|
||||||
They can also be used for "tagging" a player.
|
|
||||||
You can check if a player has a modifier through the extension method `HasModifier` on a `PlayerControl` object.
|
|
||||||
|
|
||||||
To start using a modifier, pick one of the base classes above and create a class that inherits from it. Implement the properties and methods you would like, then add the `[RegisterModifier]` attribute to the class.
|
|
||||||
|
|
||||||
An example Game modifier can be found [here](https://github.com/All-Of-Us-Mods/MiraAPI/blob/master/MiraAPI.Example/Modifiers/GameModifierExample.cs).
|
|
||||||
An example Timer modifier can be found [here](https://github.com/All-Of-Us-Mods/MiraAPI/blob/master/MiraAPI.Example/Modifiers/ModifierTimerExample.cs).
|
|
||||||
|
|
||||||
## Options
|
|
||||||
Options are also simple in Mira API.
|
|
||||||
Mira API handles all the hard work behind the scenes,
|
|
||||||
so developers only have to follow a few steps to create their custom options.
|
|
||||||
The Options API is split up into Groups and Options.
|
|
||||||
Every Option needs to be in a Group.
|
|
||||||
|
|
||||||
To create a group, you need to create a class that inherits from the `AbstractOptionGroup` abstract class.
|
|
||||||
Groups contain some properties for developers to control,
|
|
||||||
including `GroupName`, `GroupColor`, `GroupVisible`, and `OptionableType`.
|
|
||||||
The full list of properties can be found in the `AbstractOptionGroup` class.
|
|
||||||
Only the `GroupName` is required.
|
|
||||||
|
|
||||||
Here is an example of a group class:
|
|
||||||
```csharp
|
|
||||||
public class MyOptionsGroup : AbstractOptionGroup
|
|
||||||
{
|
|
||||||
public override string GroupName => "My Options"; // this is required
|
|
||||||
|
|
||||||
[ModdedNumberOption("My Number Option", min: 0, max: 10)]
|
|
||||||
public float MyNumberOption { get; set; } = 5f;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
You can access any group class using the `OptionGroupSingleton` class like this:
|
|
||||||
```csharp
|
|
||||||
// MyOptionsGroup is a class that inherits from AbstractOptionGroup
|
|
||||||
var myGroup = OptionGroupSingleton<MyOptionsGroup>.Instance; // gets the instance of the group
|
|
||||||
Logger<MyPlugin>.Info(myGroup.MyNumberOption); // prints the value of the option to the console
|
|
||||||
```
|
|
||||||
|
|
||||||
Once you have an options group, there are two ways to make the actual options:
|
|
||||||
- Use an Option Attribute with a property.
|
|
||||||
- Create a ModdedOption property.
|
|
||||||
|
|
||||||
### Option Attributes
|
|
||||||
|
|
||||||
This is an example of using an Option Attribute on a property:
|
|
||||||
```csharp
|
|
||||||
// The first parameter is always the name of the option. The rest are dependent on the type of option.
|
|
||||||
[ModdedNumberOption("Sussy level", min: 0, max: 10)]
|
|
||||||
public float SussyLevel { get; set; } = 4f; // You can set a default value here.
|
|
||||||
```
|
|
||||||
|
|
||||||
Here are the available Option Attributes and their signatures:
|
|
||||||
```csharp
|
|
||||||
ModdedEnumOption(string name, Type enumType, string[]? values = null, Type? roleType = null)
|
|
||||||
|
|
||||||
ModdedNumberOption(
|
|
||||||
string name,
|
|
||||||
float min,
|
|
||||||
float max,
|
|
||||||
float increment=1
|
|
||||||
NumberSuffixes suffixType = NumberSuffixes.None,
|
|
||||||
bool zeroInfinity = false,
|
|
||||||
Type? roleType = null)
|
|
||||||
|
|
||||||
ModdedToggleOption(string name, Type? roleType = null)
|
|
||||||
```
|
|
||||||
|
|
||||||
### ModdedOption Properties
|
|
||||||
|
|
||||||
And this is an example of a ModdedOption property:
|
|
||||||
```csharp
|
|
||||||
public ModdedToggleOption YeezusAbility { get; } = new ModdedToggleOption("Yeezus Ability", false);
|
|
||||||
```
|
|
||||||
|
|
||||||
There are currently three types of ModdedOptions you can create:
|
|
||||||
- `ModdedEnumOption`
|
|
||||||
- `ModdedNumberOption`
|
|
||||||
- `ModdedToggleOption`
|
|
||||||
|
|
||||||
To see a full example of an option class,
|
|
||||||
see [this file](https://github.com/All-Of-Us-Mods/MiraAPI/blob/master/MiraAPI.Example/Options/ExampleOptions.cs).
|
|
||||||
|
|
||||||
### Role Options
|
|
||||||
|
|
||||||
You can also specify a role type for an option or option group.
|
|
||||||
|
|
||||||
To set the role type for an entire group, set the `OptionableType` property on that group like this:
|
|
||||||
```csharp
|
|
||||||
public class MyOptionsGroup : AbstractOptionGroup
|
|
||||||
{
|
|
||||||
public override string GroupName => "My Options";
|
|
||||||
public override Type? OptionableType => typeof(MyRole); // this is the role that will have these options
|
|
||||||
|
|
||||||
[ModdedNumberOption("Ability Uses", min: 0, max: 10)]
|
|
||||||
public float AbilityUses { get; set; } = 5f;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also use a modifier type for the `OptionableType` property.
|
|
||||||
This will associate that option group with the specified modifier type
|
|
||||||
and display it in the modifier options menu.
|
|
||||||
|
|
||||||
An example can be found [here](https://github.com/All-Of-Us-Mods/MiraAPI/blob/master/MiraAPI.Example/Options/Roles/CustomRoleSettings.cs).
|
|
||||||
|
|
||||||
## Custom Murders
|
|
||||||
Mira API provides its own implementation for murders.
|
|
||||||
Our implementation allows for more customization on kills, and helps bypass server checks.
|
|
||||||
You can use `PlayerControl.RpcCustomMurder` to perform a networked custom murder, or `PlayerControl.CustomMurder` to normally perform a custom murder.
|
|
||||||
For example:
|
|
||||||
```cs
|
|
||||||
PlayerControl.LocalPlayer.RpcCustomMurder(Target, createDeadBody: false, teleportMurderer: false, playKillSound: false, resetKillTimer: false, showKillAnim: false);
|
|
||||||
```
|
|
||||||
This will kill a player without creating a dead body and without teleporting the murderer.
|
|
||||||
|
|
||||||
## Buttons
|
|
||||||
|
|
||||||
Mira API provides a simple interface for adding ability buttons to the game.
|
|
||||||
|
|
||||||
The only thing you need to do is create a class
|
|
||||||
that inherits from the `CustomActionButton` class and implement the properties and methods.
|
|
||||||
|
|
||||||
Mira API handles all other tasks and logic required to add the button to the game.
|
|
||||||
|
|
||||||
In case you need to access your `CustomActionButton` instance from another class,
|
|
||||||
you can use the `CustomButtonSingleton` class like this:
|
|
||||||
```csharp
|
|
||||||
var myButton = CustomButtonSingleton<MyCoolButton>.Instance;
|
|
||||||
```
|
|
||||||
|
|
||||||
The button API is straightforward, but provides a lot of flexibility.
|
|
||||||
There are various methods you can override to customize the behavior of your button.
|
|
||||||
See [this file](https://github.com/All-Of-Us-Mods/MiraAPI/blob/master/MiraAPI/Hud/CustomActionButton.cs)
|
|
||||||
for a full list of methods you can override.
|
|
||||||
|
|
||||||
An example button can be found [here](https://github.com/All-Of-Us-Mods/MiraAPI/blob/master/MiraAPI.Example/Buttons/ExampleButton.cs).
|
|
||||||
|
|
||||||
## Custom Colors
|
|
||||||
|
|
||||||
Mira provides a simple Custom Color API that allows you to add custom player colors to the game.
|
|
||||||
|
|
||||||
Creating custom colors isn't difficult, but there are some requirements for Mira to register your colors.
|
|
||||||
|
|
||||||
1. Create a ***STATIC*** class to house all your `CustomColor` objects.
|
|
||||||
2. Inside this class, create a `CustomColor` property for each color you intend to add.
|
|
||||||
3. Add the `[RegisterCustomColors]` attribute to the class.
|
|
||||||
|
|
||||||
Here is an example of a custom color class:
|
|
||||||
```csharp
|
|
||||||
[RegisterCustomColors]
|
|
||||||
public static class MyCustomColors
|
|
||||||
{
|
|
||||||
public static CustomColor Cerulean { get; } = new CustomColor("Cerulean", new Color(0.0f, 0.48f, 0.65f));
|
|
||||||
|
|
||||||
public static CustomColor Rose { get; } = new CustomColor("Rose", new Color(0.98f, 0.26f, 0.62f));
|
|
||||||
|
|
||||||
public static CustomColor Gold { get; } = new CustomColor("Gold", new Color(1.0f, 0.84f, 0.0f));
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Assets
|
|
||||||
|
|
||||||
Mira API provides a simple, but expandable asset system.
|
|
||||||
The core of the system is the `LoadableAsset<T>` class.
|
|
||||||
This is a generic abstract class that provides a pattern for loading assets.
|
|
||||||
|
|
||||||
Mira API comes with a few asset loaders:
|
|
||||||
1. `LoadableBundleAsset<T>`: This is used for loading assets from AssetBundles.
|
|
||||||
2. `LoadableAddressableAsset<T>`: This is used for loading assets from Addressables.
|
|
||||||
3. `LoadableResourceAsset`: This is used for loading **only sprites** from the Embedded Resources within a mod.
|
|
||||||
4. `LoadableAudioResourceAsset`: This is used for loading **only audio clips** from the Embedded Resources within a mod.
|
|
||||||
|
|
||||||
The code below shows how to use an asset loaders:
|
|
||||||
```csharp
|
|
||||||
// Load a sprite from an AssetBundle
|
|
||||||
AssetBundle bundle = AssetBundleManager.Load("MyBundle"); // AssetBundleManager is a utility provided by Reactor
|
|
||||||
LoadableAsset<Sprite> mySpriteAsset = new LoadableBundleAsset<Sprite>("MySprite", bundle);
|
|
||||||
Sprite sprite = mySpriteAsset.LoadAsset();
|
|
||||||
|
|
||||||
// Load a sprite from an Embedded Resource
|
|
||||||
// Make sure to set the Build Action of your image to Embedded Resource!
|
|
||||||
LoadableAsset<Sprite> buttonAsset = new LoadableResourceAsset("ExampleMod.Resources.MyButton.png");
|
|
||||||
Sprite button = buttonSpriteAsset.LoadAsset();
|
|
||||||
```
|
|
||||||
|
|
||||||
You can view an example file [here](https://github.com/All-Of-Us-Mods/MiraAPI/blob/master/MiraAPI.Example/ExampleAssets.cs).
|
|
||||||
|
|
||||||
You can create your own asset loaders by inheriting from `LoadableAsset<T>` and implementing the `LoadAsset` method.
|
|
||||||
Binary file not shown.
Reference in new issue
Block a user