Compare commits

..
Author SHA1 Message Date
Atony 0aa4a82d48 Add options to list (no logic)
Issue is mainly the button scale, that needs to update as soon as the change is made
2025-09-09 21:50:45 -05:00
266 changed files with 3168 additions and 10439 deletions

No files matched your search

+4 -4
View File
@@ -1,14 +1,14 @@
<Project>
<ItemGroup>
<PackageReference Include="Reactor" Version="2.5.0-ci.371" />
<PackageReference Include="Reactor" Version="2.3.1" />
<PackageReference Include="BepInEx.Unity.IL2CPP" Version="6.0.0-be.735" Private="false" ExcludeAssets="runtime;native" />
<PackageReference Include="AmongUs.GameLibs.Steam" Version="2026.8.18" PrivateAssets="all" />
<PackageReference Include="AmongUs.GameLibs.Steam" Version="2025.4.15" PrivateAssets="all" />
<PackageReference Include="BepInEx.AutoPlugin" Version="1.1.0" PrivateAssets="all" />
<PackageReference Include="BepInEx.IL2CPP.MSBuild" Version="2.1.0-rc.1" PrivateAssets="all" ExcludeAssets="runtime" />
</ItemGroup>
<Target Name="Copy" AfterTargets="Build" Condition="'$(AmongUs)' != ''">
<Copy SourceFiles="$(TargetPath)" DestinationFolder="$(AmongUs)/BepInEx/plugins/" UseSymboliclinksIfPossible="true" />
</Target>
</Project>
</Project>
+2 -2
View File
@@ -8,7 +8,7 @@
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<VersionPrefix>0.4.3</VersionPrefix>
<VersionPrefix>0.2.1</VersionPrefix>
<VersionSuffix>dev</VersionSuffix>
<Authors>All Of Us, XtraCube, Angxl</Authors>
@@ -42,4 +42,4 @@
<PropertyGroup>
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)\stylecop.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
</Project>
</Project>
+2 -18
View File
@@ -1,5 +1,4 @@
using System.Globalization;
using System.Linq;
using System.Linq;
using MiraAPI.Events;
using MiraAPI.Events.Mira;
using MiraAPI.Events.Vanilla.Gameplay;
@@ -18,7 +17,7 @@ public static class ExampleEventHandlers
{
public static void Initialize()
{
// You can register event handlers with the MiraEventManager class
// You can register event handlers with the MiraEventManager class.
var handle = MiraEventManager.RegisterEventHandler<BeforeMurderEvent>(@event =>
{
Logger<ExamplePlugin>.Info($"{@event.Source.Data.PlayerName} is about to kill {@event.Target.Data.PlayerName}");
@@ -38,21 +37,6 @@ public static class ExampleEventHandlers
MiraEventManager.UnregisterEventHandler(handle);
}
// If you want to check vanilla buttons, use something like this!
[RegisterEvent]
public static void VanillaButtonClickHandler(VanillaButtonClickEvent @event)
{
Logger<ExamplePlugin>.Info($"{@event.Button.buttonLabelText.text} is being used!");
if (@event.PlayerTarget != null)
{
Logger<ExamplePlugin>.Info($"{@event.PlayerTarget.Data.PlayerName} is being targeted!");
}
else if (@event.VentTarget != null)
{
Logger<ExamplePlugin>.Info($"Vent with id {@event.VentTarget.Id.ToString(CultureInfo.InvariantCulture)} is being targeted!");
}
}
// If you want to add extra votes to a player, do something like this.
[RegisterEvent]
public static void StartMeetingEvent(StartMeetingEvent _)
+2 -4
View File
@@ -1,13 +1,12 @@
using BepInEx;
using System.Linq;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using MiraAPI.PluginLoading;
using MiraAPI.Utilities.Assets;
using Reactor;
using Reactor.Networking;
using Reactor.Networking.Attributes;
using UnityEngine;
namespace MiraAPI.Example;
@@ -20,7 +19,6 @@ public partial class ExamplePlugin : BasePlugin, IMiraPlugin
{
public Harmony Harmony { get; } = new(Id);
public string OptionsTitleText => "Mira API\nExample Mod";
public string CustomOptionMenuNameTwo => "angxl's Options";
public ConfigFile GetConfigFile() => Config;
public override void Load()
{
-13
View File
@@ -1,13 +0,0 @@
using MiraAPI.GameModes;
using MiraAPI.Utilities.Assets;
using UnityEngine;
namespace MiraAPI.Example.GameModes;
public class ExampleMode : AbstractGameMode
{
public override string Name => "Example Mode";
public override string Description => "An example gamemode.";
public override LoadableAsset<Sprite>? Icon => ExampleAssets.ExampleButton;
public override Color Color => Color.red;
}
@@ -10,8 +10,6 @@ public class ExampleOptions2 : AbstractOptionGroup
public override uint GroupPriority => 0; // This group will be displayed first. The default value is uint.MaxValue.
public override MenuCategory ParentMenu => MenuCategory.CustomTwo;
public ModdedPlayerOption PlayerOption2 { get; } = new("YOU MUST CHOSE A PLAYER", false);
public ModdedPlayerOption PlayerOption { get; } = new("Eh whatever");
public ModdedToggleOption ToggleOpt1 { get; } = new("Toggle Option 1", false);
@@ -9,7 +9,7 @@ public class GeneralModifierOptions : AbstractOptionGroup
public override uint GroupPriority => 0;
public override MenuCategory ParentMenu => MenuCategory.Modifiers;
public override bool ShowInModifiersMenu => true;
[ModdedToggleOption("Some Boolean Option")]
public bool SomeBooleanOption { get; set; } = true;
@@ -2,17 +2,11 @@
using MiraAPI.GameOptions;
using MiraAPI.GameOptions.Attributes;
using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets;
namespace MiraAPI.Example.Options.Roles;
public class FreezerRoleSettings : AbstractOptionGroup<FreezerRole>
{
public override OptionNotifConfiguration Configuration => new(
Palette.Blue,
TmpSpriteUtils.CreateSpriteAsset(
MiraAssets.ImpostorFile.LoadAsset(),
"ApiExample.Role.Impostor.FreezerRole"));
public override string GroupName => "Custom Role";
[ModdedNumberOption("Freeze Duration", 1, 15, 1, MiraNumberSuffixes.Seconds)]
@@ -6,7 +6,7 @@ using MiraAPI.Utilities;
namespace MiraAPI.Example.Options.Roles;
public class TeleporterOptions : AbstractRoleOptionGroup<TeleporterRole>
public class TeleporterOptions : AbstractOptionGroup<TeleporterRole>
{
public override string GroupName => "Teleporter";
-3
View File
@@ -1,7 +1,6 @@
using AmongUs.GameOptions;
using MiraAPI.Patches.Stubs;
using MiraAPI.Roles;
using MiraAPI.Utilities.Assets;
using Reactor.Utilities;
using TMPro;
using UnityEngine;
@@ -21,8 +20,6 @@ public class ChameloenRole : CrewmateRole, ICustomRole
public CustomRoleConfiguration Configuration => new(this)
{
OptionsScreenshot = ExampleAssets.Banner,
Icon = MiraAssets.CrewmateFile,
IconTmp = TmpSpriteUtils.CreateSpriteAsset(MiraAssets.CrewmateFile.LoadAsset(), "ApiExample.Role.Impostor.ChameleonRole"),
IntroSound = CustomRoleUtils.GetIntroSound(RoleTypes.Shapeshifter),
};
-3
View File
@@ -1,5 +1,4 @@
using MiraAPI.Roles;
using MiraAPI.Utilities.Assets;
using UnityEngine;
namespace MiraAPI.Example.Roles;
@@ -14,8 +13,6 @@ public class FreezerRole : ImpostorRole, ICustomRole
public CustomRoleConfiguration Configuration => new CustomRoleConfiguration(this)
{
Icon = MiraAssets.ImpostorFile,
IconTmp = TmpSpriteUtils.CreateSpriteAsset(MiraAssets.ImpostorFile.LoadAsset(), "ApiExample.Role.Impostor.FreezerRole"),
OptionsScreenshot = ExampleAssets.Banner,
MaxRoleCount = 2,
};
+1 -3
View File
@@ -1,5 +1,4 @@
using Il2CppInterop.Runtime.Attributes;
using MiraAPI.Example.GameOver;
using MiraAPI.Example.GameOver;
using MiraAPI.GameEnd;
using MiraAPI.Roles;
using UnityEngine;
@@ -23,7 +22,6 @@ public class NeutralKillerRole : ImpostorRole, ICustomRole
public RoleOptionsGroup RoleOptionsGroup { get; } = new("Outcast", Color.gray);
[HideFromIl2Cpp]
public TeamIntroConfiguration? IntroConfiguration { get; } = new(
Color.gray,
"OUTCAST",
+2 -2
View File
@@ -5,12 +5,12 @@ using HarmonyLib;
namespace MiraAPI.Colors;
/// <summary>
/// Used to register and track <see cref="CustomColor"/>s.
/// Used to register and track custom colors.
/// </summary>
public static class PaletteManager
{
/// <summary>
/// Gets all registered <see cref="CustomColor"/>s.
/// Gets all registered custom colors.
/// </summary>
public static CustomColor[] RegisteredColors => [.. CustomColors];
@@ -3,7 +3,7 @@
namespace MiraAPI.Colors;
/// <summary>
/// Used to mark a class for <see cref="CustomColor"/> registration.
/// Used to mark a class for custom colors registration.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class RegisterCustomColorsAttribute : Attribute;
@@ -3,20 +3,20 @@
namespace MiraAPI.Events.Mira;
/// <summary>
/// Invoked when a <see cref="CustomActionButton"/> click is cancelled. Do not use for vanilla <see cref="AbilityButton"/>s.
/// Invoked when a Mira Button click is cancelled. Do not use for vanilla buttons.
/// </summary>
/// <typeparam name="T">The <see cref="CustomActionButton"/> type.</typeparam>
/// <typeparam name="T">The Mira Button type.</typeparam>
public sealed class MiraButtonCancelledEvent<T> : MiraEvent where T : CustomActionButton
{
/// <summary>
/// Gets the <see cref="CustomActionButton"/> whose click was cancelled.
/// Gets Mira Button whose click was cancelled.
/// </summary>
public T Button { get; }
/// <summary>
/// Initializes a new instance of the <see cref="MiraButtonCancelledEvent{T}"/> class.
/// </summary>
/// <param name="button">The <see cref="CustomActionButton"/> whose click was cancelled.</param>
/// <param name="button">The Mira Button whose click was cancelled.</param>
public MiraButtonCancelledEvent(T button)
{
Button = button;
@@ -24,19 +24,19 @@ public sealed class MiraButtonCancelledEvent<T> : MiraEvent where T : CustomActi
}
/// <summary>
/// Invoked when a <see cref="CustomActionButton"/> click is cancelled. Do not use for vanilla <see cref="AbilityButton"/>s.
/// Invoked when a Mira Button click is cancelled. Do not use for vanilla buttons.
/// </summary>
public sealed class MiraButtonCancelledEvent : MiraEvent
{
/// <summary>
/// Gets the <see cref="CustomActionButton"/> whose click was cancelled.
/// Gets Mira Button whose click was cancelled.
/// </summary>
public CustomActionButton Button { get; }
/// <summary>
/// Initializes a new instance of the <see cref="MiraButtonCancelledEvent"/> class.
/// </summary>
/// <param name="button">The <see cref="CustomActionButton"/> whose click was cancelled.</param>
/// <param name="button">The Mira Button whose click was cancelled.</param>
public MiraButtonCancelledEvent(CustomActionButton button)
{
Button = button;
+11 -11
View File
@@ -3,26 +3,26 @@
namespace MiraAPI.Events.Mira;
/// <summary>
/// Button click event for <see cref="CustomActionButton"/>s only. Do not use for vanilla <see cref="AbilityButton"/>s.
/// Button click event for Mira Buttons only. Do not use for vanilla buttons.
/// </summary>
/// <typeparam name="T">The <see cref="CustomActionButton"/> type.</typeparam>
public class MiraButtonClickEvent<T> : MiraCancelableEvent where T : CustomActionButton
/// <typeparam name="T">The Mira Button type.</typeparam>
public sealed class MiraButtonClickEvent<T> : MiraCancelableEvent where T : CustomActionButton
{
/// <summary>
/// Gets the <see cref="CustomActionButton"/> that was clicked.
/// Gets Mira Button that was clicked.
/// </summary>
public T Button { get; }
/// <summary>
/// Gets the generic <see cref="MiraButtonClickEvent"/> that is invoked before this button-specific event.
/// Gets the generic click event that is invoked before this button-specific event.
/// </summary>
public MiraButtonClickEvent GenericClickEvent { get; }
/// <summary>
/// Initializes a new instance of the <see cref="MiraButtonClickEvent{T}"/> class.
/// </summary>
/// <param name="button">The <see cref="CustomActionButton"/> that was clicked.</param>
/// <param name="genericClickEvent">The generic <see cref="MiraButtonClickEvent"/> invoked before button-specific events.</param>
/// <param name="button">The Mira Button that was clicked.</param>
/// <param name="genericClickEvent">The generic click event invoked before button-specific events.</param>
public MiraButtonClickEvent(T button, MiraButtonClickEvent genericClickEvent)
{
GenericClickEvent = genericClickEvent;
@@ -31,19 +31,19 @@ public class MiraButtonClickEvent<T> : MiraCancelableEvent where T : CustomActio
}
/// <summary>
/// Button click event for <see cref="CustomActionButton"/>s only. Do not use for vanilla <see cref="AbilityButton"/>s.
/// Button click event for Mira Buttons only. Do not use for vanilla buttons.
/// </summary>
public class MiraButtonClickEvent : MiraCancelableEvent
public sealed class MiraButtonClickEvent : MiraCancelableEvent
{
/// <summary>
/// Gets the <see cref="CustomActionButton"/> that was clicked.
/// Gets Mira Button that was clicked.
/// </summary>
public CustomActionButton Button { get; }
/// <summary>
/// Initializes a new instance of the <see cref="MiraButtonClickEvent"/> class.
/// </summary>
/// <param name="button">The <see cref="CustomActionButton"/> that was clicked.</param>
/// <param name="button">The Mira Button that was clicked.</param>
public MiraButtonClickEvent(CustomActionButton button)
{
Button = button;
+1 -4
View File
@@ -1,11 +1,8 @@
using MiraAPI.PluginLoading;
namespace MiraAPI.Events;
namespace MiraAPI.Events;
/// <summary>
/// Abstract class for Mira Events that can be cancelled.
/// </summary>
[MiraIgnore]
public abstract class MiraCancelableEvent : MiraEvent
{
/// <summary>
+1 -4
View File
@@ -1,9 +1,6 @@
using MiraAPI.PluginLoading;
namespace MiraAPI.Events;
namespace MiraAPI.Events;
/// <summary>
/// Abstract class for Mira Events.
/// </summary>
[MiraIgnore]
public abstract class MiraEvent;
+1 -1
View File
@@ -3,7 +3,7 @@
namespace MiraAPI.Events;
/// <summary>
/// Event handle to use when unregistering a <see cref="MiraEvent"/>.
/// Event handle to use when unregistering an event.
/// </summary>
public class MiraEventHandle
{
+18 -17
View File
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Reactor.Utilities;
namespace MiraAPI.Events;
@@ -12,11 +13,11 @@ public static class MiraEventManager
private static readonly Dictionary<Type, List<MiraEventWrapper>> EventWrappers = [];
/// <summary>
/// Invoke a <see cref="MiraEvent"/>.
/// Invoke an event.
/// </summary>
/// <param name="eventInstance">The <typeparamref name="T"/> instance.</param>
/// <param name="eventInstance">The event instance.</param>
/// <typeparam name="T">Type of Event.</typeparam>
/// <returns>If there was an event handler invoked for this event, return <see langword="true"/>. Otherwise, return <see langword="false"/>.</returns>
/// <returns>If there was an event handler invoked for this event, return true. Otherwise, return false.</returns>
public static bool InvokeEvent<T>(T eventInstance) where T : MiraEvent
{
EventWrappers.TryGetValue(typeof(T), out var handlers);
@@ -33,7 +34,7 @@ public static class MiraEventManager
}
catch (Exception ex)
{
Error($"Error invoking event handler for {typeof(T).Name}: {ex.ToString()}");
Logger<MiraApiPlugin>.Error($"Error invoking event handler for {typeof(T).Name}: {ex.ToString()}");
}
}
@@ -41,11 +42,11 @@ public static class MiraEventManager
}
/// <summary>
/// Invoke a <see cref="MiraEvent"/> and use a specific type to find the handlers.
/// Invoke an event and use a specific type to find the handlers.
/// </summary>
/// <param name="eventInstance">The <see cref="MiraEvent"/> instance.</param>
/// <param name="eventInstance">The event instance.</param>
/// <param name="type">The type to use for handler lookup.</param>
/// <returns>If there was an event handler invoked for this event, return <see langword="true"/>. Otherwise, return <see langword="false"/>.</returns>
/// <returns>If there was an event handler invoked for this event, return true. Otherwise, return false.</returns>
public static bool InvokeEvent(MiraEvent eventInstance, Type type)
{
EventWrappers.TryGetValue(type, out var handlers);
@@ -62,7 +63,7 @@ public static class MiraEventManager
}
catch (Exception ex)
{
Error($"Error invoking event handler for {type.Name}: {ex.ToString()}");
Logger<MiraApiPlugin>.Error($"Error invoking event handler for {type.Name}: {ex.ToString()}");
}
}
@@ -70,12 +71,12 @@ public static class MiraEventManager
}
/// <summary>
/// Register a <see cref="MiraEvent"/> handler.
/// Register an event.
/// </summary>
/// <param name="type">The type of <see cref="MiraEvent"/> event.</param>
/// <param name="methodInfo">The <see cref="MethodInfo"/> of the event handler.</param>
/// <param name="type">The type of event.</param>
/// <param name="methodInfo">The MethodInfo of the event handler.</param>
/// <param name="priority">The priority of the event handler. Lower values are called first.</param>
/// <returns>A <see cref="MiraEventHandle"/> to use when unregistering the event.</returns>
/// <returns>An event handle to use when unregistering the event.</returns>
public static MiraEventHandle RegisterEventHandler(Type type, MethodInfo methodInfo, int priority = 0)
{
if (!type.IsSubclassOf(typeof(MiraEvent)))
@@ -101,12 +102,12 @@ public static class MiraEventManager
}
/// <summary>
/// Register a <typeparamref name="T"/> handler.
/// Register an event.
/// </summary>
/// <param name="handler">The callback method/handler for the event.</param>
/// <param name="priority">The priority of the event handler. Lower values are called first.</param>
/// <typeparam name="T">Type of <see cref="MiraEvent"/> event.</typeparam>
/// <returns>A <see cref="MiraEventHandle"/> to use when unregistering the event.</returns>
/// <typeparam name="T">Type of event.</typeparam>
/// <returns>An event handle to use when unregistering the event.</returns>
public static MiraEventHandle RegisterEventHandler<T>(Action<T> handler, int priority = 0) where T : MiraEvent
{
EventWrappers.TryAdd(typeof(T), []);
@@ -126,10 +127,10 @@ public static class MiraEventManager
}
/// <summary>
/// Unregister a <see cref="MiraEvent"/> handler using the <see cref="MiraEventHandle"/>.
/// Unregister an event using the event handle.
/// </summary>
/// <param name="eventHandle">A handle to the event.</param>
/// <returns><see langword="true"/> if the event was unregistered successfully, <see langword="false"/> otherwise.</returns>
/// <returns>True if the event was unregistered successfully, false otherwise.</returns>
public static bool UnregisterEventHandler(MiraEventHandle eventHandle)
{
if (!EventWrappers.TryGetValue(eventHandle.EventType, out var handlers))
+1 -1
View File
@@ -3,7 +3,7 @@
namespace MiraAPI.Events;
/// <summary>
/// Wrapper for <see cref="MiraEvent"/> handlers.
/// Wrapper for event handlers.
/// </summary>
public class MiraEventWrapper
{
+1 -1
View File
@@ -3,7 +3,7 @@
namespace MiraAPI.Events;
/// <summary>
/// Register a <see cref="MiraEvent"/> handler.
/// Register an event.
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class RegisterEventAttribute : Attribute
@@ -6,86 +6,30 @@
public class AfterMurderEvent : MiraEvent
{
/// <summary>
/// Gets the <see cref="PlayerControl"/> that killed the <see cref="Target"/>.
/// Gets the player that killed the target.
/// </summary>
public PlayerControl Source { get; }
/// <summary>
/// Gets the <see cref="PlayerControl"/> that was killed.
/// Gets the player that was killed.
/// </summary>
public PlayerControl Target { get; }
/// <summary>
/// Gets the <see cref="PlayerControl"/> that was framed, if any.
/// </summary>
public PlayerControl? Framed { get; }
/// <summary>
/// Gets a value indicating whether the murder was done indirectly.
/// This is commonly used to prevent the <see cref="Source"/> <see cref="PlayerControl"/> from being killed.
/// </summary>
public bool IsIndirectAttack { get; }
/// <summary>
/// Gets a value indicating whether the murder was able to bypass protective roles or general defense, such as <see cref="GuardianAngelRole"/>.
/// </summary>
public bool IgnoreDefense { get; }
/// <summary>
/// Gets the <see cref="Target"/>'s <see cref="DeadBody"/>, if it exists.
/// Gets the player's body, if it exists.
/// </summary>
public DeadBody? DeadBody { get; }
/// <summary>
/// Initializes a new instance of the <see cref="AfterMurderEvent"/> class.
/// </summary>
/// <param name="source">The <see cref="PlayerControl"/> that killed the <paramref name="target"/>.</param>
/// <param name="target">The <see cref="PlayerControl"/> that was killed.</param>
/// <param name="deadBody">The <paramref name="target"/>'s <see cref="DeadBody"/>, if it exists.</param>
/// <param name="isIndirect">Whether the murder was caused indirectly.</param>
/// <param name="ignoreDefense">Whether the murder ignored protection.</param>
public AfterMurderEvent(PlayerControl source, PlayerControl target, DeadBody? deadBody, bool isIndirect, bool ignoreDefense = false)
{
Source = source;
Target = target;
Framed = null;
DeadBody = deadBody;
IsIndirectAttack = isIndirect;
IgnoreDefense = ignoreDefense;
}
/// <summary>
/// Initializes a new instance of the <see cref="AfterMurderEvent"/> class.
/// </summary>
/// <param name="source">The <see cref="PlayerControl"/> that killed the <paramref name="target"/>.</param>
/// <param name="target">The <see cref="PlayerControl"/> that was killed.</param>
/// <param name="framed">The <see cref="PlayerControl"/> that was framed.</param>
/// <param name="deadBody">The <paramref name="target"/>'s <see cref="DeadBody"/>, if it exists.</param>
/// <param name="isIndirect">Whether the murder was caused indirectly.</param>
/// <param name="ignoreDefense">Whether the murder ignored protection.</param>
public AfterMurderEvent(PlayerControl source, PlayerControl target, PlayerControl framed, DeadBody? deadBody, bool isIndirect = true, bool ignoreDefense = false)
{
Source = source;
Target = target;
Framed = framed;
DeadBody = deadBody;
IsIndirectAttack = isIndirect;
IgnoreDefense = ignoreDefense;
}
/// <summary>
/// Initializes a new instance of the <see cref="AfterMurderEvent"/> class.
/// </summary>
/// <param name="source">The <see cref="PlayerControl"/> that killed the <paramref name="target"/>.</param>
/// <param name="target">The <see cref="PlayerControl"/> that was killed.</param>
/// <param name="deadBody">The <paramref name="target"/>'s <see cref="DeadBody"/>, if it exists.</param>
/// <param name="source">The killer.</param>
/// <param name="target">The killed player.</param>
/// <param name="deadBody">The player's vody, if it exists.</param>
public AfterMurderEvent(PlayerControl source, PlayerControl target, DeadBody? deadBody)
{
Source = source;
Target = target;
Framed = null;
DeadBody = deadBody;
IsIndirectAttack = false;
IgnoreDefense = false;
}
}
@@ -1,7 +1,7 @@
namespace MiraAPI.Events.Vanilla.Gameplay;
/// <summary>
/// Invoked before <see cref="GameManager.RpcEndGame"/> is called, allowing cancellation of the game end entirely.
/// Invoked before RpcEndGame is called, allowing cancellation of the game end entirely.
/// </summary>
public class BeforeGameEndEvent : MiraCancelableEvent
{
@@ -1,6 +1,4 @@
using MiraAPI.Networking;
namespace MiraAPI.Events.Vanilla.Gameplay;
namespace MiraAPI.Events.Vanilla.Gameplay;
/// <summary>
/// Event that is invoked before a player is murdered. This event is cancelable.
@@ -8,101 +6,23 @@ namespace MiraAPI.Events.Vanilla.Gameplay;
public sealed class BeforeMurderEvent : MiraCancelableEvent
{
/// <summary>
/// Gets the <see cref="PlayerControl"/> that is killing the <see cref="Target"/>.
/// Gets the player that is killing the target.
/// </summary>
public PlayerControl Source { get; }
/// <summary>
/// Gets the <see cref="PlayerControl"/> that is being killed.
/// Gets the player that is being killed.
/// </summary>
public PlayerControl Target { get; }
/// <summary>
/// Gets the <see cref="PlayerControl"/> that is being framed, if any.
/// </summary>
public PlayerControl? Framed { get; }
/// <summary>
/// Gets or sets a value indicating whether the murder is done indirectly.
/// This is commonly used to prevent the <see cref="Source"/> <see cref="PlayerControl"/> from being killed.
/// </summary>
public bool IsIndirectAttack { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the murder should bypass protective roles or general defense, such as <see cref="GuardianAngelRole"/>.
/// </summary>
public bool IgnoreDefense { get; set; }
/// <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 <see cref="PlayerControl"/> that is killing the <paramref name="target"/>.</param>
/// <param name="target">The <see cref="PlayerControl"/> that is being killed.</param>
/// <param name="isIndirect">Whether the murder is caused indirectly.</param>
/// <param name="ignoreDefense">Whether the murder should ignore protection.</param>
/// <param name="inMeeting">Whether the murder is intended to be triggered in a meeting.</param>
public BeforeMurderEvent(PlayerControl source, PlayerControl target, bool isIndirect, bool ignoreDefense = false, MeetingCheck inMeeting = MeetingCheck.Ignore)
{
Source = source;
Target = target;
Framed = null;
IsIndirectAttack = isIndirect;
IgnoreDefense = ignoreDefense;
InMeeting = inMeeting;
}
/// <summary>
/// Initializes a new instance of the <see cref="BeforeMurderEvent"/> class.
/// </summary>
/// <param name="source">The <see cref="PlayerControl"/> that is killing the <paramref name="target"/>.</param>
/// <param name="target">The <see cref="PlayerControl"/> that is being killed.</param>
/// <param name="framed">The <see cref="PlayerControl"/> that is being framed.</param>
/// <param name="isIndirect">Whether the murder is caused indirectly.</param>
/// <param name="ignoreDefense">Whether the murder should ignore protection.</param>
/// <param name="inMeeting">Whether the murder is intended to be triggered in a meeting.</param>
public BeforeMurderEvent(PlayerControl source, PlayerControl target, PlayerControl framed, bool isIndirect = true, bool ignoreDefense = false, MeetingCheck inMeeting = MeetingCheck.Ignore)
{
Source = source;
Target = target;
Framed = framed;
IsIndirectAttack = isIndirect;
IgnoreDefense = ignoreDefense;
InMeeting = inMeeting;
}
/// <summary>
/// Initializes a new instance of the <see cref="BeforeMurderEvent"/> class.
/// </summary>
/// <param name="source">The <see cref="PlayerControl"/> that is killing the <paramref name="target"/>.</param>
/// <param name="target">The <see cref="PlayerControl"/> 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;
Framed = null;
IsIndirectAttack = false;
IgnoreDefense = false;
InMeeting = inMeeting;
}
/// <summary>
/// Initializes a new instance of the <see cref="BeforeMurderEvent"/> class.
/// </summary>
/// <param name="source">The <see cref="PlayerControl"/> that is killing the <paramref name="target"/>.</param>
/// <param name="target">The <see cref="PlayerControl"/> that is being killed.</param>
/// <param name="source">The player that is killing the target.</param>
/// <param name="target">The player that is being killed.</param>
public BeforeMurderEvent(PlayerControl source, PlayerControl target)
{
Source = source;
Target = target;
Framed = null;
IsIndirectAttack = false;
IgnoreDefense = false;
InMeeting = MeetingCheck.Ignore;
}
}
@@ -1,13 +1,12 @@
namespace MiraAPI.Events.Vanilla.Gameplay;
/// <summary>
/// Event invoked on <see cref="IntroCutscene.OnDestroy"/> and <see cref="ExileController.WrapUp"/>
/// to determine if it should run the <see cref="RoundStartEvent"/> event or not.
/// Event invoked on IntroCutscene.OnDestroy and ExileController.WrapUp to determine if it should run the round event or not.
/// </summary>
public class BeforeRoundStartEvent : MiraCancelableEvent
{
/// <summary>
/// Gets a value indicating whether the event was triggered by the <see cref="IntroCutscene"/> or <see cref="ExileController"/>.
/// Gets a value indicating whether the event was triggered by the IntroCutscene or ExileController.
/// </summary>
public bool TriggeredByIntro { get; }
@@ -6,14 +6,14 @@
public class GameEndEvent : MiraEvent
{
/// <summary>
/// Gets the <see cref="global::EndGameManager"/> instance.
/// Gets the instance of the EndGameManager.
/// </summary>
public EndGameManager EndGameManager { get; }
/// <summary>
/// Initializes a new instance of the <see cref="GameEndEvent"/> class.
/// </summary>
/// <param name="manager">The <see cref="global::EndGameManager"/> instance.</param>
/// <param name="manager">The instance of the EndGameManager.</param>
public GameEndEvent(EndGameManager manager)
{
EndGameManager = manager;
@@ -6,7 +6,7 @@
public class IntroBeginEvent : MiraEvent
{
/// <summary>
/// Gets the <see cref="global::IntroCutscene"/> instance.
/// Gets the instance of the IntroCutscene.
/// </summary>
public IntroCutscene IntroCutscene { get; }
@@ -6,7 +6,7 @@
public class IntroEndEvent(IntroCutscene introCutscene) : MiraEvent
{
/// <summary>
/// Gets the <see cref="global::IntroCutscene"/> instance.
/// Gets the instance of the IntroCutscene.
/// </summary>
public IntroCutscene IntroCutscene { get; } = introCutscene;
}
@@ -6,7 +6,7 @@
public class IntroRoleRevealEvent : MiraEvent
{
/// <summary>
/// Gets the <see cref="global::IntroCutscene"/> instance.
/// Gets the instance of the IntroCutscene.
/// </summary>
public IntroCutscene IntroCutscene { get; }
@@ -1,13 +1,12 @@
namespace MiraAPI.Events.Vanilla.Gameplay;
/// <summary>
/// Start Round event, invoked on <see cref="IntroCutscene.OnDestroy"/> and <see cref="ExileController.WrapUp"/>
/// if the <see cref="BeforeRoundStartEvent"/> isn't cancelled.
/// Start Round event, invoked on IntroCutscene.OnDestroy and ExileController.WrapUp if the event isn't cancelled.
/// </summary>
public class RoundStartEvent : MiraEvent
{
/// <summary>
/// Gets a value indicating whether the event was triggered by the <see cref="IntroCutscene"/> or <see cref="ExileController"/>.
/// Gets a value indicating whether the event was triggered by the IntroCutscene or ExileController.
/// </summary>
public bool TriggeredByIntro { get; }
@@ -8,7 +8,7 @@ namespace MiraAPI.Events.Vanilla.Gameplay;
public class SetRoleEvent : MiraEvent
{
/// <summary>
/// Gets the <see cref="PlayerControl"/> whose role was changed.
/// Gets the player whos role was changed.
/// </summary>
public PlayerControl Player { get; }
@@ -1,44 +0,0 @@
using MiraAPI.Hud;
namespace MiraAPI.Events.Vanilla.Gameplay;
/// <summary>
/// Invoked when a Vanilla <see cref="AbilityButton"/> click is cancelled. Do not use for <see cref="CustomActionButton"/>.
/// </summary>
/// <typeparam name="T">The vanilla <see cref="AbilityButton"/> type.</typeparam>
public sealed class VanillaButtonCancelledEvent<T> : MiraEvent where T : AbilityButton
{
/// <summary>
/// Gets the Vanilla <see cref="AbilityButton"/> 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 vanilla <see cref="AbilityButton"/> whose click was cancelled.</param>
public VanillaButtonCancelledEvent(T button)
{
Button = button;
}
}
/// <summary>
/// Invoked when a Vanilla <see cref="AbilityButton"/> click is cancelled. Do not use for <see cref="CustomActionButton"/>s.
/// </summary>
public sealed class VanillaButtonCancelledEvent : MiraEvent
{
/// <summary>
/// Gets the Vanilla <see cref="AbilityButton"/> 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 <see cref="AbilityButton"/> whose click was cancelled.</param>
public VanillaButtonCancelledEvent(AbilityButton button)
{
Button = button;
}
}
@@ -1,65 +0,0 @@
using MiraAPI.Hud;
namespace MiraAPI.Events.Vanilla.Gameplay;
/// <summary>
/// Button click event for Vanilla <see cref="AbilityButton"/>s only. Do not use for <see cref="CustomActionButton"/>s.
/// </summary>
/// <typeparam name="T">The vanilla <see cref="AbilityButton"/> type.</typeparam>
public sealed class VanillaButtonClickEvent<T> : MiraCancelableEvent where T : AbilityButton
{
/// <summary>
/// Gets the vanilla <see cref="AbilityButton"/> that was clicked.
/// </summary>
public T Button { get; }
/// <summary>
/// Gets the generic <see cref="VanillaButtonClickEvent"/> 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 vanilla <see cref="AbilityButton"/> that was clicked.</param>
/// <param name="genericClickEvent">The generic <see cref="VanillaButtonClickEvent"/> invoked before button-specific events.</param>
public VanillaButtonClickEvent(T button, VanillaButtonClickEvent genericClickEvent)
{
GenericClickEvent = genericClickEvent;
Button = button;
}
}
/// <summary>
/// Button click event for Vanilla <see cref="AbilityButton"/> only. Do not use for <see cref="CustomActionButton"/>s.
/// </summary>
public sealed class VanillaButtonClickEvent : MiraCancelableEvent
{
/// <summary>
/// Gets the Vanilla <see cref="AbilityButton"/> that was clicked.
/// </summary>
public AbilityButton Button { get; }
/// <summary>
/// Gets the target <see cref="PlayerControl"/>, if any, of the Vanilla <see cref="AbilityButton"/> that was clicked.
/// </summary>
public PlayerControl? PlayerTarget { get; }
/// <summary>
/// Gets the target <see cref="Vent"/>, if any, of the Vanilla <see cref="AbilityButton"/> 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 <see cref="AbilityButton"/> that was clicked.</param>
/// <param name="playerTarget">The <see cref="PlayerControl"/> target, if any.</param>
/// <param name="ventTarget">The <see cref="Vent"/> target, if any.</param>
public VanillaButtonClickEvent(AbilityButton button, PlayerControl? playerTarget = null, Vent? ventTarget = null)
{
Button = button;
PlayerTarget = playerTarget;
VentTarget = ventTarget;
}
}
@@ -6,14 +6,14 @@
public class PlayerOpenSabotageEvent : MiraCancelableEvent
{
/// <summary>
/// Gets the <see cref="global::MapBehaviour"/>.
/// Gets the MapBehaviour.
/// </summary>
public MapBehaviour MapBehaviour { get; }
/// <summary>
/// Initializes a new instance of the <see cref="PlayerOpenSabotageEvent"/> class.
/// </summary>
/// <param name="mapBehaviour">The <see cref="global::MapBehaviour"/>.</param>
/// <param name="mapBehaviour">The MapBehaviour.</param>
public PlayerOpenSabotageEvent(MapBehaviour mapBehaviour)
{
MapBehaviour = mapBehaviour;
@@ -1,7 +1,7 @@
namespace MiraAPI.Events.Vanilla.Map;
/// <summary>
/// <see cref="MiraCancelableEvent"/> that is invoked before a system is updated. Usually used for sabotage events.
/// Cancelable event that is invoked before a system is updated. Usually used for sabotage events.
/// </summary>
public class UpdateSystemEvent : MiraCancelableEvent
{
@@ -11,7 +11,7 @@ public class UpdateSystemEvent : MiraCancelableEvent
public SystemTypes SystemType { get; }
/// <summary>
/// Gets the <see cref="PlayerControl"/> that is updating the system.
/// Gets the player that is updating the system.
/// </summary>
public PlayerControl Player { get; }
@@ -21,10 +21,10 @@ public class UpdateSystemEvent : MiraCancelableEvent
public byte Amount { get; }
/// <summary>
/// Initializes a new instance of the <see cref="UpdateSystemEvent"/> class.
/// Initializes a new instance of the <see cref="UpdateSystemEvent"/> class.
/// </summary>
/// <param name="systemType">The <see cref="SystemTypes"/> being updated.</param>
/// <param name="player">The <see cref="PlayerControl"/> that is updating the system.</param>
/// <param name="systemType">The SystemType being updated.</param>
/// <param name="player">The player that is updating the system.</param>
/// <param name="amount">Amount to update System to.</param>
public UpdateSystemEvent(SystemTypes systemType, PlayerControl player, byte amount)
{
@@ -6,14 +6,14 @@
public class EjectionEvent : MiraEvent
{
/// <summary>
/// Gets the <see cref="global::ExileController"/> instance.
/// Gets the instance of the ExileController.
/// </summary>
public ExileController ExileController { get; }
/// <summary>
/// Initializes a new instance of the <see cref="EjectionEvent"/> class.
/// </summary>
/// <param name="controller">The <see cref="global::ExileController"/> instance.</param>
/// <param name="controller">The exile controller.</param>
public EjectionEvent(ExileController controller)
{
ExileController = controller;
@@ -6,7 +6,7 @@
public class EndMeetingEvent(MeetingHud meetingHud) : MiraEvent
{
/// <summary>
/// Gets the <see cref="global::MeetingHud"/> instance.
/// Gets the MeetingHud instance.
/// </summary>
public MeetingHud MeetingHud { get; } = meetingHud;
}
@@ -2,7 +2,7 @@
/// <summary>
/// The event that is invoked when a body is reported.
/// This is invoked in <see cref="PlayerControl.CmdReportDeadBody(NetworkedPlayerInfo)"/>.
/// This is invoked in CmdReportBody.
/// </summary>
public class ReportBodyEvent : MiraCancelableEvent
{
@@ -12,12 +12,12 @@ public class ReportBodyEvent : MiraCancelableEvent
public PlayerControl Reporter { get; }
/// <summary>
/// Gets the player that is being reported. Will be <see langword="null"/> for emergency meeting.
/// Gets the player that is being reported. Will be null for emergency meeting.
/// </summary>
public NetworkedPlayerInfo? Target { get; }
/// <summary>
/// Gets the <see cref="DeadBody"/> that is being reported. Will be <see langword="null"/> for emergency meeting.
/// Gets the body that is being reported. Will be null for emergency meeting.
/// </summary>
public DeadBody? Body { get; }
@@ -26,7 +26,7 @@ public class ReportBodyEvent : MiraCancelableEvent
/// </summary>
/// <param name="reporter">The player who reported the body.</param>
/// <param name="target">The player being reported.</param>
/// <param name="body">The <see cref="DeadBody"/> being reported.</param>
/// <param name="body">The body being reported.</param>
public ReportBodyEvent(PlayerControl reporter, NetworkedPlayerInfo? target, DeadBody? body)
{
Reporter = reporter;
@@ -7,14 +7,14 @@
public class StartMeetingEvent : MiraEvent
{
/// <summary>
/// Gets the <see cref="global::MeetingHud"/> instance.
/// Gets the MeetingHud instance.
/// </summary>
public MeetingHud MeetingHud { get; }
/// <summary>
/// Initializes a new instance of the <see cref="StartMeetingEvent"/> class.
/// </summary>
/// <param name="meetingHud">The <see cref="global::MeetingHud"/> instance.</param>
/// <param name="meetingHud">The MeetingHud instance.</param>
public StartMeetingEvent(MeetingHud meetingHud)
{
MeetingHud = meetingHud;
@@ -1,4 +1,6 @@
namespace MiraAPI.Events.Vanilla.Meeting.Voting;
using MiraAPI.Voting;
namespace MiraAPI.Events.Vanilla.Meeting.Voting;
/// <summary>
/// Event that is invoked after the local player successfully votes a player or skips. This event is not cancelable.
@@ -6,7 +8,7 @@
public class AfterVoteEvent : MiraEvent
{
/// <summary>
/// Gets the instance of the voter's <see cref="PlayerVoteArea"/>.
/// Gets the instance of the voter's vote data.
/// </summary>
public PlayerVoteArea VoteArea { get; }
@@ -18,7 +20,7 @@ public class AfterVoteEvent : MiraEvent
/// <summary>
/// Initializes a new instance of the <see cref="AfterVoteEvent"/> class.
/// </summary>
/// <param name="playerVoteArea">The <see cref="PlayerVoteArea"/> that was voted on.</param>
/// <param name="playerVoteArea">The player vote area that was voted on.</param>
/// <param name="voter">The player who voted.</param>
public AfterVoteEvent(PlayerVoteArea playerVoteArea, PlayerControl voter)
{
@@ -1,4 +1,6 @@
namespace MiraAPI.Events.Vanilla.Meeting.Voting;
using MiraAPI.Voting;
namespace MiraAPI.Events.Vanilla.Meeting.Voting;
/// <summary>
/// Event that is invoked before the local player confirms a vote on a player or when skipping. This event is cancelable.
@@ -6,7 +8,7 @@
public class BeforeVoteEvent : MiraCancelableEvent
{
/// <summary>
/// Gets the instance of the voter's <see cref="PlayerVoteArea"/>.
/// Gets the instance of the voter's vote data.
/// </summary>
public PlayerVoteArea VoteArea { get; }
@@ -18,7 +20,7 @@ public class BeforeVoteEvent : MiraCancelableEvent
/// <summary>
/// Initializes a new instance of the <see cref="BeforeVoteEvent"/> class.
/// </summary>
/// <param name="playerVoteArea">The <see cref="PlayerVoteArea"/> that was voted on.</param>
/// <param name="playerVoteArea">The player vote area that was voted on.</param>
/// <param name="voter">The player who voted.</param>
public BeforeVoteEvent(PlayerVoteArea playerVoteArea, PlayerControl voter)
{
@@ -18,7 +18,7 @@ public class CheckForEndVotingEvent : MiraCancelableEvent
/// <summary>
/// Initializes a new instance of the <see cref="CheckForEndVotingEvent"/> class.
/// </summary>
/// <param name="isVotingComplete"><see langword="true"/> if voting is complete, <see langword="false"/> otherwise.</param>
/// <param name="isVotingComplete">>True if voting is complete, false otherwise.</param>
public CheckForEndVotingEvent(bool isVotingComplete)
{
IsVotingComplete = isVotingComplete;
@@ -15,7 +15,7 @@ public class DummyVoteEvent : MiraCancelableEvent
public DummyBehaviour Dummy { get; }
/// <summary>
/// Gets the instance of the dummy's <see cref="PlayerVoteData"/>.
/// Gets the instance of the dummy's vote data.
/// </summary>
public PlayerVoteData VoteData { get; }
@@ -5,12 +5,12 @@ namespace MiraAPI.Events.Vanilla.Meeting.Voting;
/// <summary>
/// The event that is invoked when Mira handles a vote. This is invoked before Mira's behaviour, so be cautious.
/// If you intend on adding custom vote behaviour, cancel the event and do so.
/// If you are NOT canceling the event, keep in mind that Mira automatically removes a vote and adds the suspect to the <see cref="PlayerVoteData.Votes"/> list after this event is invoked.
/// If you are NOT canceling the event, keep in mind that Mira automatically removes a vote and adds the suspect to the VotedPlayers list after this event is invoked.
/// </summary>
public class HandleVoteEvent : MiraCancelableEvent
{
/// <summary>
/// Gets the instance of the voter's <see cref="PlayerVoteData"/>.
/// Gets the instance of the voter's vote data.
/// </summary>
public PlayerVoteData VoteData { get; }
@@ -25,24 +25,19 @@ public class HandleVoteEvent : MiraCancelableEvent
public byte TargetId { get; }
/// <summary>
/// Gets or sets a value indicating whether to prevent the vote from commencing.
/// Gets a value indicating whether to prevent the vote from commencing.
/// </summary>
public bool PreventVote { get; set; }
public bool PreventVote { get; }
/// <summary>
/// Gets the <see cref="NetworkedPlayerInfo"/> of the target.
/// Gets the player info of the target.
/// </summary>
public NetworkedPlayerInfo TargetPlayerInfo { get; }
/// <summary>
/// Gets or sets a value indicating whether the vote event is an Overrule caused by a Judge.
/// </summary>
public bool IsOverruling { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="HandleVoteEvent"/> class.
/// </summary>
/// <param name="playerVoteData">The voter's <see cref="PlayerVoteData"/>.</param>
/// <param name="playerVoteData">The voter's data.</param>
/// <param name="targetId">The target's playerId.</param>
public HandleVoteEvent(PlayerVoteData playerVoteData, byte targetId)
{
@@ -52,20 +47,4 @@ public class HandleVoteEvent : MiraCancelableEvent
PreventVote = false;
TargetPlayerInfo = GameData.Instance.GetPlayerById(targetId);
}
/// <summary>
/// Initializes a new instance of the <see cref="HandleVoteEvent"/> class.
/// </summary>
/// <param name="playerVoteData">The voter's <see cref="PlayerVoteData"/>.</param>
/// <param name="targetId">The target's playerId.</param>
/// <param name="isOverruling">Whether the vote is actually for a Judge.</param>
public HandleVoteEvent(PlayerVoteData playerVoteData, byte targetId, bool isOverruling)
{
VoteData = playerVoteData;
Player = playerVoteData.Owner;
TargetId = targetId;
PreventVote = false;
IsOverruling = isOverruling;
TargetPlayerInfo = GameData.Instance.GetPlayerById(targetId);
}
}
@@ -8,7 +8,7 @@ namespace MiraAPI.Events.Vanilla.Meeting.Voting;
public class MeetingSelectEvent : MiraEvent
{
/// <summary>
/// Gets the instance of the voter's <see cref="PlayerVoteData"/>.
/// Gets the instance of the voter's vote data.
/// </summary>
public PlayerVoteData VoteData { get; }
@@ -18,7 +18,7 @@ public class MeetingSelectEvent : MiraEvent
public int TargetId { get; }
/// <summary>
/// Gets the <see cref="NetworkedPlayerInfo"/> of the target.
/// Gets the player info of the target.
/// </summary>
public NetworkedPlayerInfo TargetPlayerInfo { get; }
@@ -30,9 +30,9 @@ public class MeetingSelectEvent : MiraEvent
/// <summary>
/// Initializes a new instance of the <see cref="MeetingSelectEvent"/> class.
/// </summary>
/// <param name="playerVoteData">The voter's <see cref="PlayerVoteData"/>.</param>
/// <param name="playerVoteData">The voter's data.</param>
/// <param name="targetId">The target's playerId.</param>
/// <param name="allowSelect">Whether the player is allowed to select the target.</param>
/// <param name="allowSelect">>Whether the player is allowed to select the target.</param>
public MeetingSelectEvent(PlayerVoteData playerVoteData, int targetId, bool allowSelect)
{
VoteData = playerVoteData;
@@ -4,20 +4,20 @@ using MiraAPI.Voting;
namespace MiraAPI.Events.Vanilla.Meeting.Voting;
/// <summary>
/// Called in <see cref="VotingUtils.HandlePopulateResults"/>.
/// Called in VotingUtils.HandlePopulateResults.
/// Cancelling is NOT advised but if you do plan on cancelling, please ensure you account for displaying ALL player's votes.
/// </summary>
public class PopulateResultsEvent : MiraCancelableEvent
{
/// <summary>
/// Gets a list of networked <see cref="CustomVote"/>s.
/// Gets a list of networked votes.
/// </summary>
public List<CustomVote> Votes { get; }
/// <summary>
/// Initializes a new instance of the <see cref="PopulateResultsEvent"/> class.
/// </summary>
/// <param name="votes">The list of <see cref="CustomVote"/>s.</param>
/// <param name="votes">The list of votes.</param>
public PopulateResultsEvent(List<CustomVote> votes)
{
Votes = votes;
@@ -10,8 +10,8 @@ namespace MiraAPI.Events.Vanilla.Meeting.Voting;
/// <remarks>
/// Initializes a new instance of the <see cref="ProcessVotesEvent"/> class.
/// </remarks>
/// <param name="votes">The list of <see cref="CustomVote"/>s that are being processed.</param>
/// <param name="exiledPlayer">The player to be exiled. Will be <see langword="null"/> if no player is to be exiled.</param>
/// <param name="votes">>The list of votes that are being processed.</param>
/// <param name="exiledPlayer">The player to be exiled. Will be null if no player is to be exiled.</param>
public class ProcessVotesEvent(List<CustomVote> votes, NetworkedPlayerInfo? exiledPlayer = null) : MiraEvent
{
private readonly List<CustomVote> _originalVotes = [.. votes];
@@ -29,22 +29,12 @@ public class ProcessVotesEvent(List<CustomVote> votes, NetworkedPlayerInfo? exil
public bool VotesModified => !Votes.SequenceEqual(_originalVotes);
/// <summary>
/// Gets the list of <see cref="CustomVote"/>s that are being processed.
/// Gets the list of votes that are being processed.
/// </summary>
public List<CustomVote> Votes { get; } = votes;
/// <summary>
/// Gets or sets a value indicating whether an overruling was done. Normally set by judge but can be overwritten.
/// </summary>
public bool OverruledVote { get; set; }
/// <summary>
/// Gets or sets a value specifically to determine which Judge (if any) caused the overruling. Normally set by judge but can be overwritten.
/// </summary>
public ushort OverruledNonce { get; set; }
/// <summary>
/// Gets or sets the player to be exiled. Will be <see langword="null"/> if no player is to be exiled.
/// Gets or sets the player to be exiled. Will be null if no player is to be exiled.
/// </summary>
public NetworkedPlayerInfo? ExiledPlayer
{
@@ -3,11 +3,11 @@
/// <summary>
/// The event that is invoked when the voting phase of a meeting is complete.
/// </summary>
/// <param name="meetingHud">The <see cref="global::MeetingHud"/> instance.</param>
/// <param name="meetingHud">The MeetingHud instance.</param>
public class VotingCompleteEvent(MeetingHud meetingHud) : MiraEvent
{
/// <summary>
/// Gets the <see cref="global::MeetingHud"/> instance.
/// Gets the MeetingHud instance.
/// </summary>
public MeetingHud MeetingHud { get; } = meetingHud;
}
@@ -1,35 +0,0 @@
namespace MiraAPI.Events.Vanilla.Player;
/// <summary>
/// The event that is invoked when a player completes a task within Hide and Seek. Non-cancelable.
/// </summary>
public class CompleteHnsTaskEvent : MiraEvent
{
/// <summary>
/// Gets the instance of the <see cref="PlayerControl"/>.
/// </summary>
public PlayerControl Player { get; }
/// <summary>
/// Gets the instance of the <see cref="PlayerTask"/> that the player completed.
/// </summary>
public PlayerTask Task { get; }
/// <summary>
/// Gets the instance of the <see cref="HideAndSeekManager"/> attached to the game.
/// </summary>
public HideAndSeekManager HnsManager { get; }
/// <summary>
/// Initializes a new instance of the <see cref="CompleteHnsTaskEvent"/> class.
/// </summary>
/// <param name="hnsManager">The <see cref="HideAndSeekManager"/> currently active.</param>
/// <param name="player">The player who completed the task.</param>
/// <param name="task">The task that the player completed.</param>
public CompleteHnsTaskEvent(HideAndSeekManager hnsManager, PlayerControl player, PlayerTask task)
{
HnsManager = hnsManager;
Player = player;
Task = task;
}
}
@@ -16,7 +16,7 @@ public class PlayerDeathEvent : MiraEvent
public DeathReason DeathReason { get; }
/// <summary>
/// Gets the <see cref="global::DeadBody"/> associated with the player, if any.
/// Gets the dead body associated with the player, if any.
/// </summary>
public DeadBody? DeadBody { get; }
@@ -25,7 +25,7 @@ public class PlayerDeathEvent : MiraEvent
/// </summary>
/// <param name="player">The player who died.</param>
/// <param name="reason">The reason the player died.</param>
/// <param name="deadBody">The player's <see cref="global::DeadBody"/>, if it exists.</param>
/// <param name="deadBody">The player's body, if it exists.</param>
public PlayerDeathEvent(PlayerControl player, DeathReason reason, DeadBody? deadBody)
{
Player = player;
@@ -6,20 +6,20 @@
public class EnterVentEvent : MiraCancelableEvent
{
/// <summary>
/// Gets the <see cref="PlayerControl"/> that is entering the <see cref="Vent"/>.
/// Gets the player that is entering the vent.
/// </summary>
public PlayerControl Player { get; }
/// <summary>
/// Gets the <see cref="global::Vent"/> that the <see cref="Player"/> is entering.
/// Gets the vent that the player is entering.
/// </summary>
public Vent? Vent { get; }
/// <summary>
/// Initializes a new instance of the <see cref="EnterVentEvent"/> class.
/// </summary>
/// <param name="player">The <see cref="PlayerControl"/> that is entering the <paramref name="vent"/>.</param>
/// <param name="vent">The <see cref="global::Vent"/> being entered.</param>
/// <param name="player">The player that is entering the vent.</param>
/// <param name="vent">The vent being entered.</param>
public EnterVentEvent(PlayerControl player, Vent? vent)
{
Player = player;
@@ -6,20 +6,20 @@
public class ExitVentEvent : MiraCancelableEvent
{
/// <summary>
/// Gets the <see cref="PlayerControl"/> that is exiting the <see cref="Vent"/>.
/// Gets the player that is exiting the vent.
/// </summary>
public PlayerControl Player { get; }
/// <summary>
/// Gets the <see cref="global::Vent"/> that the <see cref="Player"/> is exiting.
/// Gets the vent that the player is exiting.
/// </summary>
public Vent? Vent { get; }
/// <summary>
/// Initializes a new instance of the <see cref="ExitVentEvent"/> class.
/// </summary>
/// <param name="player">The <see cref="PlayerControl"/> who is exiting the <paramref name="vent"/>.</param>
/// <param name="vent">The <see cref="global::Vent"/> being exited from.</param>
/// <param name="player">The player who is exiting the vent.</param>
/// <param name="vent">The vent being exited from.</param>
public ExitVentEvent(PlayerControl player, Vent? vent)
{
Player = player;
@@ -8,7 +8,7 @@ public class PlayerCanUseEvent : PlayerUseEvent
/// <summary>
/// Initializes a new instance of the <see cref="PlayerCanUseEvent"/> class.
/// </summary>
/// <param name="usable">The <see cref="IUsable"/>.</param>
/// <param name="usable">The IUsable.</param>
public PlayerCanUseEvent(IUsable usable) : base(usable)
{
}
@@ -11,19 +11,19 @@ public class PlayerUseEvent : MiraCancelableEvent
public IUsable Usable { get; }
/// <summary>
/// Gets a value indicating whether the <see cref="Usable"/> is a <see cref="Console"/>, <see cref="MapConsole"/>, or <see cref="SystemConsole"/>.
/// Gets a value indicating whether the IUsable is a <see cref="Console"/>, <see cref="MapConsole"/>, or <see cref="SystemConsole"/>.
/// </summary>
public bool IsPrimaryConsole { get; }
/// <summary>
/// Gets a value indicating whether the <see cref="Usable"/> is a <see cref="Vent"/>.
/// Gets a value indicating whether the IUsable is a <see cref="Vent"/>.
/// </summary>
public bool IsVent { get; }
/// <summary>
/// Initializes a new instance of the <see cref="PlayerUseEvent"/> class.
/// </summary>
/// <param name="usable">The <see cref="IUsable"/>.</param>
/// <param name="usable">The IUsable.</param>
public PlayerUseEvent(IUsable usable)
{
Usable = usable;
+14 -14
View File
@@ -10,44 +10,44 @@ namespace MiraAPI.GameEnd;
public abstract class CustomGameOver
{
/// <summary>
/// Gets the current <see cref="CustomGameOver"/>.
/// Gets the current CustomGameOver.
/// </summary>
public static CustomGameOver? Instance { get; internal set; }
/// <summary>
/// Verifies if the condition for this <see cref="CustomGameOver"/> is met.
/// Verifies if the condition for this CustomGameOver is met.
/// </summary>
/// <param name="playerControl">The <see cref="PlayerControl"/> that requested the Game Over.</param>
/// <param name="playerControl">The PlayerControl that requested the Game Over.</param>
/// <param name="winners">The collection of winners.</param>
/// <returns><see langword="true"/> if the condition is met, otherwise <see langword="false"/>.</returns>
/// <returns>True if the condition is met, otherwise false.</returns>
public virtual bool VerifyCondition(PlayerControl playerControl, NetworkedPlayerInfo[] winners)
{
return true;
}
/// <summary>
/// Runs before the base game calls <see cref="EndGameManager.SetEverythingUp"/>.
/// Runs before the base game calls EndGameManager.SetEverythingUp.
/// </summary>
/// <param name="endGameManager">The <see cref="EndGameManager"/> instance.</param>
/// <returns>Return <see langword="true"/> to use the run the original method, return <see langword="false"/> to skip it.</returns>
/// <param name="endGameManager">The EndGameManager instance.</param>
/// <returns>Return True to use the run the original method, return False to skip it.</returns>
public virtual bool BeforeEndGameSetup(EndGameManager endGameManager)
{
return true;
}
/// <summary>
/// Runs after the base game calls <see cref="EndGameManager.SetEverythingUp"/>.
/// Runs after the base game calls EndGameManager.SetEverythingUp.
/// </summary>
/// <param name="endGameManager">The <see cref="EndGameManager"/> instance.</param>
/// <param name="endGameManager">The EndGameManager instance.</param>
public virtual void AfterEndGameSetup(EndGameManager endGameManager)
{
}
/// <summary>
/// Get the <see cref="global::GameOverReason"/> associated with this <see cref ="CustomGameOver"/>.
/// Get the GameOverReason associated with this CustomGameOver.
/// </summary>
/// <typeparam name="T">Type of the custom game over.</typeparam>
/// <returns>The <see cref="global::GameOverReason"/> associated with the custom game over.</returns>
/// <returns>The GameOverReason associated with the custom game over.</returns>
public static GameOverReason GameOverReason<T>() where T : CustomGameOver
{
return (GameOverReason)GameOverManager.GetGameOverId<T>();
@@ -67,10 +67,10 @@ public abstract class CustomGameOver
}
/// <summary>
/// Implicitly convert a <see cref="CustomGameOver"/> to a <see cref="global::GameOverReason"/>.
/// Implicitly convert a CustomGameOver to a GameOverReason.
/// </summary>
/// <param name="gameOver">The <see cref="CustomGameOver"/> instance.</param>
/// <returns>The <see cref="global::GameOverReason"/> associated with the CustomGameOver.</returns>
/// <param name="gameOver">The CustomGameOver instance.</param>
/// <returns>The GameOverReason associated with the CustomGameOver.</returns>
public static implicit operator GameOverReason(CustomGameOver gameOver)
{
return (GameOverReason)GameOverManager.GetGameOverId(gameOver.GetType());
+18 -17
View File
@@ -1,11 +1,12 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Reactor.Utilities;
namespace MiraAPI.GameEnd;
/// <summary>
/// Manage <see cref="CustomGameOver"/>s.
/// Manage custom game overs.
/// </summary>
public static class GameOverManager
{
@@ -15,11 +16,11 @@ public static class GameOverManager
private static int _nextId = Enum.GetNames<GameOverReason>().Length;
/// <summary>
/// Register a <see cref="CustomGameOver"/>.
/// Register a custom game over.
/// </summary>
/// <param name="gameOverType">Type of the <see cref="CustomGameOver"/>.</param>
/// <exception cref="ArgumentException">Thrown when the type is not a subclass of <see cref="CustomGameOver"/>, is abstract, or does not have a parameterless constructor.</exception>
/// <returns><see langword="true"/> if the game over was registered successfully, <see langword="false"/> otherwise.</returns>
/// <param name="gameOverType">Type of the custom game over.</param>
/// <exception cref="ArgumentException">Thrown when the type is not a subclass of CustomGameOver, is abstract, or does not have a parameterless constructor.</exception>
/// <returns>>True if the game over was registered successfully, false otherwise.</returns>
public static bool RegisterGameOver(Type gameOverType)
{
if (!typeof(CustomGameOver).IsAssignableFrom(gameOverType))
@@ -29,13 +30,13 @@ public static class GameOverManager
if (gameOverType.IsAbstract)
{
Error("The type must not be abstract.");
Logger<MiraApiPlugin>.Error("The type must not be abstract.");
return false;
}
if (gameOverType.GetConstructor(Type.EmptyTypes) == null)
{
Error("The type must have a parameterless constructor.");
Logger<MiraApiPlugin>.Error("The type must have a parameterless constructor.");
return false;
}
@@ -46,11 +47,11 @@ public static class GameOverManager
}
/// <summary>
/// Create an instance of a <see cref="CustomGameOver"/>.
/// Create an instance of a custom game over.
/// </summary>
/// <param name="id">ID of the <see cref="CustomGameOver"/>.</param>
/// <param name="customGameOver">The created instance of the <see cref="CustomGameOver"/>.</param>
/// <returns>An instance of the <see cref="CustomGameOver"/>.</returns>
/// <param name="id">ID of the custom game over.</param>
/// <param name="customGameOver">The created instance of the custom game over.</param>
/// <returns>An instance of the custom game over.</returns>
public static bool TryGetGameOver(int id, [NotNullWhen(true)] out CustomGameOver? customGameOver)
{
if (GameOverTypes.TryGetValue(id, out var gameOverType))
@@ -64,20 +65,20 @@ public static class GameOverManager
}
/// <summary>
/// Get the ID of a <see cref="CustomGameOver"/>.
/// Get the ID of a custom game over.
/// </summary>
/// <typeparam name="T">Type of the <see cref="CustomGameOver"/>.</typeparam>
/// <returns>The ID of the <see cref="CustomGameOver"/>.</returns>
/// <typeparam name="T">Type of the custom game over.</typeparam>
/// <returns>The ID of the custom game over.</returns>
public static int GetGameOverId<T>() where T : CustomGameOver
{
return GetGameOverId(typeof(T));
}
/// <summary>
/// Get the ID of a <see cref="CustomGameOver"/>.
/// Get the ID of a custom game over.
/// </summary>
/// <param name="gameOverType">Type of the <see cref="CustomGameOver"/>.</param>
/// <returns>The ID of the <see cref="CustomGameOver"/>.</returns>
/// <param name="gameOverType">Type of the custom game over.</param>
/// <returns>The ID of the custom game over.</returns>
/// <exception cref="ArgumentException">Thrown when the type is not registered.</exception>
public static int GetGameOverId(Type gameOverType)
{
-296
View File
@@ -1,296 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using AmongUs.GameOptions;
using MiraAPI.GameOptions;
using MiraAPI.PluginLoading;
using MiraAPI.Utilities.Assets;
using Reactor.Utilities.Extensions;
using TMPro;
using UnityEngine;
namespace MiraAPI.GameModes;
/// <summary>
/// Base class for custom gamemodes.
/// </summary>
[MiraIgnore]
public abstract class AbstractGameMode : IOptionable
{
/// <summary>
/// Gets the game mode name.
/// </summary>
public abstract string Name { get; }
/// <summary>
/// Gets a value indicating whether the mode is visible in the menu. Only checked at startup.
/// </summary>
public virtual bool HideMode => false;
/// <summary>
/// Gets the game mode description.
/// </summary>
public abstract string Description { get; }
/// <summary>
/// Gets the game mode description.
/// </summary>
public virtual Color Color { get; } = Color.white;
/// <summary>
/// Gets the icon of the gamemode. Will be shown in options menu underneath the gamemode option.
/// </summary>
public virtual LoadableAsset<Sprite>? Icon => null;
/// <summary>
/// Gets the icon of the gamemode for use on the options pop-up.
/// </summary>
public TMP_SpriteAsset TmpIcon { get; set; } = null!;
/// <summary>
/// Gets the colored game mode name, using the color and name properties.
/// </summary>
public string ColoredName => $"<color=#{this.Color.ToHtmlStringRGBA()}>{this.Name}</color>";
/// <summary>
/// Gets the game mode id.
/// </summary>
public uint ID { get; internal set; }
/// <summary>
/// Gets a value indicating whether a specific body type is used by the game mode.
/// </summary>
public virtual bool GameModeBodyTypeOverride => false;
/// <summary>
/// Gets a value indicating whether the gamemode removes the normal game settings.
/// </summary>
public virtual bool ShowNormalGameSettings => true;
/// <summary>
/// Gets a value indicating whether the gamemode removes the normal role settings.
/// </summary>
public virtual bool ShowNormalRoleSettings => true;
/// <summary>
/// Gets a value indicating the default kill cooldown for impostors in this mode.
/// </summary>
public virtual float DefaultImpostorKillCooldown => GameOptionsManager.Instance.CurrentGameOptions.GetFloat(FloatOptionNames.KillCooldown);
/// <summary>
/// Called on GameManager.GetBodyType().
/// </summary>
/// <param name="player">Player to get body type from.</param>
/// <returns>Resulting body type.</returns>
public virtual PlayerBodyTypes GetBodyType(PlayerControl player)
{
if (AprilFoolsMode.ShouldHorseAround())
{
return PlayerBodyTypes.Horse;
}
if (AprilFoolsMode.ShouldLongAround())
{
return PlayerBodyTypes.Long;
}
return PlayerBodyTypes.Normal;
}
/// <summary>
/// Gets a value indicating whether a custom intro sequence is implemented by the game mode.
/// </summary>
public virtual bool ShowGameModeIntroCutscene => false;
/// <summary>
/// Called when Intro Cutscene is destroyed.
/// </summary>
public virtual void Initialize()
{
}
/// <summary>
/// Called on HudManager.Start().
/// </summary>
/// <param name="instance">Instance of HudManager.</param>
public virtual void HudStart(HudManager instance)
{
}
/// <summary>
/// Called every frame on HudManager.Update().
/// </summary>
/// <param name="instance">Instance of HudManager.</param>
public virtual void HudUpdate(HudManager instance)
{
}
/// <summary>
/// Called when a player is killed.
/// </summary>
/// <param name="player">PlayerControl that was killed.</param>
/// <param name="assignGhostRole">Determines whether to assign a ghost role on the player.</param>
public virtual void OnPlayerDeath(PlayerControl player, bool assignGhostRole)
{
GameManager.Instance.LogicRoleSelection.OnPlayerDeath(player, assignGhostRole);
}
/// <summary>
/// Calculate Game End logic.
/// </summary>
/// <param name="runOriginal">Should original game end logic be used.</param>
/// <param name="instance">Instance of LogicGameFlowNormal.</param>
public virtual void CheckGameEnd(out bool runOriginal, LogicGameFlowNormal instance)
{
runOriginal = true;
}
/// <summary>
/// Custom role assignment.
/// </summary>
/// <param name="runOriginal">Should original role assignment be used.</param>
/// <param name="instance">Instance of LogicRoleSelection.</param>
public virtual void AssignRoles(out bool runOriginal, LogicRoleSelectionNormal instance)
{
runOriginal = true;
}
/// <summary>
/// Custom role assignment to run after all roles are handed out.
/// </summary>
/// <param name="instance">Instance of LogicRoleSelection.</param>
public virtual void PostAssignRoles(LogicRoleSelectionNormal instance)
{
}
/// <summary>
/// Custom murder logic.
/// </summary>
/// <param name="runOriginal">Should the original murder logic be used.</param>
/// <param name="result">Result of custom murder logic.</param>
/// <param name="target">Target player for murder.</param>
public virtual void CanKill(out bool runOriginal, out bool result, PlayerControl target)
{
result = false;
runOriginal = true;
}
/// <summary>
/// Custom winner selection.
/// </summary>
/// <returns>List of winners or null.</returns>
public virtual List<NetworkedPlayerInfo>? CalculateWinners() => null;
/// <summary>
/// The IEnumerator that plays the intro cutscene for this gamemode.
/// </summary>
/// <param name="__instance">An instance of IntroCutscene.</param>
/// <returns>An IEnumerator to run the intro cutscene instead of the base game one.</returns>
public virtual IEnumerator IntroCutscene(IntroCutscene __instance)
{
yield return new WaitForEndOfFrame();
}
/// <summary>
/// Can Admin be used in this gamemode.
/// </summary>
/// <param name="console">Admin Console.</param>
/// <returns>True if Admin console is enabled.</returns>
public virtual bool CanUseMapConsole(MapConsole console) => true;
/// <summary>
/// Can a body be reported in this gamemode.
/// </summary>
/// <param name="body">Target body for reporting.</param>
/// <returns>True if dead bodies can be reported.</returns>
public virtual bool CanReport(DeadBody body) => true;
/// <summary>
/// Can system consoles be used in this gamemode.
/// </summary>
/// <param name="console">System Console.</param>
/// <returns>True if system consoles are enabled in this mode.</returns>
public virtual bool CanUseSystemConsole(SystemConsole console) => true;
/// <summary>
/// Can tasks be interacted with in this gamemode.
/// </summary>
/// <param name="console">Task console.</param>
/// <returns>True if tasks are enabled in this mode.</returns>
public virtual bool CanUseTasks(Console console) => true;
/// <summary>
/// Should the sabotage map be used when attempting to open sabotage overlay.
/// </summary>
/// <param name="map">MapBehaviour object.</param>
/// <returns>True if the sabotage map should be shown.</returns>
public virtual bool ShouldShowSabotageMap(MapBehaviour map) => true;
/// <summary>
/// Gets the <see cref="MapOptions"/> to display on the map.
/// </summary>
/// <returns>The <see cref="MapOptions"/> for the gamemode.</returns>
public virtual MapOptions GetMapOptions()
{
return new MapOptions
{
Mode = PlayerControl.LocalPlayer.Data.Role.IsImpostor && !MeetingHud.Instance
? MapOptions.Modes.Sabotage
: MapOptions.Modes.Normal,
};
}
/// <summary>
/// Can a player vent in this gamemode.
/// </summary>
/// <param name="vent">Target vent.</param>
/// <param name="playerInfo">Player attempting to vent.</param>
/// <returns>True if venting is enabled in this mode.</returns>
public virtual bool CanVent(Vent vent, NetworkedPlayerInfo playerInfo) => true;
/// <summary>
/// Does the task bar appear in the gamemode.
/// </summary>
/// <returns>True if the task bar is enabled in this mode.</returns>
public virtual bool ShowTaskBar => true;
public virtual void UpdateTaskPanel(TaskPanelBehaviour instance)
{
instance.background.transform.localScale = (instance.taskText.textBounds.size.x > 0f)
? new Vector3(instance.taskText.textBounds.size.x + 0.2f, instance.taskText.textBounds.size.y + 0.2f, 1f)
: Vector3.zero;
Vector3 vector = instance.background.sprite.bounds.extents;
vector.y = -vector.y;
vector = vector.Mul(instance.background.transform.localScale);
instance.background.transform.localPosition = vector;
Vector3 vector2 = instance.tab.sprite.bounds.extents;
vector2 = vector2.Mul(instance.tab.transform.localScale);
vector2.y = -vector2.y;
vector2.x += vector.x * 2f;
instance.tab.transform.localPosition = vector2;
if (GameManager.Instance == null)
{
return;
}
var yPos = 0.6f;
var xPos = -instance.background.sprite.bounds.size.x * instance.background.transform.localScale.x;
instance.closedPosition = new Vector3(xPos, yPos, instance.closedPosition.z);
instance.openPosition = new Vector3(instance.openPosition.x, yPos, instance.openPosition.z);
if (instance.open)
{
instance.timer = Mathf.Min(1f, instance.timer + Time.deltaTime / instance.animationTimeSeconds);
}
else
{
instance.timer = Mathf.Max(0f, instance.timer - Time.deltaTime / instance.animationTimeSeconds);
}
Vector3 relativePos = new(
Mathf.SmoothStep(instance.closedPosition.x, instance.openPosition.x, instance.timer),
yPos,
instance.openPosition.z);
instance.transform.localPosition =
AspectPosition.ComputePosition(AspectPosition.EdgeAlignments.LeftTop, relativePos);
}
/// <inheritdoc/>
public override string ToString() => Name;
}
-20
View File
@@ -1,20 +0,0 @@
using MiraAPI.PluginLoading;
using MiraAPI.Utilities.Assets;
using UnityEngine;
namespace MiraAPI.GameModes;
/// <summary>
/// The classic game mode.
/// </summary>
[MiraIgnore]
public class ClassicMode : AbstractGameMode
{
/// <inheritdoc/>
public override string Name => "Classic";
/// <inheritdoc/>
public override string Description => "The classic Among Us experience!\nFind the Impostors or deceive the Crew.";
public override LoadableAsset<Sprite>? Icon => MiraAssets.ClassicGamemodeIcon;
}
+154
View File
@@ -0,0 +1,154 @@
using System.Collections.Generic;
namespace MiraAPI.GameModes;
/// <summary>
/// Base class for custom gamemodes.
/// </summary>
public abstract class CustomGameMode
{
/// <summary>
/// Gets the game mode name.
/// </summary>
public abstract string Name { get; }
/// <summary>
/// Gets the game mode description.
/// </summary>
public abstract string Description { get; }
/// <summary>
/// Gets the game mode ID.
/// </summary>
public abstract int Id { get; }
/// <summary>
/// Called when Intro Cutscene is destroyed.
/// </summary>
public virtual void Initialize()
{
}
/// <summary>
/// Called on HudManager.Start().
/// </summary>
/// <param name="instance">Instance of HudManager.</param>
public virtual void HudStart(HudManager instance)
{
}
/// <summary>
/// Called every frame on HudManager.Update().
/// </summary>
/// <param name="instance">Instance of HudManager.</param>
public virtual void HudUpdate(HudManager instance)
{
}
/// <summary>
/// Called when a player is killed.
/// </summary>
/// <param name="player">PlayerControl that was killed.</param>
public virtual void OnDeath(PlayerControl player)
{
}
/// <summary>
/// Calculate Game End logic.
/// </summary>
/// <param name="runOriginal">Should original game end logic be used.</param>
/// <param name="instance">Instance of LogicGameFlowNormal.</param>
public virtual void CheckGameEnd(out bool runOriginal, LogicGameFlowNormal instance)
{
runOriginal = true;
}
/// <summary>
/// Custom role assignment.
/// </summary>
/// <param name="runOriginal">Should original role assignment be used.</param>
/// <param name="instance">Instance of LogicRoleSelection.</param>
public virtual void AssignRoles(out bool runOriginal, LogicRoleSelectionNormal instance)
{
runOriginal = true;
}
/// <summary>
/// Custom murder logic.
/// </summary>
/// <param name="runOriginal">Should the original murder logic be used.</param>
/// <param name="result">Result of custom murder logic.</param>
/// <param name="target">Target player for murder.</param>
public virtual void CanKill(out bool runOriginal, out bool result, PlayerControl target)
{
result = false;
runOriginal = true;
}
/// <summary>
/// Should Roles Settings be available when this gamemode is selected.
/// </summary>
/// <returns>True if Role Settings are enabled in this game mode.</returns>
public virtual bool AreRoleSettingsEnabled() => true;
/// <summary>
/// Should Game Settings be available when this gamemode is selected.
/// </summary>
/// <returns>True if Game Settings are enabled in this mode.</returns>
public virtual bool AreGameSettingsEnabled() => true;
/// <summary>
/// Custom winner selection.
/// </summary>
/// <returns>List of winners or null.</returns>
public virtual List<NetworkedPlayerInfo>? CalculateWinners() => null;
/// <summary>
/// Show gamemode in Intro Cutscene.
/// </summary>
/// <returns>True if the game mode should be shown in the intro cutscene.</returns>
public virtual bool ShowGameModeIntroCutscene() => false;
/// <summary>
/// Can Admin be used in this gamemode.
/// </summary>
/// <param name="console">Admin Console.</param>
/// <returns>True if Admin console is enabled.</returns>
public virtual bool CanUseMapConsole(MapConsole console) => true;
/// <summary>
/// Can a body be reported in this gamemode.
/// </summary>
/// <param name="body">Target body for reporting.</param>
/// <returns>True if dead bodies can be reported.</returns>
public virtual bool CanReport(DeadBody body) => true;
/// <summary>
/// Can system consoles be used in this gamemode.
/// </summary>
/// <param name="console">System Console.</param>
/// <returns>True if system consoles are enabled in this mode.</returns>
public virtual bool CanUseSystemConsole(SystemConsole console) => true;
/// <summary>
/// Can tasks be interacted with in this gamemode.
/// </summary>
/// <param name="console">Task console.</param>
/// <returns>True if tasks are enabled in this mode.</returns>
public virtual bool CanUseTasks(Console console) => true;
/// <summary>
/// Should the sabotage map be used when attempting to open sabotage overlay.
/// </summary>
/// <param name="map">MapBehaviour object.</param>
/// <returns>True if the sabotage map should be shown.</returns>
public virtual bool ShouldShowSabotageMap(MapBehaviour map) => true;
/// <summary>
/// Can a player vent in this gamemode.
/// </summary>
/// <param name="vent">Target vent.</param>
/// <param name="playerInfo">Player attempting to vent.</param>
/// <returns>True if venting is enabled in this mode.</returns>
public virtual bool CanVent(Vent vent, NetworkedPlayerInfo playerInfo) => true;
}
+41 -93
View File
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using MiraAPI.PluginLoading;
using Reactor.Utilities;
namespace MiraAPI.GameModes;
@@ -11,113 +10,62 @@ namespace MiraAPI.GameModes;
/// </summary>
public static class CustomGameModeManager
{
internal static readonly Dictionary<uint, AbstractGameMode> IdToModeMap = [];
private static uint GetNextId() => ++LastId;
private static uint LastId { get; set; }
/// <summary>
/// Register gamemode from type.
/// List of registered gamemodes.
/// </summary>
/// <param name="gameModeType">Type of gamemode class, should inherit from <see cref="AbstractGameMode"/>.</param>
/// <param name="pluginInfo">The custom plugin info of the mod.</param>
/// <returns>Whether the gamemode was successfully registered.</returns>
internal static bool RegisterGameMode(Type gameModeType, MiraPluginInfo pluginInfo)
internal static readonly Dictionary<int, CustomGameMode> GameModes = [];
public static bool IsDefault()
{
if (!typeof(AbstractGameMode).IsAssignableFrom(gameModeType))
{
Warning($"{gameModeType.Name} does not inherit CustomGameMode!");
return false;
}
var instance = Activator.CreateInstance(gameModeType);
if (instance is not AbstractGameMode mode)
{
Error($"Failed to create instance of {gameModeType.Name}");
return false;
}
if (!mode.HideMode)
{
IdToModeMap.Add(GetNextId(), mode);
pluginInfo.GameModes.Add(LastId, mode);
mode.ID = LastId;
GameModeOption.AddOption(mode);
}
return true;
return ActiveMode?.Id == 0;
}
/// <summary>
/// Checks to see if the current game mode is an instance of the classic mode.
/// </summary>
/// <returns>True if the classic mode is the current one.</returns>
public static bool IsClassic() => ActiveMode != null && ActiveMode.GetType().IsAssignableTo(typeof(ClassicMode));
/// <summary>
/// Checks to see if the current game mode is an instance of the HNS mode.
/// </summary>
/// <returns>True if the Hide & Seek mode is the current one.</returns>
public static bool IsHideNSeek() => ActiveMode != null && ActiveMode.GetType().IsAssignableTo(typeof(HideAndSeekMode));
/// <summary>
/// Checks if a provided GameMode is the current active one.
/// </summary>
/// <typeparam name="T">The AbstractGameMode subclass being checked.</typeparam>
/// <returns>Whether the provided mode is the current active one.</returns>
public static bool IsActiveGameMode<T>() where T : AbstractGameMode => ActiveMode is T;
/// <summary>
/// Gets the current gamemode.
/// </summary>
public static AbstractGameMode? ActiveMode { get; private set; }
public static CustomGameMode? ActiveMode { get; internal set; } = new DefaultMode();
/// <summary>
/// Gets a gamemode from an ID.
/// Set current gamemode.
/// </summary>
/// <param name="id">The ID of the gamemode to fetch.</param>
/// <returns>The gamemode matching that ID.</returns>
public static AbstractGameMode GetMode(uint id) => IdToModeMap[id];
/// <summary>
/// Finds the parent mod of a custom gamemode.
/// </summary>
/// <param name="gameMode">The <see cref="AbstractGameMode"/> object.</param>
/// <returns>A <see cref="MiraPluginInfo"/> object representing the parent mod of the gamemode.</returns>
public static MiraPluginInfo? FindParentMod(AbstractGameMode gameMode)
/// <param name="id">gamemode ID.</param>
public static void SetGameMode(int id)
{
return MiraPluginManager.Instance.RegisteredPlugins.FirstOrDefault(plugin => plugin.GameModes.ContainsValue(gameMode));
}
internal static void RegisterDefaultMode()
{
var defaultMode = new ClassicMode();
IdToModeMap.Add(0, defaultMode);
// no need to add to game mode option as it already contains it
// because we cannot have the option be created with no values
defaultMode.ID = 0;
var hnsMode = new HideAndSeekMode();
if (!hnsMode.HideMode)
if (GameModes.TryGetValue(id, out var gameMode))
{
IdToModeMap.Add(1, hnsMode);
hnsMode.ID = 1;
GameModeOption.AddOption(hnsMode);
LastId++;
}
}
internal static void GetAndSetGameMode()
{
var id = (uint)GameModeOption.Value;
if (IdToModeMap.TryGetValue(id, out var mode))
{
ActiveMode = mode;
ActiveMode = gameMode;
return;
}
ActiveMode = IdToModeMap[0];
Logger<MiraApiPlugin>.Warning($"Unable to find game mode of id {id}!");
Logger<MiraApiPlugin>.Error($"No gamemode with id {id} found!");
}
/// <summary>
/// Register gamemode from type.
/// </summary>
/// <param name="gameModeType">Type of gamemode class, should inherit from <see cref="CustomGameMode"/>.</param>
internal static void RegisterGameMode(Type gameModeType)
{
if (!typeof(CustomGameMode).IsAssignableFrom(gameModeType))
{
Logger<MiraApiPlugin>.Warning($"{gameModeType.Name} does not inherit CustomGameMode!");
return;
}
var modeObj = Activator.CreateInstance(gameModeType);
if (modeObj is not CustomGameMode gameMode)
{
Logger<MiraApiPlugin>.Error($"Failed to create instance of {gameModeType.Name}");
return;
}
if (GameModes.Any(x => x.Key == gameMode.Id))
{
Logger<MiraApiPlugin>.Error($"ID for gamemode {gameMode.Name} already exists!");
return;
}
GameModes.Add(gameMode.Id, gameMode);
}
}
+8
View File
@@ -0,0 +1,8 @@
namespace MiraAPI.GameModes;
public class DefaultMode : CustomGameMode
{
public override string Name => "Default";
public override string Description => "Default Among Us GameMode";
public override int Id => 0;
}
-160
View File
@@ -1,160 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using MiraAPI.GameOptions;
using MiraAPI.Networking;
using MiraAPI.Patches.GameModes;
using MiraAPI.Patches.Options;
using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets;
using Reactor.Localization.Utilities;
using Reactor.Networking.Attributes;
using Reactor.Utilities.Extensions;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.ProBuilder;
using Object = UnityEngine.Object;
namespace MiraAPI.GameModes;
/// <summary>
/// The game mode option.
/// </summary>
[HarmonyPatch]
public static class GameModeOption
{
/// <summary>
/// Gets the current index of the Game Mode Option
/// For the value as an <see cref="AbstractGameMode"/>, see <see cref="CustomGameModeManager.ActiveMode"/>.
/// </summary>
public static int Value
{
get =>
OptionBehaviour != null
? OptionBehaviour.GetInt()
: _lastValue;
private set
{
_lastValue = value;
if (OptionBehaviour == null)
return;
OptionBehaviour.Value = value;
OptionBehaviour.UpdateValue();
_lastValue = value;
}
}
internal static StringOption OptionBehaviour { get; set; } = null!;
internal static int _lastValue;
internal static readonly StringNames GamemodeName = CustomStringName.CreateAndRegister("Gamemode");
internal static readonly StringNames CustomName = CustomStringName.CreateAndRegister("Custom");
internal static readonly Dictionary<uint, StringNames> Values = new()
{
[0] = CustomStringName.CreateAndRegister("Classic"),
};
internal static void AddOption(AbstractGameMode mode)
{
if (!Values.ContainsKey(mode.ID))
Values.Add(mode.ID, CustomStringName.CreateAndRegister(mode.ColoredName));
}
/*[HarmonyPatch(typeof(GameOptionsMenu), nameof(GameOptionsMenu.CreateSettings))]
[HarmonyPostfix]
private static void CreateSettingsPatch(GameOptionsMenu __instance)
{
if (MenuState.Instance.CurrentModIdx != 0 || GameManager.Instance.IsHideAndSeek() || CustomGameModeManager.ActiveMode == null)
{
return;
}
var num = 0.713f;
foreach (var category in __instance.settingsContainer.GetComponentsInChildren<CategoryHeaderMasked>())
{
if (category)
category.gameObject.transform.localPosition -= new Vector3(0, 1.3f, 0);
}
CategoryHeaderMasked categoryHeaderMasked = Object.Instantiate(__instance.categoryHeaderOrigin, Vector3.zero, Quaternion.identity, __instance.settingsContainer);
categoryHeaderMasked.SetHeader(CustomName, 20);
categoryHeaderMasked.transform.localScale = Vector3.one * 0.63f;
categoryHeaderMasked.transform.localPosition = new Vector3(-0.903f, num, -2f);
OptionBehaviour = Object.Instantiate(
__instance.stringOptionOrigin,
Vector3.zero,
Quaternion.identity,
__instance.settingsContainer);
num -= 0.63f;
OptionBehaviour.transform.localPosition = new Vector3(0.952f, num, -2f);
OptionBehaviour.SetClickMask(__instance.ButtonClickMask);
StringGameSetting setting = ScriptableObject.CreateInstance<StringGameSetting>();
setting.Type = OptionTypes.MultipleChoice;
setting.Title = GamemodeName;
setting.Index = _lastValue;
setting.Values = new Il2CppStructArray<StringNames>([Values[0]]);
OptionBehaviour.SetUpFromData(setting, 20);
Set(_lastValue);
OptionBehaviour.TitleText.fontSize = 3;
foreach (var optionBehaviour in __instance.Children.ToArray().Skip(1))
{
optionBehaviour.gameObject.transform.localPosition -= new Vector3(0, 1.3f, 0);
}
__instance.Children.Add(OptionBehaviour);
for (var i = 1; i < Values.Count; i++)
OptionBehaviour.Values = (Il2CppStructArray<StringNames>)OptionBehaviour.Values.Add(Values.ElementAt(i).Value);
__instance.scrollBar.SetYBoundsMax(__instance.scrollBar.GetYBounds().max + 1);
}*/
internal static void Set(int val)
{
Value = val;
var previousMode = CustomGameModeManager.ActiveMode;
CustomGameModeManager.GetAndSetGameMode();
HudPatches.SetGameModeText(CustomGameModeManager.GetMode(Values.ElementAt(_lastValue).Key).ColoredName);
var gm = CustomGameModeManager.ActiveMode!;
if (gm != previousMode)
{
ModdedOptionsManager.AddSettingsChangeMessage(
HudManager.Instance.Notifier,
GamemodeName,
gm.ColoredName,
new Color(0.7333f, 0.7333f, 0.7333f, 1),
gm.TmpIcon,
true);
if (MenuState.Instance)
{
// should force reset roles for gamemodes properly
foreach (var roleMenu in MenuState.Instance.QueuedRoleMenuRefresh)
{
MenuState.Instance.QueuedRoleMenuRefresh[roleMenu.Key] = true;
}
}
}
// could make Values a dict of AbstractGameMode too
}
[HarmonyPatch(typeof(GameOptionsMenu), nameof(GameOptionsMenu.ValueChanged))]
[HarmonyPrefix]
private static bool ValueChanged(GameOptionsMenu __instance, OptionBehaviour option)
{
if (OptionBehaviour.Equals(option))
{
Info($"Game mode changed to {option.GetInt()}");
RpcSyncGamemode(PlayerControl.LocalPlayer, option.GetInt());
if (GameSettingMenu.Instance && CustomGameModeManager.ActiveMode != null)
{
GameOptionsMenuPatch.ToggleGamemodeOptions(CustomGameModeManager.ActiveMode, __instance);
GameSettingMenu.Instance.RoleSettingsButton.gameObject.SetActive(CustomGameModeManager.ActiveMode.ShowNormalRoleSettings);
}
return false;
}
return true;
}
[MethodRpc((uint) MiraRpc.SyncGamemodeOption)]
internal static void RpcSyncGamemode(PlayerControl host, int data)
{
Set(data);
}
}
-534
View File
@@ -1,534 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using AmongUs.Data;
using AmongUs.GameOptions;
using HarmonyLib;
using InnerNet;
using MiraAPI.GameOptions;
using MiraAPI.HnsReimplemented;
using MiraAPI.HnsReimplemented.Options;
using MiraAPI.PluginLoading;
using MiraAPI.Roles;
using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets;
using PowerTools;
using UnityEngine;
namespace MiraAPI.GameModes;
/// <summary>
/// The vanilla Hide and Seek game mode, ported to Mira.
/// </summary>
[MiraIgnore]
public class HideAndSeekMode : AbstractGameMode
{
/// <inheritdoc/>
public override string Name => "Hide n Seek";
/// <inheritdoc/>
public override string Description => "You can run, but you can't hide!\nOutlive the timer or eliminate the crew.";
// TODO: Once the Hide n Seek port is ready for release, we will remove this.
public override bool HideMode => !MiraApiPlugin.IsDevBuild;
public override Color Color { get; } = new Color32(255, 88, 90, 255);
public override LoadableAsset<Sprite>? Icon => MiraAssets.HnSGamemodeIcon;
public override bool CanReport(DeadBody body) => false;
public override bool ShouldShowSabotageMap(MapBehaviour map) => false;
public override bool ShowGameModeIntroCutscene => true;
public override bool GameModeBodyTypeOverride => true;
public override bool ShowNormalGameSettings => false;
public override bool ShowNormalRoleSettings => false;
public override float DefaultImpostorKillCooldown => 1f;
public override bool ShowTaskBar => false;
public static int ImpostorPlayerID()
{
return OptionGroupSingleton<HnsImpostorOptions>.Instance.SelectedSeeker.Value;
}
public static bool HasImpostorPlayerID()
{
return ImpostorPlayerID() > -1;
}
public static bool ValidateImpostorPlayerID(List<NetworkedPlayerInfo> players)
{
return HasImpostorPlayerID() && players.Find((NetworkedPlayerInfo p) => (int)p.PlayerId == ImpostorPlayerID()) != null;
}
public override void AssignRoles(out bool runOriginal, LogicRoleSelectionNormal instance)
{
runOriginal = false;
Il2CppSystem.Collections.Generic.List<ClientData> list = new();
AmongUsClient.Instance.GetAllClients(list);
List<NetworkedPlayerInfo> list2 = list.ToArray()
.Where(c => c.Character != null && c.Character.Data != null && !c.Character.Data.Disconnected &&
!c.Character.Data.IsDead).OrderBy(c => c.Id).Select(c => c.Character.Data)
.ToList();
foreach (NetworkedPlayerInfo networkedPlayerInfo in GameData.Instance.AllPlayers)
{
if (networkedPlayerInfo.Object != null && networkedPlayerInfo.Object.isDummy)
{
list2.Add(networkedPlayerInfo);
}
}
IGameOptions currentGameOptions = GameOptionsManager.Instance.CurrentGameOptions;
int adjustedNumImpostors = GameOptionsManager.Instance.CurrentGameOptions.GetAdjustedNumImpostors(list2.Count);
AssignRolesForTeam(list2, currentGameOptions, RoleTeamTypes.Impostor, Math.Max(adjustedNumImpostors, 1), RoleTypes.Impostor);
AssignRolesForTeam(list2, currentGameOptions, RoleTeamTypes.Crewmate, int.MaxValue, RoleTypes.Engineer);
}
public static void AssignRolesForTeam(
List<NetworkedPlayerInfo> players,
IGameOptions opts,
RoleTeamTypes team,
int teamMax,
RoleTypes defaultRole)
{
Error($"Hide And Seek Mode - AssignRolesForTeam: Team: {team}, Max: {teamMax}, Players: {players.Count}, DefaultRole: {defaultRole}");
int num = 0;
IRoleOptionsCollection roleOptions = opts.RoleOptions;
var source = RoleManager.Instance.AllRoles.ToArray()
.Where(role => role.TeamType == team && !RoleManager.IsGhostRole(role.Role) &&
CustomRoleUtils.CanSpawnOnCurrentMode(role));
var assignmentData = source.Where(x => !x.IsDead).Select(role =>
new RoleManager.RoleAssignmentData(
role,
roleOptions.GetNumPerGame(role.Role),
roleOptions.GetChancePerGame(role.Role))).ToList();
var source2 = CustomRoleUtils.GetPossibleRoles(assignmentData, x => x.Chance == 100);
var guaranteedRoles = source.Where(x => source2.Contains(((ushort)x.Role, 100)));
List<RoleTypes> list = new List<RoleTypes>();
if (team == RoleTeamTypes.Crewmate)
{
Error($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: Before Guaranteed Assignment");
foreach (RoleManager.RoleAssignmentData roleAssignmentData in guaranteedRoles.Select((x) =>
new RoleManager.RoleAssignmentData(x, roleOptions.GetNumPerGame(x.Role), 100)))
{
while (true)
{
RoleManager.RoleAssignmentData roleAssignmentData2 = roleAssignmentData;
int count = roleAssignmentData2.Count;
roleAssignmentData2.Count = count - 1;
if (count <= 0)
{
break;
}
list.Add(roleAssignmentData.Role.Role);
}
}
Error($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: After Guaranteed Assignment");
AssignRolesFromList(players, teamMax, list, ref num);
var list2 = source.Where(x => !x.IsDead).Select(role =>
new RoleManager.RoleAssignmentData(
role,
roleOptions.GetNumPerGame(role.Role),
roleOptions.GetChancePerGame(role.Role))).ToList();
list.Clear();
foreach (RoleManager.RoleAssignmentData roleAssignmentData3 in list2)
{
for (int i = 0; i < roleAssignmentData3.Count; i++)
{
if (HashRandom.Next(101) < roleAssignmentData3.Chance)
{
list.Add(roleAssignmentData3.Role.Role);
}
}
}
Error($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: After Potential Assignment");
AssignRolesFromList(players, teamMax, list, ref num);
var basicRole = RoleTypes.Engineer;
while (list.Count < players.Count && list.Count + num < teamMax)
{
list.Add(basicRole);
}
AssignRolesFromList(players, teamMax, list, ref num);
Error($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: After Fallback Assignment");
}
else if (team == RoleTeamTypes.Impostor)
{
Error($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: Before Guaranteed Assignment");
var newImpostors = new List<NetworkedPlayerInfo>();
// Specified Seeker
if (HasImpostorPlayerID() &&
ValidateImpostorPlayerID(players) &&
!AmongUsClient.Instance.IsGamePublic)
{
NetworkedPlayerInfo networkedPlayerInfo = players.ToArray()
.First(p => p.PlayerId == ImpostorPlayerID());
players.Remove(networkedPlayerInfo);
newImpostors.Add(networkedPlayerInfo);
Error($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: Seeker is {networkedPlayerInfo.PlayerName}, ID: {networkedPlayerInfo.PlayerId}");
}
// Random Seeker
else
{
int num2 = 0;
while (num2 < teamMax && players.Count > 0)
{
PseudoRandomList<NetworkedPlayerInfo> pseudoRandomList = new PseudoRandomList<NetworkedPlayerInfo>(AmongUsClient.Instance.GameId);
players.Do(x => pseudoRandomList.Add(x));
for (int i = 0; i < GameData.RoundsPlayedInSession; i++)
{
pseudoRandomList.PickRandom();
}
NetworkedPlayerInfo networkedPlayerInfo = pseudoRandomList.PickRandom();
players.Remove(networkedPlayerInfo);
newImpostors.Add(networkedPlayerInfo);
num2++;
Error($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: Seeker is {networkedPlayerInfo.PlayerName}, ID: {networkedPlayerInfo.PlayerId}");
}
}
Error($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: After Guaranteed Assignment");
foreach (RoleManager.RoleAssignmentData roleAssignmentData in guaranteedRoles.Select((x) =>
new RoleManager.RoleAssignmentData(x, roleOptions.GetNumPerGame(x.Role), 100)))
{
while (true)
{
RoleManager.RoleAssignmentData roleAssignmentData2 = roleAssignmentData;
int count = roleAssignmentData2.Count;
roleAssignmentData2.Count = count - 1;
if (count <= 0)
{
break;
}
list.Add(roleAssignmentData.Role.Role);
}
}
AssignRolesFromList(newImpostors, teamMax, list, ref num);
var list2 = source.Where(x => !x.IsDead).Select(role =>
new RoleManager.RoleAssignmentData(
role,
roleOptions.GetNumPerGame(role.Role),
roleOptions.GetChancePerGame(role.Role))).ToList();
list.Clear();
foreach (RoleManager.RoleAssignmentData roleAssignmentData3 in list2)
{
for (int i = 0; i < roleAssignmentData3.Count; i++)
{
if (HashRandom.Next(101) < roleAssignmentData3.Chance)
{
list.Add(roleAssignmentData3.Role.Role);
}
}
}
AssignRolesFromList(newImpostors, teamMax, list, ref num);
var basicRole = RoleTypes.Impostor;
while (list.Count < newImpostors.Count && list.Count + num < teamMax)
{
list.Add(basicRole);
}
AssignRolesFromList(newImpostors, teamMax, list, ref num);
}
}
public static void AssignRolesFromList(List<NetworkedPlayerInfo> players, int teamMax, List<RoleTypes> roleList, ref int rolesAssigned)
{
while (roleList.Count > 0 && players.Count > 0 && rolesAssigned < teamMax)
{
int index = HashRandom.FastNext(roleList.Count);
RoleTypes roleType = roleList[index];
roleList.RemoveAt(index);
int index2 = HashRandom.FastNext(players.Count);
players[index2].Object.RpcSetRole(roleType, false);
players.RemoveAt(index2);
rolesAssigned++;
}
}
public override IEnumerator IntroCutscene(IntroCutscene __instance)
{
SoundManager.Instance.PlaySound(__instance.IntroStinger, false, 1f, null);
Logger.GlobalInstance.Info("IntroCutscene :: CoBegin() :: Game Mode: Hide and Seek (MiraAPI)", null);
__instance.LogPlayerRoleData();
__instance.HideAndSeekPanels.SetActive(true);
if (PlayerControl.LocalPlayer.Data.Role.IsImpostor)
{
__instance.CrewmateRules.SetActive(false);
__instance.ImpostorRules.SetActive(true);
}
else
{
__instance.CrewmateRules.SetActive(true);
__instance.ImpostorRules.SetActive(false);
}
__instance.ImpostorName.gameObject.SetActive(true);
__instance.ImpostorTitle.gameObject.SetActive(true);
__instance.BackgroundBar.enabled = false;
__instance.TeamTitle.gameObject.SetActive(false);
var impostor = PlayerControl.AllPlayerControls.ToArray().FirstOrDefault(x => x.Data.Role.IsImpostor);
if (impostor == null)
{
Logger.GlobalInstance.Error("IntroCutscene :: CoBegin() :: impostor is NULL", null);
}
GameManager.Instance.SetSpecialCosmetics(impostor);
if (impostor != null)
{
__instance.ImpostorName.text = impostor.Data.PlayerName;
}
else
{
__instance.ImpostorName.text = "???";
}
yield return new WaitForSecondsRealtime(0.1f);
if (impostor != null)
{
__instance.ImpostorTitle.text = impostor.Data.Role.GetRoleName();
}
PoolablePlayer playerSlot = null;
if (impostor != null)
{
playerSlot = __instance.CreatePlayer(1, 1, impostor.Data, false);
playerSlot.SetBodyType(PlayerBodyTypes.Normal);
playerSlot.SetFlipX(false);
playerSlot.transform.localPosition = __instance.impostorPos;
playerSlot.transform.localScale = Vector3.one * __instance.impostorScale;
}
yield return ShipStatus.Instance.CosmeticsCache.PopulateFromPlayers();
yield return new WaitForSecondsRealtime(6f);
if (playerSlot != null)
{
playerSlot.gameObject.SetActive(false);
}
__instance.HideAndSeekPanels.SetActive(false);
__instance.CrewmateRules.SetActive(false);
__instance.ImpostorRules.SetActive(false);
HnsMusicHandler.Instance.StartMusicWithIntro();
var hideTimer = 10f;
if (PlayerControl.LocalPlayer.Data.Role.IsImpostor)
{
__instance.HideAndSeekTimerText.gameObject.SetActive(true);
PoolablePlayer poolablePlayer;
AnimationClip anim;
if (AprilFoolsMode.ShouldHorseAround())
{
poolablePlayer = __instance.HorseWrangleVisualSuit;
poolablePlayer.gameObject.SetActive(true);
poolablePlayer.SetBodyType(PlayerBodyTypes.Seeker);
anim = __instance.HnSSeekerSpawnHorseAnim;
__instance.HorseWrangleVisualPlayer.SetBodyType(PlayerBodyTypes.Normal);
__instance.HorseWrangleVisualPlayer.UpdateFromPlayerData(
PlayerControl.LocalPlayer.Data,
PlayerControl.LocalPlayer.CurrentOutfitType,
PlayerMaterial.MaskType.None,
false,
null,
false);
}
else if (AprilFoolsMode.ShouldLongAround())
{
poolablePlayer = __instance.HideAndSeekPlayerVisual;
poolablePlayer.gameObject.SetActive(true);
poolablePlayer.SetBodyType(PlayerBodyTypes.LongSeeker);
anim = __instance.HnSSeekerSpawnLongAnim;
}
else
{
poolablePlayer = __instance.HideAndSeekPlayerVisual;
poolablePlayer.gameObject.SetActive(true);
poolablePlayer.SetBodyType(PlayerBodyTypes.Seeker);
anim = __instance.HnSSeekerSpawnAnim;
}
poolablePlayer.SetBodyCosmeticsVisible(false);
poolablePlayer.UpdateFromPlayerData(
PlayerControl.LocalPlayer.Data,
PlayerControl.LocalPlayer.CurrentOutfitType,
PlayerMaterial.MaskType.None,
false,
null,
false);
SpriteAnim component = poolablePlayer.GetComponent<SpriteAnim>();
poolablePlayer.gameObject.SetActive(true);
poolablePlayer.ToggleName(false);
component.Play(anim, 1f);
while (hideTimer > 0f)
{
__instance.HideAndSeekTimerText.text = Mathf.RoundToInt(hideTimer).ToString();
hideTimer -= Time.deltaTime;
yield return null;
}
}
else
{
HideAndSeekHudHelper.Instance.HideCountdown = hideTimer;
if (AprilFoolsMode.ShouldHorseAround())
{
if (impostor != null)
{
impostor.AnimateCustom(__instance.HnSSeekerSpawnHorseInGameAnim);
}
}
else if (AprilFoolsMode.ShouldLongAround())
{
if (impostor != null)
{
impostor.AnimateCustom(__instance.HnSSeekerSpawnLongInGameAnim);
}
}
else if (impostor != null)
{
impostor.AnimateCustom(__instance.HnSSeekerSpawnAnim);
impostor.cosmetics.SetBodyCosmeticsVisible(false);
}
}
ShipStatus.Instance.StartSFX();
HnsMusicHandler.Instance.OnGameStart();
HnsDangerMeter.Instance.OnGameStart();
UnityEngine.Object.Destroy(__instance.gameObject);
}
public override void Initialize()
{
deadPlayerCount = 0;
ShipStatus.Instance.BreakEmergencyButton();
PlayerControl.LocalPlayer.SetKillTimer(0.01f);
if (HudManager.InstanceExists)
{
HudManager.Instance.gameObject.AddComponent<HnsMusicHandler>();
HudManager.Instance.gameObject.AddComponent<HideAndSeekHudHelper>();
HudManager.Instance.gameObject.AddComponent<HnsDangerMeter>();
}
}
public override MapOptions GetMapOptions()
{
MapOptions mapOptions = new MapOptions
{
Mode = MapOptions.Modes.Normal,
};
if (PlayerControl.LocalPlayer.Data.Role.IsImpostor && HideAndSeekHudHelper.Instance.SeekerAdminMapEnabled(PlayerControl.LocalPlayer))
{
mapOptions.Mode = MapOptions.Modes.CountOverlay;
mapOptions.AllowMovementWhileMapOpen = true;
mapOptions.IncludeDeadBodies = false;
mapOptions.ShowLivePlayerPosition = false;
}
return mapOptions;
}
public override void CheckGameEnd(out bool runOriginal, LogicGameFlowNormal instance)
{
runOriginal = false;
if (Helpers.GetAlivePlayers().Count(x => x.Data.Role.IsImpostor) == 0)
{
instance.Manager.RpcEndGame(GameOverReason.ImpostorDisconnect, !DataManager.Player.Ads.HasPurchasedAdRemoval);
}
if (Helpers.GetAlivePlayers().Count(x => !x.Data.Role.IsImpostor) != 0)
{
if (HideAndSeekHudHelper.Instance.AllTimersExpired())
{
instance.Manager.RpcEndGame(GameOverReason.HideAndSeek_CrewmatesByTimer, !DataManager.Player.Ads.HasPurchasedAdRemoval);
}
return;
}
instance.Manager.RpcEndGame(GameOverReason.HideAndSeek_ImpostorsByKills, !DataManager.Player.Ads.HasPurchasedAdRemoval);
}
public override PlayerBodyTypes GetBodyType(PlayerControl player)
{
if (player == null || player.Data == null || player.Data.Role == null)
{
if (AprilFoolsMode.ShouldHorseAround())
{
return PlayerBodyTypes.Horse;
}
if (AprilFoolsMode.ShouldLongAround())
{
return PlayerBodyTypes.Long;
}
return PlayerBodyTypes.Normal;
}
else if (AprilFoolsMode.ShouldHorseAround())
{
if (player.Data.Role.IsImpostor)
{
return PlayerBodyTypes.Normal;
}
return PlayerBodyTypes.Horse;
}
else if (AprilFoolsMode.ShouldLongAround())
{
if (player.Data.Role.IsImpostor)
{
return PlayerBodyTypes.LongSeeker;
}
return PlayerBodyTypes.Long;
}
else
{
if (player.Data.Role.IsImpostor)
{
return PlayerBodyTypes.Seeker;
}
return PlayerBodyTypes.Normal;
}
}
public override void UpdateTaskPanel(TaskPanelBehaviour instance)
{
instance.background.transform.localScale = (instance.taskText.textBounds.size.x > 0f)
? new Vector3(instance.taskText.textBounds.size.x + 0.2f, instance.taskText.textBounds.size.y + 0.2f, 1f)
: Vector3.zero;
Vector3 vector = instance.background.sprite.bounds.extents;
vector.y = -vector.y;
vector = vector.Mul(instance.background.transform.localScale);
instance.background.transform.localPosition = vector;
Vector3 vector2 = instance.tab.sprite.bounds.extents;
vector2 = vector2.Mul(instance.tab.transform.localScale);
vector2.y = -vector2.y;
vector2.x += vector.x * 2f;
instance.tab.transform.localPosition = vector2;
if (GameManager.Instance == null)
{
return;
}
var yPos = 1.6f;
var xPos = -instance.background.sprite.bounds.size.x * instance.background.transform.localScale.x;
instance.closedPosition = new Vector3(xPos, yPos, instance.closedPosition.z);
instance.openPosition = new Vector3(instance.openPosition.x, yPos, instance.openPosition.z);
if (instance.open)
{
instance.timer = Mathf.Min(1f, instance.timer + Time.deltaTime / instance.animationTimeSeconds);
}
else
{
instance.timer = Mathf.Max(0f, instance.timer - Time.deltaTime / instance.animationTimeSeconds);
}
Vector3 relativePos = new(
Mathf.SmoothStep(instance.closedPosition.x, instance.openPosition.x, instance.timer),
yPos,
instance.openPosition.z);
instance.transform.localPosition =
AspectPosition.ComputePosition(AspectPosition.EdgeAlignments.LeftTop, relativePos);
}
private int deadPlayerCount;
public override void OnPlayerDeath(PlayerControl player, bool assignGhostRole)
{
base.OnPlayerDeath(player, assignGhostRole);
HudManager.Instance.NotifyOfDeath();
var popup = GameManagerCreator.Instance.HideAndSeekManagerPrefab.DeathPopupPrefab;
deadPlayerCount++;
var item = UnityEngine.Object.Instantiate(popup, HudManager.Instance.transform.parent);
item.Show(player, deadPlayerCount);
}
}
+2 -101
View File
@@ -1,9 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using MiraAPI.Modifiers;
using MiraAPI.Roles;
using UnityEngine;
namespace MiraAPI.GameOptions;
@@ -34,42 +31,27 @@ public abstract class AbstractOptionGroup
/// Gets a value indicating whether the group should be shown in the modifiers menu.
/// </summary>
// TODO: make this not a boolean
#pragma warning disable S1133
[Obsolete("Use ParentMenu instead.")]
#pragma warning restore S1133
public virtual bool ShowInModifiersMenu => false;
/// <summary>
/// Gets a value indicating which menu the group is in.
/// </summary>
public virtual MenuCategory ParentMenu => MenuCategory.Game;
/// <summary>
/// Gets the function that determines whether the group should be visible or not.
/// </summary>
public virtual Func<bool> GroupVisible => () => true;
/// <summary>
/// Gets the group <see cref="Color"/>. This is used to color the group in the options menu.
/// Gets the group color. This is used to color the group in the options menu.
/// </summary>
public virtual Color GroupColor => MiraApiPlugin.DefaultHeaderColor;
/// <summary>
/// Gets the group priority. This is used to determine the order in which groups are displayed in the options menu.
/// Zero is the highest priority, and the default value is the max <see langword="uint"/> value.
/// Zero is the highest priority, and the default value is the max uint value.
/// </summary>
public virtual uint GroupPriority => uint.MaxValue;
/// <summary>
/// Gets the default notification settings for the group.
/// </summary>
public virtual OptionNotifConfiguration Configuration => new(new Color(0.7333f, 0.7333f, 0.7333f, 1));
internal bool AllOptionsHidden { get; set; }
internal CategoryHeaderMasked? Header { get; set; }
internal bool Ready { get; set; }
}
/// <summary>
@@ -80,85 +62,4 @@ public abstract class AbstractOptionGroup<T> : AbstractOptionGroup where T : IOp
{
/// <inheritdoc />
public override Type OptionableType => typeof(T);
/// <inheritdoc />
public override MenuCategory ParentMenu
{
get
{
var type = typeof(T);
if (type == typeof(BaseModifier))
{
return MenuCategory.Modifiers;
}
else if (type == typeof(RoleBehaviour))
{
return MenuCategory.Roles;
}
return MenuCategory.Game;
}
}
}
/// <summary>
/// Base class for option groups. An option group is a collection of options that are displayed together in the options menu.
/// </summary>
/// <typeparam name="T">The custom role that the group is for.</typeparam>
public abstract class AbstractRoleOptionGroup<T>() : AbstractOptionGroup<T> where T : ICustomRole
{
/// <inheritdoc />
public override Type OptionableType => typeof(T);
/// <inheritdoc />
public override MenuCategory ParentMenu => MenuCategory.Roles;
/// <inheritdoc />
public override OptionNotifConfiguration Configuration
{
get
{
var role = CustomRoleManager.CustomMiraRoles.FirstOrDefault(x => x.GetType() == OptionableType);
if (role == null)
{
return new(new Color(0.7333f, 0.7333f, 0.7333f, 1));
}
return new(role.RoleColor, role.Configuration.IconTmp);
}
}
}
/// <summary>
/// Menu categories for option groups.
/// </summary>
public enum MenuCategory
{
/// <summary>
/// Placeholder for indexing purposes. Options don't exist in the presset tab.
/// </summary>
Preset,
/// <summary>
/// Determines the option group is in the game settings tab.
/// </summary>
Game,
/// <summary>
/// Determines the option group is in the role settings tab.
/// </summary>
Roles,
/// <summary>
/// Determines the option group is in the modifier settings tab.
/// </summary>
Modifiers,
/// <summary>
/// Determines the option group is in the first custom settings tab.
/// </summary>
CustomOne,
/// <summary>
/// Determines the option group is in the second custom settings tab.
/// </summary>
CustomTwo,
}
@@ -5,7 +5,7 @@ using MiraAPI.GameOptions.OptionTypes;
namespace MiraAPI.GameOptions.Attributes;
/// <summary>
/// Attribute for creating a <see cref="ModdedEnumOption"/>.
/// Attribute for creating an enum option.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class ModdedEnumOptionAttribute(string title, Type enumType, string[]? values = null)
@@ -6,7 +6,7 @@ using MiraAPI.Utilities;
namespace MiraAPI.GameOptions.Attributes;
/// <summary>
/// Attribute for a <see cref="ModdedNumberOption"/>.
/// A number option attribute for the modded options system.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class ModdedNumberOptionAttribute(
@@ -21,15 +21,7 @@ public class ModdedNumberOptionAttribute(
{
internal override IModdedOption CreateOption(object? value, PropertyInfo property)
{
return new ModdedNumberOption(
Title,
(float)(value ?? min + increment),
min,
max,
increment,
suffixType,
formatString,
zeroInfinity);
return new ModdedNumberOption(Title, (float)(value ?? min+increment), min, max, increment, suffixType, formatString, zeroInfinity);
}
/// <inheritdoc />
@@ -4,13 +4,12 @@ using System.Reflection;
namespace MiraAPI.GameOptions.Attributes;
/// <summary>
/// Represents an attribute that is used to define an <see cref="IModdedOption"/>.
/// Represents an attribute that is used to define a modded option.
/// </summary>
/// <param name="title">The option title.</param>
/// <param name="roleType">Optional parameter to specify a role Type.</param>
/// <param name="modeType">Optional parameter to specify a game mode Type.</param>
[AttributeUsage(AttributeTargets.Property)]
public abstract class ModdedOptionAttribute(string title, Type? roleType = null, Type? modeType = null) : Attribute
public abstract class ModdedOptionAttribute(string title, Type? roleType = null) : Attribute
{
internal IModdedOption? HolderOption { get; set; }
@@ -24,21 +23,16 @@ public abstract class ModdedOptionAttribute(string title, Type? roleType = null,
/// </summary>
protected Type? RoleType { get; private set; } = roleType;
/// <summary>
/// Gets the game mode type of the option.
/// </summary>
protected Type? ModeType { get; private set; } = modeType;
/// <summary>
/// Sets the value of the option.
/// </summary>
/// <param name="value">The new value as an <see langword="object"/>.</param>
/// <param name="value">The new value as an object.</param>
public abstract void SetValue(object value);
/// <summary>
/// Gets the value of the option.
/// </summary>
/// <returns>The value of the option as an <see langword="object"/>.</returns>
/// <returns>The value of the option as an object.</returns>
public abstract object GetValue();
internal abstract IModdedOption? CreateOption(object? value, PropertyInfo property);
@@ -5,7 +5,7 @@ using MiraAPI.GameOptions.OptionTypes;
namespace MiraAPI.GameOptions.Attributes;
/// <summary>
/// Attribute for a <see cref="ModdedToggleOption"/>.
/// Attribute for a toggle option.
/// </summary>
/// <param name="title">The option title.</param>
[AttributeUsage(AttributeTargets.Property)]
+21 -31
View File
@@ -17,37 +17,27 @@ public interface IModdedOption
uint Id { get; }
/// <summary>
/// Gets or sets the title of the option.
/// Gets the title of the option.
/// </summary>
string Title { get; set; }
string Title { get; }
/// <summary>
/// Gets or sets the parent <see cref="AbstractOptionGroup"/> of the option.
/// </summary>
AbstractOptionGroup ParentGroup { get; set; }
/// <summary>
/// Gets the notification settings for the option.
/// </summary>
OptionNotifConfiguration Configuration { get; }
/// <summary>
/// Gets the <see cref="StringNames"/> for the option, used for localization.
/// Gets the StringName for the option, used for localization.
/// </summary>
StringNames StringName { get; }
/// <summary>
/// Gets or sets the <see cref="IMiraPlugin"/> that created this option.
/// Gets or sets the MiraPlugin that created this option.
/// </summary>
IMiraPlugin? ParentMod { get; set; }
/// <summary>
/// Gets the <see cref="BaseGameSetting"/> for the option.
/// Gets the game setting data for the option.
/// </summary>
BaseGameSetting Data { get; }
/// <summary>
/// Gets the <see cref="global::OptionBehaviour"/> object of the option.
/// Gets the OptionBehaviour object of the option.
/// </summary>
OptionBehaviour? OptionBehaviour { get; }
@@ -62,55 +52,55 @@ public interface IModdedOption
bool IncludeInPreset { get; set; }
/// <summary>
/// Gets or sets the <see cref="BepInEx.Configuration.ConfigDefinition"/> for the option, used for BepInEx configuration.
/// Gets or sets the ConfigDefinition for the option, used for BepInEx configuration.
/// </summary>
ConfigDefinition? ConfigDefinition { get; set; }
/// <summary>
/// Creates the option behaviour for the modded option.
/// </summary>
/// <param name="toggleOpt">The <see cref="ToggleOption"/> template.</param>
/// <param name="numberOpt">The <see cref="NumberOption"/> template.</param>
/// <param name="stringOpt">The <see cref="StringOption"/> template.</param>
/// <param name="playerOpt">The <see cref="PlayerOption"/> template.</param>
/// <param name="container">The <see cref="Transform"/> container for the option.</param>
/// <returns>The created <see cref="global::OptionBehaviour"/> object.</returns>
/// <param name="toggleOpt">The ToggleOption template.</param>
/// <param name="numberOpt">The NumberOption template.</param>
/// <param name="stringOpt">The StringOption template.</param>
/// <param name="playerOpt">The PlayerOption template.</param>
/// <param name="container">>The Transform container for the option.</param>
/// <returns>>The created OptionBehaviour object.</returns>
OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, PlayerOption playerOpt, Transform container);
/// <summary>
/// Gets the value as a <see langword="float"/>.
/// Gets the value as a float.
/// </summary>
/// <returns>The value of the option as a <see langword="float"/>.</returns>
/// <returns>>The value of the option as a float.</returns>
float GetFloatData();
/// <summary>
/// Gets the <see cref="NetData"/> for the option, used for network synchronization.
/// Gets the NetData for the option, used for network synchronization.
/// </summary>
/// <returns>Returns the <see cref="NetData"/> object for the option.</returns>
/// <returns>>Returns the NetData object for the option.</returns>
NetData GetNetData();
/// <summary>
/// Handles incoming network data for the option.
/// </summary>
/// <param name="data">The byte array representing the network data.</param>
/// <param name="data">>The byte array representing the network data.</param>
void HandleNetData(byte[] data);
/// <summary>
/// Saves the option to a preset configuration file.
/// </summary>
/// <param name="presetConfig">The <see cref="ConfigFile"/> representing the preset configuration.</param>
/// <param name="presetConfig">The ConfigFile representing the preset configuration.</param>
/// <param name="saveDefault">Indicates whether to save the default value instead of the current value.</param>
void SaveToPreset(ConfigFile presetConfig, bool saveDefault = false);
/// <summary>
/// Binds the option to a configuration file.
/// </summary>
/// <param name="config">The <see cref="ConfigFile"/> to bind the option to.</param>
/// <param name="config">The ConfigFile to bind the option to.</param>
void Bind(ConfigFile config);
/// <summary>
/// Loads the option from a preset configuration file, applying the values to the option's configuration.
/// </summary>
/// <param name="presetConfig">The <see cref="ConfigFile"/> representing the preset configuration.</param>
/// <param name="presetConfig">The ConfigFile representing the preset configuration.</param>
void LoadFromPreset(ConfigFile presetConfig);
}
@@ -1,40 +0,0 @@
using System;
using Il2CppInterop.Runtime.Attributes;
using MiraAPI.GameOptions.OptionTypes;
using Reactor.Utilities.Attributes;
using UnityEngine;
namespace MiraAPI.GameOptions;
/// <summary>
/// Component added onto options generated via MiraAPI. This allows developers to hook patches onto options in general, without using a list.
/// </summary>
[RegisterInIl2Cpp]
public class MiraNumberOptionComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
{
/// <summary>
/// Gets or sets the <see cref="ModdedNumberOption"/> associated with the object.
/// </summary>
[HideFromIl2Cpp]
public ModdedNumberOption NumberOption { get; set; }
/// <summary>
/// Gets or sets the default increment.
/// </summary>
public float DefaultIncrement { get; set; }
/// <summary>
/// Gets or sets a value indicating whether holding shift will split the increment in half.
/// </summary>
public bool ShiftIncrementToggle { get; set; }
/// <summary>
/// Gets or sets a value indicating what Zero displays.
/// </summary>
public string ZeroValue { get; set; } = "#";
/// <summary>
/// Gets or sets a value indicating what Negative One displays.
/// </summary>
public string NegativeValue { get; set; } = "#";
}
+8 -79
View File
@@ -5,8 +5,6 @@ using System.Linq;
using System.Reflection;
using BepInEx.Configuration;
using HarmonyLib;
using Il2CppInterop.Runtime.Injection;
using MiraAPI.GameModes;
using MiraAPI.GameOptions.Attributes;
using MiraAPI.GameOptions.OptionTypes;
using MiraAPI.Networking;
@@ -14,13 +12,11 @@ using MiraAPI.PluginLoading;
using MiraAPI.Utilities;
using Reactor.Networking.Rpc;
using Reactor.Utilities;
using TMPro;
using UnityEngine;
namespace MiraAPI.GameOptions;
/// <summary>
/// Handles <see cref="IModdedOption"/>s.
/// Handles modded options.
/// </summary>
public static class ModdedOptionsManager
{
@@ -28,45 +24,13 @@ public static class ModdedOptionsManager
private static readonly Dictionary<Type, AbstractOptionGroup> TypeToGroup = [];
internal static readonly Dictionary<OptionBehaviour, ModdedPlayerOption> CreatedPlayerOptions = [];
internal static readonly Dictionary<OptionBehaviour, ModdedStringOption> CreatedStringOptions = [];
internal static readonly Dictionary<Type, List<AbstractOptionGroup>> GameModeOptionGroups = [];
internal static readonly Dictionary<uint, IModdedOption> ModdedOptions = [];
internal static readonly List<AbstractOptionGroup> Groups = [];
internal static uint NextId => _nextId++;
private static uint _nextId = 1;
public static void AddSettingsChangeMessage(NotificationPopper notif, StringNames key, string value, Color textColor, TMP_SpriteAsset? sprite, bool playSound = true)
{
string item;
var text = textColor.ToTextColor();
if (sprite != null)
{
item = TranslationController.Instance.GetString(
StringNames.LobbyChangeSettingNotification,
string.Concat(
"<sprite name=\"",
sprite.name,
"\"><font=\"Barlow-Black SDF\" material=\"Barlow-Black Outline\">",
text,
TranslationController.Instance.GetString(key),
"</color></font>"),
"<font=\"Barlow-Black SDF\" material=\"Barlow-Black Outline\">" + value + "</font>"
);
}
else
{
item = TranslationController.Instance.GetString(
StringNames.LobbyChangeSettingNotification,
"<font=\"Barlow-Black SDF\" material=\"Barlow-Black Outline\">" +
text +
TranslationController.Instance.GetString(key) + "</color></font>",
"<font=\"Barlow-Black SDF\" material=\"Barlow-Black Outline\">" + value + "</font>");
}
notif.SettingsChangeMessageLogic(key, item, playSound);
}
internal static bool RegisterGroup(Type type)
internal static bool RegisterGroup(Type type, MiraPluginInfo pluginInfo)
{
if (Activator.CreateInstance(type) is not AbstractOptionGroup group)
{
@@ -81,40 +45,6 @@ public static class ModdedOptionsManager
Groups.Add(group);
TypeToGroup.Add(type, group);
typeof(OptionGroupSingleton<>).MakeGenericType(type)
.GetField("_instance", BindingFlags.Static | BindingFlags.NonPublic)!
.SetValue(null, group);
return true;
}
internal static bool RegisterGroup(Type type, MiraPluginInfo pluginInfo)
{
if (Activator.CreateInstance(type) is not AbstractOptionGroup group)
{
return false;
}
if (TypeToGroup.ContainsKey(type))
{
Error($"Group {type.Name} already exists.");
return false;
}
Groups.Add(group);
TypeToGroup.Add(type, group);
if (group.OptionableType?.IsAssignableTo(typeof(AbstractGameMode)) == true)
{
if (GameModeOptionGroups.TryGetValue(group.OptionableType, out var oldList))
{
oldList.Add(group);
}
else
{
GameModeOptionGroups.Add(group.OptionableType, new List<AbstractOptionGroup>() { group });
}
}
pluginInfo.InternalOptionGroups.Add(group);
typeof(OptionGroupSingleton<>).MakeGenericType(type)
@@ -130,13 +60,13 @@ public static class ModdedOptionsManager
{
if (!TypeToGroup.TryGetValue(type, out var group))
{
Error($"Failed to get group for {type.Name}");
Logger<MiraApiPlugin>.Error($"Failed to get group for {type.Name}");
return;
}
if (property.GetValue(group) is not IModdedOption option)
{
Error($"Failed to get option for {property.Name}");
Logger<MiraApiPlugin>.Error($"Failed to get option for {property.Name}");
return;
}
@@ -151,13 +81,13 @@ public static class ModdedOptionsManager
{
if (OptionAttributes.ContainsKey(property))
{
Error($"Property {property.Name} already has an attribute registered.");
Logger<MiraApiPlugin>.Error($"Property {property.Name} already has an attribute registered.");
return;
}
if (!TypeToGroup.TryGetValue(type, out var group))
{
Error($"Failed to get group for {type.Name}");
Logger<MiraApiPlugin>.Error($"Failed to get group for {type.Name}");
return;
}
@@ -165,7 +95,7 @@ public static class ModdedOptionsManager
if (option == null)
{
Error($"Failed to get option for {property.Name}");
Logger<MiraApiPlugin>.Error($"Failed to get option for {property.Name}");
return;
}
@@ -193,7 +123,6 @@ public static class ModdedOptionsManager
option.ConfigDefinition = new ConfigDefinition(groupName, propertyName);
option.ParentGroup = group;
option.ParentMod = pluginInfo.MiraPlugin;
pluginInfo.InternalOptions.Add(option);
ModdedOptions.Add(option.Id, option);
@@ -263,7 +192,7 @@ public static class ModdedOptionsManager
/// </summary>
/// <param name="__originalMethod">The original getter method.</param>
/// <param name="__result">The result of the property getter.</param>
/// <returns><see langword="false"/> so the original getter gets skipped.</returns>
/// <returns>False so the original getter gets skipped.</returns>
#pragma warning disable CA1707
[SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Harmony naming convention")]
public static bool PropertyGetterPatch(MethodBase __originalMethod, ref object __result)
@@ -1,10 +1,8 @@
using System.Linq;
using MiraAPI.PluginLoading;
namespace MiraAPI.GameOptions;
#pragma warning disable CA1852
[MiraIgnore]
internal class ModifierOptionGroup : AbstractOptionGroup
#pragma warning restore CA1852
{
+2 -2
View File
@@ -3,7 +3,7 @@
namespace MiraAPI.GameOptions;
/// <summary>
/// Singleton for <see cref="AbstractOptionGroup"/>s.
/// Singleton for option groups.
/// </summary>
/// <typeparam name="T">The option group type.</typeparam>
public static class OptionGroupSingleton<T> where T : AbstractOptionGroup
@@ -11,7 +11,7 @@ public static class OptionGroupSingleton<T> where T : AbstractOptionGroup
private static T? _instance;
/// <summary>
/// Gets the instance of the <typeparamref name="T"/> group.
/// Gets the instance of the option group.
/// </summary>
#pragma warning disable CA1000
public static T Instance => _instance ??= ModdedOptionsManager.Groups.OfType<T>().Single();
@@ -1,53 +0,0 @@
using System;
using Il2CppInterop.Runtime.Attributes;
using TMPro;
using UnityEngine;
namespace MiraAPI.GameOptions;
/// <summary>
/// Used to configure the specific visuals for option notifications.
/// </summary>
public record struct OptionNotifConfiguration
{
#pragma warning disable S1133
[Obsolete("Default constructor is not supported")]
#pragma warning restore S1133
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public OptionNotifConfiguration()
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
{
throw new NotImplementedException("Default constructor is not supported.");
}
/// <summary>
/// Initializes a new instance of the <see cref="OptionNotifConfiguration"/> struct based on a group.
/// </summary>
/// <param name="group">The <see cref="AbstractOptionGroup"/> in which you are configuring.</param>
public OptionNotifConfiguration(AbstractOptionGroup group)
{
PopUpIconTmp = group.Configuration.PopUpIconTmp;
PopUpTextColor = group.Configuration.PopUpTextColor;
}
/// <summary>
/// Initializes a new instance of the <see cref="OptionNotifConfiguration"/> struct from scratch.
/// </summary>
/// <param name="color">The text <see cref="Color"/> for the configuration.</param>
/// <param name="asset">The <see cref="TMP_SpriteAsset"/> icon for the configuration.</param>
public OptionNotifConfiguration(Color color, TMP_SpriteAsset asset = null!)
{
PopUpIconTmp = asset;
PopUpTextColor = color;
}
/// <summary>
/// Gets or sets the <see cref="TMP_SpriteAsset"/> for the icon on the options pop-up.
/// </summary>
public TMP_SpriteAsset PopUpIconTmp { get; set; }
/// <summary>
/// Gets or sets the <see cref="Color"/> for the text on the options pop-up.
/// </summary>
[HideFromIl2Cpp]
public Color PopUpTextColor { get; set; }
}
@@ -11,7 +11,7 @@ using Object = UnityEngine.Object;
namespace MiraAPI.GameOptions.OptionTypes;
/// <summary>
/// An option for selecting an <see langword="enum"/> value.
/// An option for selecting an enum value.
/// </summary>
public class ModdedEnumOption : ModdedOption<int>
{
@@ -25,8 +25,8 @@ public class ModdedEnumOption : ModdedOption<int>
/// </summary>
/// <param name="title">The title of the option.</param>
/// <param name="defaultValue">The default value as an int.</param>
/// <param name="enumType">The <see cref="Enum"/> type.</param>
/// <param name="values">An option list of string values to use in place of the <see langword="enum"/> name.</param>
/// <param name="enumType">The Enum type.</param>
/// <param name="values">An option list of string values to use in place of the enum name.</param>
/// <param name="includeInPreset">Whether to include this option in the preset or not.</param>
public ModdedEnumOption(string title, int defaultValue, Type enumType, string[]? values = null, bool includeInPreset=true) : base(title, defaultValue, includeInPreset)
{
@@ -47,8 +47,6 @@ public class ModdedEnumOption : ModdedOption<int>
public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, PlayerOption playerOpt, Transform container)
{
var stringOption = Object.Instantiate(stringOpt, container);
stringOption.name =
$"{ParentMod!.OptionsTitleText}.EnumOption.{TranslationController.Instance.GetString(StringName)}";
stringOption.SetUpFromData(Data, 20);
stringOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged;
@@ -90,13 +88,7 @@ public class ModdedEnumOption : ModdedOption<int>
/// <inheritdoc />
protected override void OnValueChanged(int newValue)
{
ModdedOptionsManager.AddSettingsChangeMessage(
HudManager.Instance.Notifier,
StringName,
Data.GetValueString(newValue),
Configuration.PopUpTextColor,
Configuration.PopUpIconTmp,
false);
HudManager.Instance.Notifier.AddSettingsChangeMessage(StringName, Data.GetValueString(newValue), false);
if (!OptionBehaviour)
{
return;
@@ -110,13 +102,13 @@ public class ModdedEnumOption : ModdedOption<int>
}
/// <summary>
/// An option for selecting an <see langword="enum"/> value.
/// An option for selecting an enum value.
/// </summary>
/// <typeparam name="T">The <see cref="Enum"/> type.</typeparam>
/// <typeparam name="T">The enum type.</typeparam>
public class ModdedEnumOption<T> : ModdedOption<T> where T : Enum
{
/// <summary>
/// Gets the string values of the <see langword="enum"/>.
/// Gets the string values of the enum.
/// </summary>
public string[] Values { get; }
@@ -124,8 +116,8 @@ public class ModdedEnumOption<T> : ModdedOption<T> where T : Enum
/// Initializes a new instance of the <see cref="ModdedEnumOption{T}"/> class.
/// </summary>
/// <param name="title">The title of the option.</param>
/// <param name="defaultValue">The default value as an <see langword="int"/>.</param>
/// <param name="values">An option list of string values to use in place of the <see langword="enum"/> name.</param>
/// <param name="defaultValue">The default value as an int.</param>
/// <param name="values">An option list of string values to use in place of the enum name.</param>
/// <param name="includeInPreset">Whether to include this option in the preset or not.</param>
public ModdedEnumOption(string title, T defaultValue, string[]? values = null, bool includeInPreset=true) : base(title, defaultValue, includeInPreset)
{
@@ -146,8 +138,6 @@ public class ModdedEnumOption<T> : ModdedOption<T> where T : Enum
public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, PlayerOption playerOpt, Transform container)
{
var stringOption = Object.Instantiate(stringOpt, container);
stringOption.name =
$"{ParentMod!.OptionsTitleText}.EnumOption.{TranslationController.Instance.GetString(StringName)}";
stringOption.SetUpFromData(Data, 20);
stringOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged;
@@ -189,13 +179,7 @@ public class ModdedEnumOption<T> : ModdedOption<T> where T : Enum
/// <inheritdoc />
protected override void OnValueChanged(T newValue)
{
ModdedOptionsManager.AddSettingsChangeMessage(
HudManager.Instance.Notifier,
StringName,
Data.GetValueString(Convert.ToInt32(newValue, NumberFormatInfo.InvariantInfo)),
Configuration.PopUpTextColor,
Configuration.PopUpIconTmp,
false);
HudManager.Instance.Notifier.AddSettingsChangeMessage(StringName, Data.GetValueString(Convert.ToInt32(newValue, NumberFormatInfo.InvariantInfo)), false);
if (!OptionBehaviour)
{
return;
@@ -32,82 +32,10 @@ public class ModdedNumberOption : ModdedOption<float>
/// </summary>
public MiraNumberSuffixes SuffixType { get; }
/// <summary>
/// Gets a value indicating what zero appears as.
/// </summary>
public string ZeroWordValue { get; }
/// <summary>
/// Gets a value indicating what a negative one appears as.
/// </summary>
public string NegativeWordValue { get; }
/// <summary>
/// Gets a value indicating whether zero is infinity.
/// </summary>
public bool ZeroInfinity => ZeroWordValue == "∞";
/// <summary>
/// Gets a value indicating whether holding shift will divide the increment.
/// </summary>
public bool ShiftIncrement { get; }
/// <summary>
/// Initializes a new instance of the <see cref="ModdedNumberOption"/> class.
/// </summary>
/// <param name="title">The title of the option.</param>
/// <param name="defaultValue">The default value as a float.</param>
/// <param name="min">The minimum value.</param>
/// <param name="max">The maximum value.</param>
/// <param name="increment">The increment.</param>
/// <param name="suffixType">The suffix type.</param>
/// <param name="formatString">Optional format string for the option screen.</param>
/// <param name="zeroBehavior">Determines what is shown for zero. Options: ∞, #, or a word value (such as Off).</param>
/// <param name="negativeBehavior">Determines what is shown for negative 1. Options: ∞, #, or a word value (such as Off).</param>
/// <param name="halfIncrements">Whether increments can be split in half.</param>
/// <param name="includeInPreset">Whether to include this option in the preset or not.</param>
public ModdedNumberOption(
string title,
float defaultValue,
float min,
float max,
float increment,
string zeroBehavior,
string negativeBehavior,
MiraNumberSuffixes suffixType,
string? formatString = null,
bool halfIncrements = false,
bool includeInPreset = true) : base(title, defaultValue, includeInPreset)
{
Min = min;
Max = max;
Increment = increment;
SuffixType = suffixType;
ZeroWordValue = zeroBehavior;
NegativeWordValue = negativeBehavior;
ShiftIncrement = halfIncrements;
Value = Mathf.Clamp(defaultValue, min, max);
Data = ScriptableObject.CreateInstance<FloatGameSetting>();
var data = (FloatGameSetting)Data;
data.Type = global::OptionTypes.Float;
data.Title = StringName;
data.Value = Value;
data.Increment = Increment;
data.ValidRange = new FloatRange(Min, Max);
data.FormatString = formatString ??
(defaultValue.IsInteger() &&
Increment.IsInteger() &&
Value.IsInteger() &&
Min.IsInteger() &&
Max.IsInteger() ? "0" : "0.0");
data.ZeroIsInfinity = ZeroInfinity;
data.SuffixType = (NumberSuffixes)SuffixType;
data.OptionName = FloatOptionNames.Invalid;
}
public bool ZeroInfinity { get; }
/// <summary>
/// Initializes a new instance of the <see cref="ModdedNumberOption"/> class.
@@ -136,9 +64,7 @@ public class ModdedNumberOption : ModdedOption<float>
Max = max;
Increment = increment;
SuffixType = suffixType;
ZeroWordValue = zeroInfinity ? "∞" : "#";
NegativeWordValue = "#";
ShiftIncrement = increment != 1f;
ZeroInfinity = zeroInfinity;
Value = Mathf.Clamp(defaultValue, min, max);
@@ -171,14 +97,6 @@ public class ModdedNumberOption : ModdedOption<float>
Transform container)
{
var numberOption = Object.Instantiate(numberOpt, Vector3.zero, Quaternion.identity, container);
numberOption.name =
$"{ParentMod!.OptionsTitleText}.NumberOption.{TranslationController.Instance.GetString(StringName)}";
var optionComponent = numberOption.gameObject.AddComponent<MiraNumberOptionComponent>();
optionComponent.NumberOption = this;
optionComponent.DefaultIncrement = Increment;
optionComponent.ShiftIncrementToggle = ShiftIncrement;
optionComponent.NegativeValue = NegativeWordValue;
optionComponent.ZeroValue = ZeroWordValue;
numberOption.SetUpFromData(Data, 20);
numberOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged;
@@ -225,12 +143,9 @@ public class ModdedNumberOption : ModdedOption<float>
protected override void OnValueChanged(float newValue)
{
Value = Mathf.Clamp(newValue, Min, Max);
ModdedOptionsManager.AddSettingsChangeMessage(
HudManager.Instance.Notifier,
HudManager.Instance.Notifier.AddSettingsChangeMessage(
StringName,
Data.GetValueString(newValue),
Configuration.PopUpTextColor,
Configuration.PopUpIconTmp,
Data.GetValueString(Value),
false);
if (OptionBehaviour is NumberOption opt)
@@ -240,7 +155,7 @@ public class ModdedNumberOption : ModdedOption<float>
}
/// <summary>
/// Implicitly converts the option to an <see langword="int"/>.
/// Implicitly converts the option to an int.
/// </summary>
/// <param name="option">The option.</param>
/// <returns>Integer value.</returns>
@@ -1,9 +1,10 @@
using System;
using System;
using BepInEx.Configuration;
using MiraAPI.Networking;
using MiraAPI.PluginLoading;
using Reactor.Localization.Utilities;
using Reactor.Networking.Rpc;
using Reactor.Utilities;
using UnityEngine;
namespace MiraAPI.GameOptions.OptionTypes;
@@ -20,7 +21,7 @@ public abstract class ModdedOption<T> : IModdedOption
public uint Id { get; }
/// <inheritdoc />
public string Title { get; set; }
public string Title { get; }
/// <inheritdoc />
public StringNames StringName { get; }
@@ -124,7 +125,7 @@ public abstract class ModdedOption<T> : IModdedOption
{
if (ConfigDefinition is null)
{
Error($"Attempted to save {Title} to preset, but ConfigDefinition is null.");
Logger<MiraApiPlugin>.Error($"Attempted to save {Title} to preset, but ConfigDefinition is null.");
return;
}
Bind(presetConfig);
@@ -146,7 +147,7 @@ public abstract class ModdedOption<T> : IModdedOption
}
else
{
Error($"Attempted to load {Title} from preset, but ConfigDefinition is not found in preset.");
Logger<MiraApiPlugin>.Error($"Attempted to load {Title} from preset, but ConfigDefinition is not found in preset.");
}
}
@@ -189,10 +190,4 @@ public abstract class ModdedOption<T> : IModdedOption
{
return option.Value;
}
/// <inheritdoc />
public AbstractOptionGroup ParentGroup { get; set; }
/// <inheritdoc />
public virtual OptionNotifConfiguration Configuration => new(ParentGroup);
}
@@ -9,7 +9,7 @@ using Object = UnityEngine.Object;
namespace MiraAPI.GameOptions.OptionTypes;
/// <summary>
/// An option for selecting an ingame player. It returns an index of the values list, NOT A PLAYER ID. To get a player out of this, index the <see cref="Values"/> list.
/// An option for selecting an ingame player. It returns an index of the values list, NOT A PLAYER ID. To get a player out of this, index the Values list.
/// </summary>
public class ModdedPlayerOption : ModdedOption<int>
{
@@ -55,8 +55,6 @@ public class ModdedPlayerOption : ModdedOption<int>
Transform container)
{
var playerOption = Object.Instantiate(playerOpt, container);
playerOption.name =
$"{ParentMod!.OptionsTitleText}.PlayerOption.{TranslationController.Instance.GetString(StringName)}";
var filteredList = GetFilteredPlayers();
Values = filteredList.ToIl2CppList();
@@ -91,9 +89,9 @@ public class ModdedPlayerOption : ModdedOption<int>
}
/// <summary>
/// Returns a player from the value. If <see cref="AllowNone"/> is enabled and none is enabled this will return <see langword="null"/>.
/// Returns a player from the value. If AllowNone is enabled and none is enabled this will return null.
/// </summary>
/// <returns>A player or <see langword="null"/> if chosen none.</returns>
/// <returns>A player or null if chosen none.</returns>
public NetworkedPlayerInfo? GetPlayerValue()
{
return Value < 0 ? null : Values[Value];
@@ -126,13 +124,7 @@ public class ModdedPlayerOption : ModdedOption<int>
/// <inheritdoc />
protected override void OnValueChanged(int newValue)
{
ModdedOptionsManager.AddSettingsChangeMessage(
HudManager.Instance.Notifier,
StringName,
Data.GetValueString(newValue),
Configuration.PopUpTextColor,
Configuration.PopUpIconTmp,
false);
HudManager.Instance.Notifier.AddSettingsChangeMessage(StringName, Data.GetValueString(newValue), false);
if (!OptionBehaviour)
{
return;
@@ -1,176 +0,0 @@
using System;
using System.Globalization;
using System.Linq;
using System.Text;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using MiraAPI.Networking;
using Reactor.Localization.Utilities;
using UnityEngine;
using Object = UnityEngine.Object;
namespace MiraAPI.GameOptions.OptionTypes;
/// <summary>
/// An option for selecting an enum value.
/// </summary>
public class ModdedStringOption : ModdedOption<string>
{
/// <summary>
/// Gets or sets the string values of the option.
/// </summary>
public string[] Values { get; set; }
/// <summary>
/// Gets or sets the String Name values of the option.
/// </summary>
public StringNames[] StringNameValues { get; set; }
/// <summary>
/// Gets the string's index.
/// </summary>
/// <param name="value">The option's string value.</param>
/// <returns>The option's value in the index.</returns>
public int GetIndex(string value, int location)
{
if (Values.Contains(value))
{
return Values.ToList().IndexOf(value);
}
Info($"Cannot find a valid index for {value} in {location}. Returning 0 instead. Values: {string.Join(", ", Values)}");
return 0;
}
/// <summary>
/// Adds a value to the option.
/// </summary>
/// <param name="value">The option's string value.</param>
/// <param name="index">The additional value's index on the list.</param>
public void RegisterExtraValue(string value, int index = -1)
{
if (!Values.Contains(value))
{
if (index <= -1)
index = Values.Length;
var newVals = Values.ToList();
newVals.Insert(index, value);
Values = newVals.ToArray();
var newStrVals = StringNameValues.ToList();
newStrVals.Insert(index, CustomStringName.CreateAndRegister(value));
StringNameValues = newStrVals.ToArray();
}
if (Data != null)
{
var data = (StringGameSetting)Data;
data.Values = StringNameValues;
data.Index = GetIndex(Value, 1);
if (OptionBehaviour != null)
{
var strOpt = OptionBehaviour.TryCast<StringOption>();
if (strOpt != null)
{
strOpt.Values = StringNameValues;
strOpt.Value = GetIndex(Value, 2);
Warning("StringOption is valid");
}
else
{
Error("StringOption is null.");
}
Warning("OptionBehaviour is valid");
}
else
{
Error("OptionBehaviour is null.");
}
Warning("Data is valid");
}
else
{
Error("Data is null.");
}
}
/// <summary>
/// Initializes a new instance of the <see cref="ModdedStringOption"/> class.
/// </summary>
/// <param name="title">The title of the option.</param>
/// <param name="defaultValue">The default value as a string.</param>
/// <param name="values">An option list of string values to use.</param>
/// <param name="includeInPreset">Whether to include this option in the preset or not.</param>
public ModdedStringOption(string title, string defaultValue, string[] values, bool includeInPreset=true) : base(title, defaultValue, includeInPreset)
{
Values = values;
Data = ScriptableObject.CreateInstance<StringGameSetting>();
var data = (StringGameSetting)Data;
data.Title = StringName;
data.Type = global::OptionTypes.String;
StringNameValues = values.Select(CustomStringName.CreateAndRegister).ToArray();
data.Values = StringNameValues;
data.Index = GetIndex(Value, 3);
}
/// <inheritdoc />
public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, PlayerOption playerOpt, Transform container)
{
var stringOption = Object.Instantiate(stringOpt, container);
stringOption.name =
$"{ParentMod!.OptionsTitleText}.StringOption.{TranslationController.Instance.GetString(StringName)}";
stringOption.SetUpFromData(Data, 20);
stringOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged;
// SetUpFromData method doesnt work correctly so we must set the values manually
stringOption.Title = StringName;
stringOption.Values = (Data as StringGameSetting)?.Values ?? new Il2CppStructArray<StringNames>(0);
stringOption.Value = GetIndex(Value, 4);
OptionBehaviour = stringOption;
ModdedOptionsManager.CreatedStringOptions.TryAdd(stringOption, this);
return stringOption;
}
/// <inheritdoc />
public override float GetFloatData()
{
return Array.IndexOf(Values, Value);
}
/// <inheritdoc />
public override NetData GetNetData()
{
return new NetData(Id, Encoding.Unicode.GetBytes(Convert.ToString(Value, NumberFormatInfo.InvariantInfo)));
}
/// <inheritdoc />
public override void HandleNetData(byte[] data)
{
SetValue(Encoding.Unicode.GetString(data));
}
/// <inheritdoc />
public override string GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour)
{
return Values[optionBehaviour.GetInt()];
}
/// <inheritdoc />
protected override void OnValueChanged(string newValue)
{
HudManager.Instance.Notifier.AddSettingsChangeMessage(StringName, Data.GetValueString(GetIndex(newValue, 5)), false);
if (!OptionBehaviour)
{
return;
}
if (OptionBehaviour is StringOption opt)
{
opt.Value = GetIndex(newValue, 5);
}
}
}
@@ -29,8 +29,6 @@ public class ModdedToggleOption : ModdedOption<bool>
public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, PlayerOption playerOpt, Transform container)
{
var toggleOption = Object.Instantiate(toggleOpt, Vector3.zero, Quaternion.identity, container);
toggleOption.name =
$"{ParentMod!.OptionsTitleText}.ToggleOption.{TranslationController.Instance.GetString(StringName)}";
toggleOption.SetUpFromData(Data, 20);
@@ -71,13 +69,7 @@ public class ModdedToggleOption : ModdedOption<bool>
/// <inheritdoc />
protected override void OnValueChanged(bool newValue)
{
ModdedOptionsManager.AddSettingsChangeMessage(
HudManager.Instance.Notifier,
StringName,
newValue ? "On" : "Off",
Configuration.PopUpTextColor,
Configuration.PopUpIconTmp,
false);
HudManager.Instance.Notifier.AddSettingsChangeMessage(StringName, newValue ? "On" : "Off", false);
if (!OptionBehaviour)
{
return;
@@ -1,285 +0,0 @@
using System.Collections;
using System.Linq;
using BepInEx.Unity.IL2CPP.Utils.Collections;
using MiraAPI.GameOptions;
using MiraAPI.HnsReimplemented.Options;
using MiraAPI.Utilities;
using Reactor.Utilities.Attributes;
using UnityEngine;
namespace MiraAPI.HnsReimplemented;
[RegisterInIl2Cpp]
public sealed class HideAndSeekHudHelper(nint cppPtr) : MonoBehaviour(cppPtr)
{
public static HideAndSeekHudHelper Instance { get; private set; }
public AudioClip FinalHideAlertSfx;
public AudioClip FinalHideCountdownSfx;
private AudioClip TaskFinishedSound;
private const int SECONDS_TO_BEEP = 10;
private const float SECONDS_TO_SET_DIRTY = 1f;
private HideAndSeekTimerBar timerBar;
private float totalHideTime = float.MaxValue;
private float currentHideTime = float.MaxValue;
private float totalFinalHideTime = float.MaxValue;
private float currentFinalHideTime = float.MaxValue;
private float secondsSinceLastSetDirty;
private Coroutine beepCoroutine;
private void Awake()
{
Instance = this;
}
public bool IsFinalCountdown
{
get { return currentHideTime <= 0f; }
}
public bool SeekerAdminMapEnabled(PlayerControl player)
{
int item = Helpers.GetAlivePlayers().Count(x => !x.Data.Role.IsImpostor);
return !player.inVent && player.Data != null && player.Data.Role != null &&
((!player.inVent && player.Data.Role.IsImpostor && IsFinalCountdown &&
OptionGroupSingleton<HnsFinalHideOptions>.Instance.FinalHideSeekMap.Value) ||
(player.Data.Role.IsImpostor &&
item <=
(GameData.Instance.PlayerCount - 1) / 3));
}
public void OnTaskComplete(float timeDeduction)
{
if (timerBar != null)
{
timerBar.TaskComplete();
}
AdjustEscapeTimer(timeDeduction, true);
}
public float GetTotalRoundTime()
{
float escapeTime = OptionGroupSingleton<HnsCrewmateOptions>.Instance.HidingTime.Value;
float finalCountdownTime = OptionGroupSingleton<HnsFinalHideOptions>.Instance.FinalHideTime.Value;
return escapeTime + finalCountdownTime;
}
public float GetTotalTimeRemaining()
{
return currentHideTime + currentFinalHideTime;
}
public float GetRoundTimeElapsed()
{
return GetTotalRoundTime() - GetTotalTimeRemaining();
}
public void Start()
{
totalHideTime = OptionGroupSingleton<HnsCrewmateOptions>.Instance.HidingTime.Value;
currentHideTime = totalHideTime;
totalFinalHideTime = OptionGroupSingleton<HnsFinalHideOptions>.Instance.FinalHideTime.Value;
currentFinalHideTime = totalFinalHideTime;
if (timerBar != null)
{
Object.Destroy(timerBar);
}
FinalHideAlertSfx = GameManagerCreator.Instance.HideAndSeekManagerPrefab.FinalHideAlertSFX;
FinalHideCountdownSfx = GameManagerCreator.Instance.HideAndSeekManagerPrefab.FinalHideCountdownSFX;
TaskFinishedSound = GameManagerCreator.Instance.HideAndSeekManagerPrefab.TaskFinishedSound;
timerBar = Object.Instantiate<HideAndSeekTimerBar>(
GameManagerCreator.Instance.HideAndSeekManagerPrefab.TimerBarPrefab,
HudManager.Instance.transform.parent);
}
public void OnGameEnd()
{
if (timerBar != null)
{
Object.Destroy(timerBar.gameObject);
}
if (beepCoroutine != null)
{
StopCoroutine(beepCoroutine);
}
beepCoroutine = null!;
}
private float taskDirtyTimer;
public float HideCountdown { get; set; }
public void LateUpdate()
{
if (!HudManager.InstanceExists)
{
return;
}
if (HideCountdown > 0f && taskDirtyTimer > 0.25f)
{
float num = taskDirtyTimer;
taskDirtyTimer = 0f;
if (!PlayerControl.LocalPlayer)
{
HudManager.Instance.TaskPanel.SetTaskText(string.Empty);
return;
}
NetworkedPlayerInfo data = PlayerControl.LocalPlayer.Data;
if (data == null)
{
return;
}
bool flag = data.Role != null && data.Role.IsImpostor;
HudManager.Instance.tasksString.Clear();
if (PlayerControl.LocalPlayer.myTasks == null || PlayerControl.LocalPlayer.myTasks.Count == 0)
{
HudManager.Instance.tasksString.Append("None");
}
else
{
for (int i = 0; i < PlayerControl.LocalPlayer.myTasks.Count; i++)
{
PlayerTask playerTask = PlayerControl.LocalPlayer.myTasks[i];
if (playerTask)
{
if (playerTask.TaskType == TaskTypes.FixComms && !flag)
{
HudManager.Instance.tasksString.Clear();
playerTask.AppendTaskText(HudManager.Instance.tasksString);
break;
}
playerTask.AppendTaskText(HudManager.Instance.tasksString);
}
}
if (data.Role != null)
{
data.Role.AppendTaskHint(HudManager.Instance.tasksString);
}
if (HideCountdown > 0f)
{
HideCountdown -= num;
HudManager.Instance.tasksString.Append("\n\n" + ((int)HideCountdown));
}
HudManager.Instance.tasksString.TrimEnd();
}
HudManager.Instance.TaskPanel.SetTaskText(HudManager.Instance.tasksString.ToString());
}
}
public void FixedUpdate()
{
secondsSinceLastSetDirty += Time.fixedDeltaTime;
if (IsFinalCountdown)
{
AdjustFinalEscapeTimer(Time.fixedDeltaTime);
return;
}
AdjustEscapeTimer(Time.fixedDeltaTime, false);
}
public void OnDestroy()
{
if (timerBar != null)
{
Object.Destroy(timerBar);
}
}
private void OnFinalCountdownTriggered()
{
foreach (PlayerControl playerControl in PlayerControl.AllPlayerControls)
{
if (!playerControl.Data.Role.IsImpostor && !playerControl.Data.IsDead)
{
playerControl.ClearTasks();
PlayerTask.GetOrCreateTask<ImportantTextTask>(playerControl, 0).Text =
DestroyableSingleton<TranslationController>.Instance.GetString(StringNames.HideActionButton);
}
}
if (!PlayerControl.LocalPlayer.Data.IsDead && Minigame.Instance != null)
{
Minigame instance = Minigame.Instance;
if (instance != null)
{
instance.ForceClose();
}
}
timerBar.StartFinalHide();
SoundManager.Instance.PlaySound(FinalHideAlertSfx, false, 1f, null);
DestroyableSingleton<HudManager>.Instance.SetAlertOverlay(true);
}
private void AdjustEscapeTimer(float timeDeduction, bool forceDirty)
{
float num = currentHideTime;
currentHideTime -= timeDeduction;
currentHideTime = Mathf.Max(currentHideTime, 0f);
if (currentHideTime <= 10f && beepCoroutine == null)
{
beepCoroutine = StartCoroutine(BeepAlmostEverySecond().WrapToIl2Cpp());
}
if (num > 0f && currentHideTime <= 0f)
{
OnFinalCountdownTriggered();
}
timerBar.UpdateTimer(currentHideTime, totalHideTime);
if (forceDirty || secondsSinceLastSetDirty > 1f)
{
secondsSinceLastSetDirty = 0f;
}
}
private IEnumerator BeepAlmostEverySecond()
{
while (!IsFinalCountdown)
{
float num = currentHideTime / 10f;
float pitch = 1.5f - num / 2f;
SoundManager.Instance.PlaySoundImmediate(FinalHideCountdownSfx, false, 1f, pitch, null);
yield return new WaitForSeconds(1f);
}
yield return Effects.Wait(currentFinalHideTime - 10f);
while (currentFinalHideTime > 0f)
{
float num2 = currentFinalHideTime / 10f;
float pitch2 = 1.5f - num2 / 2f;
SoundManager.Instance.PlaySoundImmediate(FinalHideCountdownSfx, false, 1f, pitch2, null);
yield return new WaitForSeconds(1f);
}
yield break;
}
private void AdjustFinalEscapeTimer(float timeDeduction)
{
currentFinalHideTime -= timeDeduction;
currentFinalHideTime = Mathf.Max(currentFinalHideTime, 0f);
timerBar.UpdateTimer(currentFinalHideTime, totalFinalHideTime);
if (secondsSinceLastSetDirty > 1f)
{
secondsSinceLastSetDirty = 0f;
}
}
public bool AllTimersExpired()
{
return currentHideTime <= 0f && currentFinalHideTime <= 0f;
}
}
-142
View File
@@ -1,142 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using MiraAPI.GameOptions;
using MiraAPI.HnsReimplemented.Options;
using MiraAPI.Utilities;
using Reactor.Utilities.Attributes;
using UnityEngine;
namespace MiraAPI.HnsReimplemented;
[RegisterInIl2Cpp]
public sealed class HnsDangerMeter(nint cppPtr) : MonoBehaviour(cppPtr)
{
public static HnsDangerMeter Instance;
private void Awake()
{
Instance = this;
}
private DangerMeter dangerMeter;
private List<PlayerControl> impostors = [];
private float scaryMusicDistance;
private float veryScaryMusicDistance;
private float dangerLevel1;
private float dangerLevel2;
private bool firstMusicActivation;
private float firstCrossfadeCountdown;
public void FixedUpdate()
{
PlayerControl localPlayer = PlayerControl.LocalPlayer;
if (impostors == null || localPlayer == null)
{
return;
}
if (impostors.Count <= 0)
{
return;
}
float num = float.MaxValue;
foreach (PlayerControl playerControl in impostors)
{
if (!(playerControl == null))
{
float sqrMagnitude = (playerControl.transform.position - localPlayer.transform.position).sqrMagnitude;
if (sqrMagnitude < scaryMusicDistance && num > sqrMagnitude)
{
num = sqrMagnitude;
}
}
}
if (HideAndSeekHudHelper.Instance.HideCountdown > 0f)
{
dangerLevel1 = 0f;
dangerLevel2 = 0f;
}
else
{
if (firstMusicActivation)
{
firstMusicActivation = false;
firstCrossfadeCountdown = 3f;
HnsMusicHandler.Instance.SetMusicCrossfadeSpeed(0.6f);
}
if (firstCrossfadeCountdown > 0f)
{
firstCrossfadeCountdown -= Time.deltaTime;
if (firstCrossfadeCountdown <= 0f)
{
HnsMusicHandler.Instance.SetMusicCrossfadeSpeed(5f);
}
}
dangerLevel1 = Mathf.Clamp01(
(scaryMusicDistance - num) / (scaryMusicDistance - veryScaryMusicDistance));
dangerLevel2 = Mathf.Clamp01((veryScaryMusicDistance - num) / veryScaryMusicDistance);
}
UpdateDangerMeter();
UpdateDangerMusic();
}
private void UpdateDangerMusic()
{
PlayerControl localPlayer = PlayerControl.LocalPlayer;
if (localPlayer != null && localPlayer.Data != null && localPlayer.Data.IsDead)
{
HnsMusicHandler.Instance.SetTaskState(false);
HnsMusicHandler.Instance.ResetMusic();
return;
}
HnsMusicHandler.Instance.SetMusicValues(dangerLevel1, dangerLevel2);
}
private void UpdateDangerMeter()
{
if (dangerMeter == null)
{
return;
}
dangerMeter.SetDangerValue(dangerLevel1, dangerLevel2);
}
public void OnGameStart()
{
firstMusicActivation = true;
if (!PlayerControl.LocalPlayer.Data.Role.IsImpostor)
{
dangerMeter = HudManager.Instance.DangerMeter;
dangerMeter.gameObject.SetActive(true);
}
impostors = Helpers.GetAlivePlayers().Where(x => x.Data.Role.IsImpostor).ToList();
var baseSpeed = OptionGroupSingleton<HnsCrewmateOptions>.Instance.PlayerSpeed.Value;
scaryMusicDistance = 55f *
baseSpeed;
veryScaryMusicDistance = 15f *
baseSpeed;
if (scaryMusicDistance < veryScaryMusicDistance)
{
float num = veryScaryMusicDistance;
float num2 = scaryMusicDistance;
scaryMusicDistance = num;
veryScaryMusicDistance = num2;
}
}
}
-237
View File
@@ -1,237 +0,0 @@
using System.Collections.Generic;
using Amongus.GameModes.HideAndSeek;
using MiraAPI.GameOptions;
using MiraAPI.HnsReimplemented.Options;
using Reactor.Utilities.Attributes;
using UnityEngine;
namespace MiraAPI.HnsReimplemented;
[RegisterInIl2Cpp]
public sealed class HnsMusicHandler(nint cppPtr) : MonoBehaviour(cppPtr)
{
public static HnsMusicHandler Instance;
private void Awake()
{
Instance = this;
}
private HideAndSeekMusicCollection musicCollection;
private float lastMusicSyncTime;
private bool isDoingTask;
private float normalVolume;
private float taskVolume;
private float dangerLevel1Volume;
private float dangerLevel2Volume;
private AudioSource normalSource;
private AudioSource taskSource;
private AudioSource dangerLevel1Source;
private AudioSource dangerLevel2Source;
private float musicLerpSpeed = 5f;
public void Start()
{
musicCollection = GameManagerCreator.Instance.HideAndSeekManagerPrefab.MusicCollection;
}
public void OnGameStart()
{
InitMusic();
ResetMusic();
}
public void OnDestroy()
{
ResetMusic();
}
private void InitMusic()
{
if (normalSource == null)
{
normalSource =
SoundManager.Instance.GetNamedSfxSource(musicNames[LogicHnSMusic.HideAndSeekMusicTrack.Normal]);
}
normalSource.outputAudioMixerGroup = SoundManager.Instance.MusicChannel;
normalSource.clip = musicCollection.NormalMusic;
normalSource.loop = true;
if (taskSource == null)
{
taskSource =
SoundManager.Instance.GetNamedSfxSource(musicNames[LogicHnSMusic.HideAndSeekMusicTrack.Task]);
}
taskSource.outputAudioMixerGroup = SoundManager.Instance.MusicChannel;
taskSource.volume = 0f;
taskSource.clip = musicCollection.TaskMusic;
taskSource.loop = true;
if (dangerLevel1Source == null)
{
dangerLevel1Source =
SoundManager.Instance.GetNamedSfxSource(
musicNames[LogicHnSMusic.HideAndSeekMusicTrack.DangerLevel1]);
}
dangerLevel1Source.outputAudioMixerGroup = SoundManager.Instance.MusicChannel;
dangerLevel1Source.volume = 0f;
dangerLevel1Source.clip = musicCollection.DangerLevel1Music;
dangerLevel1Source.loop = true;
if (dangerLevel2Source == null)
{
dangerLevel2Source =
SoundManager.Instance.GetNamedSfxSource(
musicNames[LogicHnSMusic.HideAndSeekMusicTrack.DangerLevel2]);
}
dangerLevel2Source.outputAudioMixerGroup = SoundManager.Instance.MusicChannel;
dangerLevel2Source.volume = 0f;
dangerLevel2Source.clip = musicCollection.DangerLevel2Music;
dangerLevel2Source.loop = true;
normalSource.Play();
taskSource.Play();
dangerLevel1Source.Play();
dangerLevel2Source.Play();
SyncMusic();
}
public void StartMusicWithIntro()
{
if (PlayerControl.LocalPlayer.Data.Role.IsImpostor)
{
AudioClip clip = OptionGroupSingleton<HnsCrewmateOptions>.Instance.HidingTime.Value <= 180f
? musicCollection.ImpostorShortMusic
: musicCollection.ImpostorLongMusic;
if (AprilFoolsMode.ShouldHorseAround())
{
clip = musicCollection.ImpostorRanchMusic;
}
SoundManager.Instance.PlaySound(clip, true, 1f, SoundManager.Instance.MusicChannel);
}
}
public void SetTaskState(bool isDoingTask)
{
isDoingTask = isDoingTask;
}
public void FixedUpdate()
{
if (normalSource == null || taskSource == null || dangerLevel1Source == null ||
dangerLevel2Source == null)
{
return;
}
if (Time.unscaledTime > lastMusicSyncTime + 1f)
{
SyncMusic();
}
normalSource.volume = Mathf.Lerp(
normalSource.volume,
normalVolume,
Time.fixedDeltaTime * musicLerpSpeed);
taskSource.volume = Mathf.Lerp(
taskSource.volume,
taskVolume,
Time.fixedDeltaTime * musicLerpSpeed);
dangerLevel1Source.volume = Mathf.Lerp(
dangerLevel1Source.volume,
dangerLevel1Volume,
Time.fixedDeltaTime * musicLerpSpeed);
dangerLevel2Source.volume = Mathf.Lerp(
dangerLevel2Source.volume,
dangerLevel2Volume,
Time.fixedDeltaTime * musicLerpSpeed);
}
private void SyncMusic()
{
taskSource.timeSamples = normalSource.timeSamples;
dangerLevel1Source.timeSamples = normalSource.timeSamples;
dangerLevel2Source.timeSamples = normalSource.timeSamples;
lastMusicSyncTime = Time.unscaledTime;
}
public void ResetMusic()
{
SetMusicValues(0f, 0f);
}
public void SetMusicCrossfadeSpeed(float lerpSpeed)
{
musicLerpSpeed = lerpSpeed;
}
public void SetMusicValues(float dangerLevel1, float dangerLevel2)
{
if (PlayerControl.LocalPlayer != null && PlayerControl.LocalPlayer.Data.Role.IsImpostor)
{
return;
}
if (normalSource == null || taskSource == null || dangerLevel1Source == null ||
dangerLevel2Source == null)
{
return;
}
normalVolume = (isDoingTask ? 0f : 1f);
taskVolume = (isDoingTask ? 1f : 0f);
dangerLevel1Volume = 0f;
dangerLevel2Volume = 0f;
if (dangerLevel1 > 0f)
{
dangerLevel1Volume = dangerLevel1;
if (isDoingTask)
{
taskVolume = 1f - dangerLevel1;
}
else
{
normalVolume = 1f - dangerLevel1;
}
}
if (dangerLevel2 > 0f)
{
dangerLevel2Volume = dangerLevel2;
dangerLevel1Volume = 1f - dangerLevel2;
}
}
private readonly Dictionary<LogicHnSMusic.HideAndSeekMusicTrack, string> musicNames =
new Dictionary<LogicHnSMusic.HideAndSeekMusicTrack, string>
{
{
LogicHnSMusic.HideAndSeekMusicTrack.Normal,
"HnS_Music_Normal"
},
{
LogicHnSMusic.HideAndSeekMusicTrack.Task,
"HnS_Music_Task"
},
{
LogicHnSMusic.HideAndSeekMusicTrack.DangerLevel1,
"HnS_Music_DangerLevel1"
},
{
LogicHnSMusic.HideAndSeekMusicTrack.DangerLevel2,
"HnS_Music_DangerLevel2"
},
};
}
@@ -1,89 +0,0 @@
using MiraAPI.GameModes;
using MiraAPI.GameOptions;
using MiraAPI.GameOptions.OptionTypes;
using MiraAPI.Utilities;
namespace MiraAPI.HnsReimplemented.Options;
/// <inheritdoc />
public class HnsCrewmateOptions : AbstractOptionGroup<HideAndSeekMode>
{
/// <inheritdoc />
public override string GroupName => "Crewmates";
public ModdedNumberOption PlayerSpeed { get; set; } = new(
"Player Speed",
1f,
0.5f,
3f,
0.25f,
"#",
"#",
MiraNumberSuffixes.Multiplier,
"0.00");
public ModdedNumberOption HidingTime { get; set; } = new(
"Hiding Time",
200,
160,
300,
20,
"#",
"#",
MiraNumberSuffixes.Seconds);
public ModdedNumberOption CrewmateVision { get; set; } = new(
"Crewmate Vision",
0.6f,
0.25f,
1,
0.05f,
"#",
"#",
MiraNumberSuffixes.Multiplier,
"0.00");
public ModdedNumberOption MaxVentUses { get; set; } = new(
"Max Vent Uses",
1,
0,
5,
1,
"#",
"#",
MiraNumberSuffixes.None);
public ModdedNumberOption MaxTimeInVent { get; set; } = new(
"Max Time in Vent",
3,
1,
10,
1,
"#",
"#",
MiraNumberSuffixes.Seconds)
{
Visible = () => (int)OptionGroupSingleton<HnsCrewmateOptions>.Instance.MaxVentUses.Value > 0,
};
public ModdedToggleOption FlashlightMode { get; set; } = new(
"Flashlight Mode",
true);
public ModdedNumberOption CrewmateFlashlightSize { get; set; } = new(
"Crewmate Flashlight Size",
0.35f,
0.1f,
0.5f,
0.05f,
"#",
"#",
MiraNumberSuffixes.Multiplier)
{
Visible = () => OptionGroupSingleton<HnsCrewmateOptions>.Instance.FlashlightMode.Value,
};
public ModdedToggleOption ShowNames { get; set; } = new(
"Show Names",
false);
}
@@ -1,52 +0,0 @@
using MiraAPI.GameModes;
using MiraAPI.GameOptions;
using MiraAPI.GameOptions.OptionTypes;
using MiraAPI.Utilities;
namespace MiraAPI.HnsReimplemented.Options;
/// <inheritdoc />
public class HnsFinalHideOptions : AbstractOptionGroup<HideAndSeekMode>
{
/// <inheritdoc />
public override string GroupName => "Final Hide";
public ModdedNumberOption FinalHideTime { get; set; } = new(
"Hiding Time",
50,
30,
120,
5,
"#",
"#",
MiraNumberSuffixes.Seconds);
public ModdedToggleOption FinalHidePings { get; set; } = new(
"Final Hide Pings",
true);
public ModdedNumberOption FinalHideImpostorSpeed { get; set; } = new(
"Final Hide Impostor Speed",
1.2f,
1f,
3,
0.05f,
"#",
"#",
MiraNumberSuffixes.Multiplier,
"0.00");
public ModdedNumberOption PingInterval { get; set; } = new(
"Ping Interval",
6,
3,
10,
5,
"#",
"#",
MiraNumberSuffixes.Seconds);
public ModdedToggleOption FinalHideSeekMap { get; set; } = new(
"Final Hide Seek Map",
true);
}
@@ -1,40 +0,0 @@
using MiraAPI.GameModes;
using MiraAPI.GameOptions;
using MiraAPI.GameOptions.OptionTypes;
using MiraAPI.Utilities;
namespace MiraAPI.HnsReimplemented.Options;
/// <inheritdoc />
public class HnsImpostorOptions : AbstractOptionGroup<HideAndSeekMode>
{
/// <inheritdoc />
public override string GroupName => "Impostors";
public ModdedPlayerOption SelectedSeeker { get; set; } = new(
"Forced Impostor");
public ModdedNumberOption ImpostorVision { get; set; } = new(
"Impostor Vision",
0.6f,
0.25f,
1,
0.05f,
"#",
"#",
MiraNumberSuffixes.Multiplier,
"0.00");
public ModdedNumberOption ImpostorFlashlightSize { get; set; } = new(
"Impostor Flashlight Size",
0.35f,
0.1f,
0.5f,
0.05f,
"#",
"#",
MiraNumberSuffixes.Multiplier)
{
Visible = () => OptionGroupSingleton<HnsCrewmateOptions>.Instance.FlashlightMode.Value,
};
}
@@ -1,43 +0,0 @@
using MiraAPI.GameModes;
using MiraAPI.GameOptions;
using MiraAPI.GameOptions.OptionTypes;
using MiraAPI.Utilities;
namespace MiraAPI.HnsReimplemented.Options;
/// <inheritdoc />
public class HnsTaskOptions : AbstractOptionGroup<HideAndSeekMode>
{
/// <inheritdoc />
public override string GroupName => "Tasks";
public ModdedNumberOption CommonTasks { get; set; } = new(
"# Common Tasks",
1,
0,
4,
1,
"#",
"#",
MiraNumberSuffixes.None);
public ModdedNumberOption LongTasks { get; set; } = new(
"# Long Tasks",
1,
0,
4,
1,
"#",
"#",
MiraNumberSuffixes.None);
public ModdedNumberOption ShortTasks { get; set; } = new(
"# Short Tasks",
2,
0,
8,
1,
"#",
"#",
MiraNumberSuffixes.None);
}
+1 -1
View File
@@ -1,7 +1,7 @@
namespace MiraAPI.Hud;
/// <summary>
/// The location of the <see cref="CustomActionButton"/> on the screen.
/// The location of the custom button on the screen.
/// </summary>
public enum ButtonLocation
{
+1 -1
View File
@@ -1,7 +1,7 @@
namespace MiraAPI.Hud;
/// <summary>
/// The uses mode for <see cref="CustomActionButton"/>s.
/// The uses mode for custom buttons.
/// </summary>
public enum ButtonUsesMode
{
+39 -76
View File
@@ -7,6 +7,7 @@ using MiraAPI.LocalSettings;
using MiraAPI.Patches;
using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets;
using Reactor.Utilities;
using Rewired;
using TMPro;
using UnityEngine;
@@ -17,7 +18,7 @@ using Object = UnityEngine.Object;
namespace MiraAPI.Hud;
/// <summary>
/// Class for making custom action buttons. More customizable than the default <see cref="ActionButton"/>/<see cref="AbilityButton"/>s in the base game.
/// Class for making custom action buttons. More customizable than the default Action/Ability buttons in the base game.
/// </summary>
public abstract class CustomActionButton
{
@@ -53,7 +54,7 @@ public abstract class CustomActionButton
public virtual string CooldownTimerFormatString => "0";
/// <summary>
/// Gets a value indicating whether the button has an effect ability. By default <see langword="true"/> if <see cref="EffectDuration"/> is greater than 0.
/// Gets a value indicating whether the button has an effect ability. By default true if <see cref="EffectDuration"/> is greater than 0.
/// </summary>
public virtual bool HasEffect => EffectDuration > 0;
@@ -63,14 +64,9 @@ public abstract class CustomActionButton
public virtual float EffectDuration => 0;
/// <summary>
/// Gets a value indicating whether the button has limited uses.
/// Gets the maximum amount of uses the button has. If the button has infinite uses, set to 0.
/// </summary>
public bool LimitedUses => ZeroIsInfinite ? MaxUses > 0 : MaxUses >= 0;
/// <summary>
/// Gets the maximum number of uses the button has. If the button has infinite uses, set to 0 or -1 based on what value that ZeroIsInfinite is set to.
/// </summary>
public virtual int MaxUses => ZeroIsInfinite ? 0 : -1;
public virtual int MaxUses => 0;
/// <summary>
/// Gets the value indicating uses mode.
@@ -78,24 +74,24 @@ public abstract class CustomActionButton
public virtual ButtonUsesMode UsesMode => ButtonUsesMode.PerGame;
/// <summary>
/// Gets the keybind for this button. If <see langword="null"/>, no keybind will be added.
/// Gets the keybind for this button. If null, no keybind will be added.
/// </summary>
public virtual BaseKeybind? Keybind => null;
/// <summary>
/// Gets the button's text outline <see cref="Color"/>.
/// Gets the button's text outline color.
/// </summary>
public virtual Color TextOutlineColor => Color.clear;
/// <summary>
/// Gets or sets the <see cref="ButtonLocation"/> on the screen.
/// Gets or sets the location of the button on the screen.
/// </summary>
public virtual ButtonLocation Location { get; set; } = ButtonLocation.BottomLeft;
/// <summary>
/// Gets or sets a value indicating whether limited uses are determined via zero or a negative number of uses.
/// Gets a value indicating whether the button has limited uses.
/// </summary>
public virtual bool ZeroIsInfinite { get; set; } = true;
public bool LimitedUses => MaxUses > 0;
/// <summary>
/// Gets or sets a value indicating whether the effect is currently active, if there is one.
@@ -118,12 +114,12 @@ public abstract class CustomActionButton
public float Timer { get; set; }
/// <summary>
/// Gets or sets the <see cref="ActionButton"/> object in game. This is created by Mira API automatically.
/// Gets or sets the button object in game. This is created by Mira API automatically.
/// </summary>
public ActionButton? Button { get; set; }
/// <summary>
/// Gets the <see cref="GameObject"/> used for the keybind icon.
/// Gets the gameObject used for the keybind icon.
/// </summary>
public GameObject? KeybindIcon { get; private set; }
@@ -210,39 +206,7 @@ public abstract class CustomActionButton
{
if (Enabled(PlayerControl.LocalPlayer.Data.Role))
{
// Invoke the generic button click event.
var genericEvent = new MiraButtonClickEvent(this);
MiraEventManager.InvokeEvent(genericEvent);
if (genericEvent.IsCancelled)
{
MiraEventManager.InvokeEvent(new MiraButtonCancelledEvent(this));
}
// Invoke the button click event for specific button.
var eventType = CustomButtonManager.ButtonEventTypes[GetType()];
var @event = (MiraCancelableEvent)Activator.CreateInstance(eventType, this, genericEvent)!;
var specificInvoked = MiraEventManager.InvokeEvent(@event, eventType);
if (@event.IsCancelled)
{
var cancelEventType = CustomButtonManager.ButtonCancelledEventTypes[GetType()];
var cancelEvent = (MiraEvent)Activator.CreateInstance(cancelEventType, this)!;
MiraEventManager.InvokeEvent(cancelEvent, cancelEventType);
}
if (specificInvoked)
{
if (!@event.IsCancelled)
{
ClickHandler();
}
}
else
{
if (!genericEvent.IsCancelled)
{
ClickHandler();
}
}
ClickHandler();
}
});
@@ -250,10 +214,9 @@ public abstract class CustomActionButton
Helpers.CreateKeybindIcon(
Button.gameObject,
Keybind.CurrentKey,
new Vector3(0.4f, 0.45f, -9f)
new Vector3(MaxUses <= 0 ? -0.4f : 0.4f, 0.45f, -9f)
);
KeybindText = KeybindIcon.transform.GetChild(0).GetComponent<TextMeshPro>();
HudManagerPatches.ModdedKeybindIcons.Add(KeybindText);
Button.usesRemainingSprite.transform.localPosition = new(-0.341f, 0.45f, -0.1f);
}
}
@@ -270,7 +233,7 @@ public abstract class CustomActionButton
return;
}
Location = location;
this.Location = location;
if (!moveButton)
{
@@ -315,18 +278,18 @@ public abstract class CustomActionButton
}
/// <summary>
/// A utility function to change the outline <see cref="Color"/> of the button's text.
/// A utility function to change the outline color of the button's text.
/// </summary>
/// <param name="color">The new <see cref="Color"/>.</param>
/// <param name="color">The new color.</param>
public virtual void SetTextOutline(Color color)
{
Button?.buttonLabelText.SetOutlineColor(color);
}
/// <summary>
/// A utility function to override the <see cref="UnityEngine.Sprite"/> of the button.
/// A utility function to override the sprite of the button.
/// </summary>
/// <param name="sprite">The new <see cref="UnityEngine.Sprite"/> to override with.</param>
/// <param name="sprite">The new sprite to override with.</param>
public virtual void OverrideSprite(Sprite sprite)
{
if (Button != null)
@@ -414,7 +377,7 @@ public abstract class CustomActionButton
}
/// <summary>
/// A utility function that runs with the local player's <see cref="PlayerControl.FixedUpdate"/> if the button is enabled.
/// A utility function that runs with the local PlayerControl's FixedUpdate if the button is enabled.
/// </summary>
/// <param name="playerControl">the local PlayerControl.</param>
protected virtual void FixedUpdate(PlayerControl playerControl)
@@ -428,10 +391,10 @@ public abstract class CustomActionButton
/// <summary>
/// This method determines if the button should be active or not.
/// <see langword="true"/> means the button is active, <see langword="false"/> means the button is disabled.
/// True means the button is active, false means the button is disabled.
/// </summary>
/// <param name="role">The <see cref="RoleBehaviour"/> of the local player.</param>
/// <returns><see langword="true"/> if the button is enabled, <see langword="false"/> otherwise.</returns>
/// <param name="role">The role of the local player.</param>
/// <returns>True if the button is enabled, false otherwise.</returns>
public abstract bool Enabled(RoleBehaviour? role);
/// <summary>
@@ -444,7 +407,7 @@ public abstract class CustomActionButton
/// <summary>
/// Returns a value indicating whether the effect can be ended early by the player.
/// Always <see langword="false"/> by default.
/// Always false by default.
/// </summary>
/// <returns>Can the effect be canceled.</returns>
public virtual bool IsEffectCancellable() => false;
@@ -472,12 +435,12 @@ public abstract class CustomActionButton
}
/// <summary>
/// This method is called on the <see cref="HudManager.SetHudActive(PlayerControl, RoleBehaviour, bool)"/> method. It determines whether the button should be visible or not.
/// The default behavior is to show the button if the <paramref name="visible"/> parameter and the <see cref="Enabled"/> method return <see langword="true"/>.
/// This method is called on the HudManager.SetHudActive method. It determines whether the button should be visible or not.
/// The default behavior is to show the button if the <paramref name="visible"/> parameter and the <see cref="Enabled"/> method return true.
/// It can be overridden for custom behavior.
/// </summary>
/// <param name="visible">Passed in from <see cref="HudManager.SetHudActive(PlayerControl, RoleBehaviour, bool)"/>, should hud be active.</param>
/// <param name="role">Passed in from <see cref="HudManager.SetHudActive(PlayerControl, RoleBehaviour, bool)"/>, the current role of the player.</param>
/// <param name="visible">Passed in from HudManager.SetHudActive, should hud be active.</param>
/// <param name="role">Passed in from HudManager.SetHudActive, the current role of the player.</param>
public virtual void SetActive(bool visible, RoleBehaviour role)
{
Button?.ToggleVisible(visible && Enabled(role));
@@ -521,7 +484,7 @@ public abstract class CustomActionButton
}
/// <summary>
/// This method is called on the <see cref="PlayerControl.FixedUpdate"/> method. It is a wrapper for the <see cref="FixedUpdate"/> method.
/// This method is called on the PlayerControl.FixedUpdate method. It is a wrapper for the <see cref="FixedUpdate"/> method.
/// By default, it handles the cooldown and effect timers, and sets the button to enabled or disabled.
/// It can be overridden for custom behavior.
/// </summary>
@@ -531,7 +494,7 @@ public abstract class CustomActionButton
if (Keybind != null && KeybindText != null)
{
KeybindText.text = Keybind.CurrentKey.ToString();
KeybindIcon?.SetActive(ActiveInputManager.currentControlType is ActiveInputManager.InputType.Keyboard &&
KeybindIcon?.SetActive(ActiveInputManager.currentControlType == ActiveInputManager.InputType.Keyboard &&
LocalSettingsTabSingleton<MiraApiSettings>.Instance.ShowKeybinds.Value &&
Keybind.CurrentKey != KeyboardKeyCode.None);
}
@@ -579,39 +542,39 @@ public abstract class CustomActionButton
}
/// <summary>
/// Custom action button that has a target <typeparamref name="T"/>.
/// Custom action button that has a target object.
/// </summary>
/// <typeparam name="T">The type of the target object.</typeparam>
public abstract class CustomActionButton<T> : CustomActionButton where T : MonoBehaviour
{
/// <summary>
/// Gets or sets the target <typeparamref name="T"/> of the button.
/// Gets or sets the target object of the button.
/// </summary>
public T? Target { get; set; }
/// <summary>
/// Gets the distance the player must be from the target <typeparamref name="T"/> to use the button.
/// Gets the distance the player must be from the target object to use the button.
/// </summary>
public virtual float Distance => PlayerControl.LocalPlayer.Data.Role.GetAbilityDistance();
/// <summary>
/// Determines if the target <typeparamref name="T"/> is valid.
/// Determines if the target object is valid.
/// </summary>
/// <param name="target">The target <typeparamref name="T"/> being checked.</param>
/// <returns><see langword="true"/> if the target <typeparamref name="T"/> is valid, <see langword="false"/> otherwise.</returns>
/// <param name="target">The target object being checked.</param>
/// <returns>True if the target object is valid, false otherwise.</returns>
public virtual bool IsTargetValid(T? target)
{
return target != null;
}
/// <summary>
/// The method used to get the target <typeparamref name="T"/>.
/// The method used to get the target object.
/// </summary>
/// <returns>The target <typeparamref name="T"/> or <see langword="null"/> if it isn't found.</returns>
/// <returns>The target object or null if it isn't found.</returns>
public abstract T? GetTarget();
/// <summary>
/// Sets the outline of the target <typeparamref name="T"/>.
/// Sets the outline of the target object.
/// </summary>
/// <param name="active">Should the outline be active.</param>
public abstract void SetOutline(bool active);
+3 -16
View File
@@ -4,33 +4,20 @@ using System.Collections.ObjectModel;
using System.Reflection;
using MiraAPI.Events.Mira;
using MiraAPI.PluginLoading;
using UnityEngine;
namespace MiraAPI.Hud;
/// <summary>
/// Custom button manager for handling <see cref="CustomActionButton"/>s.
/// Custom button manager for handling custom buttons.
/// </summary>
public static class CustomButtonManager
{
/// <summary>
/// Gets a list of all registered custom <see cref="CustomActionButton"/>s.
/// Gets a list of all registered custom buttons.
/// </summary>
public static ReadOnlyCollection<CustomActionButton> Buttons { get; internal set; } = new([]);
/// <summary>
/// Gets a list of all registered button-specific <see cref="MiraButtonClickEvent"/>s.
/// </summary>
public static ReadOnlyDictionary<Type, Type> EventTypes { get; internal set; } = new(
new Dictionary<Type, Type>
{ });
/// <summary>
/// Gets a list of all registered button-specific <see cref="MiraButtonCancelledEvent"/>s.
/// </summary>
public static ReadOnlyDictionary<Type, Type> CancelledEventTypes { get; internal set; } = new(
new Dictionary<Type, Type>
{ });
internal static readonly List<CustomActionButton> CustomButtons = [];
internal static readonly Dictionary<Type, Type> ButtonEventTypes = [];
internal static readonly Dictionary<Type, Type> ButtonCancelledEventTypes = [];
+2 -2
View File
@@ -4,7 +4,7 @@ using System.Linq;
namespace MiraAPI.Hud;
/// <summary>
/// A utility class to get the instance of a <see cref="CustomActionButton"/>.
/// A utility class to get the instance of a custom action button.
/// </summary>
/// <typeparam name="T">The type of the button you are trying to access.</typeparam>
public static class CustomButtonSingleton<T> where T : CustomActionButton
@@ -12,7 +12,7 @@ public static class CustomButtonSingleton<T> where T : CustomActionButton
private static T? _instance;
/// <summary>
/// Gets the instance of the <typeparamref name="T"/> button.
/// Gets the instance of the button.
/// </summary>
[SuppressMessage("Design", "CA1000:Do not declare static members on generic types", Justification = "This is a utility class to get the instance of a custom action button.")]
public static T Instance => _instance ??= CustomButtonManager.CustomButtons.OfType<T>().Single();
+6 -6
View File
@@ -15,9 +15,9 @@ using UnityEngine.Events;
namespace MiraAPI.Hud;
/// <summary>
/// Custom Player Menu using the <see cref="ShapeshifterPanel"/> as a base.
/// Custom Player Menu using the ShapeshifterPanel as a base.
/// </summary>
/// <param name="il2CppPtr">Used by Il2Cpp. Do not use constructor, this is a <see cref="MonoBehaviour"/>.</param>
/// <param name="il2CppPtr">Used by Il2Cpp. Do not use constructor, this is a MonoBehaviour.</param>
[RegisterInIl2Cpp]
[SuppressMessage("Design", "CA1051:Do not declare visible instance fields", Justification = "Unity Convention")]
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Unity Convention")]
@@ -34,9 +34,9 @@ public class CustomPlayerMenu(IntPtr il2CppPtr) : Minigame(il2CppPtr)
public List<ShapeshifterPanel> potentialVictims;
/// <summary>
/// Creates a <see cref="CustomPlayerMenu"/>.
/// Creates a CustomPlayerMenu.
/// </summary>
/// <returns>New <see cref="CustomPlayerMenu"/> object.</returns>
/// <returns>New CustomPlayerMenu object.</returns>
public static CustomPlayerMenu Create()
{
var shapeShifterRole = RoleManager.Instance.GetRole(RoleTypes.Shapeshifter);
@@ -84,7 +84,7 @@ public class CustomPlayerMenu(IntPtr il2CppPtr) : Minigame(il2CppPtr)
/// Begins/opens the custom player menu.
/// </summary>
/// <param name="playerMatch">Function to determine if player should show in the custom menu.</param>
/// <param name="onClick"><see cref="PassiveButton.OnClick"/> action for player.</param>
/// <param name="onClick">Onclick action for player.</param>
[HideFromIl2Cpp]
public void Begin(Func<PlayerControl, bool> playerMatch, Action<PlayerControl?> onClick)
{
@@ -96,7 +96,7 @@ public class CustomPlayerMenu(IntPtr il2CppPtr) : Minigame(il2CppPtr)
onClick(null);
}));
DebugAnalytics.Instance.Analytics.MinigameOpened(PlayerControl.LocalPlayer.Data, TaskType);
DestroyableSingleton<DebugAnalytics>.Instance.Analytics.MinigameOpened(PlayerControl.LocalPlayer.Data, TaskType);
var list = PlayerControl.AllPlayerControls.ToArray().Where(playerMatch).ToList();
potentialVictims = [];
var list2 = new Il2CppSystem.Collections.Generic.List<UiElement>();
+4 -6
View File
@@ -1,11 +1,12 @@
using System.Linq;
using HarmonyLib;
using Reactor.Utilities.Extensions;
using UnityEngine;
namespace MiraAPI.Hud;
/// <summary>
/// <see cref="CustomActionButton"/> that has multiple target objects.
/// Custom action button that has multiple target objects.
/// </summary>
/// <typeparam name="T">The type of the target objects.</typeparam>
public abstract class MultiTargetButton<T> : CustomActionButton where T : MonoBehaviour
@@ -35,11 +36,8 @@ public abstract class MultiTargetButton<T> : CustomActionButton where T : MonoBe
/// <returns>The list of target objects.</returns>
public abstract T[] GetTargets();
/// <summary>
/// Sets the outline of the target <typeparamref name="T"/>.
/// </summary>
/// <param name="target">The target <typeparamref name="T"/> to set the oultine.</param>
/// <param name="active">Should the outline be active.</param>
/// <inheritdoc cref="CustomActionButton{T}.SetOutline"/>
/// <param name="target">The target object to set the oultine.</param>
public abstract void SetOutline(T target, bool active);
/// <inheritdoc cref="CustomActionButton{T}.CanUse"/>
+2 -2
View File
@@ -14,7 +14,7 @@ public class BaseKeybind
public InputAction? RewiredInputAction { get; internal set; }
/// <summary>
/// Gets or sets the unique identifier for this keybind. Used in Rewired.
/// Gets the unique identifier for this keybind. Used in Rewired.
/// </summary>
public string Id { get; protected set; }
@@ -69,4 +69,4 @@ public class BaseKeybind
{
Handler -= action;
}
}
}
+9 -8
View File
@@ -3,17 +3,18 @@ using System.Collections.Generic;
using System.Linq;
using Il2CppInterop.Runtime.Attributes;
using MiraAPI.Utilities;
using Reactor.Utilities;
using Rewired;
namespace MiraAPI.Keybinds;
/// <summary>
/// Lets you register and manage mod <see cref="MiraKeybind"/>s with conflict checks and rebinding support.
/// Lets you register and manage mod keybinds with conflict checks and rebinding support.
/// </summary>
public static class KeybindManager
{
/// <summary>
/// Gets a list of all registered <see cref="MiraKeybind"/>s.
/// Gets a list of all registered keybinds.
/// </summary>
public static List<MiraKeybind> Keybinds { get; } = new();
@@ -25,11 +26,11 @@ public static class KeybindManager
try
{
var instance = KeybindUtils.RewiredInputManager!;
foreach (var keybind in Keybinds)
foreach (var keybind in KeybindManager.Keybinds)
{
if (instance.userData.actions.ToArray().Any(x => x.name == keybind.Id))
{
Warning($"Keybind of id {keybind.Id} already exists. Skipping it");
Logger<MiraApiPlugin>.Warning($"Keybind of id {keybind.Id} already exists. Skipping it");
continue;
}
@@ -43,12 +44,12 @@ public static class KeybindManager
}
catch (Exception e)
{
Error($"Error while registering keybinds in Rewired: {e}");
Logger<MiraApiPlugin>.Error($"Error while registering keybinds in Rewired: {e}");
}
}
/// <summary>
/// Returns all conflicts where <see cref="MiraKeybind"/>s use the same key.
/// Returns all conflicts where keybinds use the same key.
/// </summary>
/// <returns>A list of pairs of keybinds that conflict.</returns>
public static Dictionary<KeyboardKeyCode, List<MiraKeybind>> GetConflicts()
@@ -76,11 +77,11 @@ public static class KeybindManager
}
/// <summary>
/// Returns all <see cref="MiraKeybind"/>s for a specified keycode.
/// Returns all keybinds for a specified keycode.
/// </summary>
/// <param name="keyCode">The key to look for.</param>
/// /// <param name="exclusiveCheck">Should only exlusive keybinds be present.</param>
/// <returns>The list of <see cref="MiraKeybind"/>s.</returns>
/// <returns>The list of keybinds.</returns>
public static MiraKeybind[] GetKeybindsForKey(KeyboardKeyCode keyCode, bool exclusiveCheck = false)
{
var all = new List<MiraKeybind>();
+5 -5
View File
@@ -14,9 +14,9 @@ public static class KeybindUtils
public static InputManager_Base? RewiredInputManager { get; internal set; }
/// <summary>
/// Returns the currently assigned keycode of a <see cref="BaseKeybind"/>.
/// Returns the currently assigned keycode of a keybind.
/// </summary>
/// <param name="keybind">The <see cref="BaseKeybind"/> to get the keycode from.</param>
/// <param name="keybind">The keybind to get the keycode from.</param>
/// <returns>The currently assigned keycode.</returns>
public static KeyboardKeyCode GetKeycodeByKeybind(BaseKeybind keybind)
{
@@ -29,7 +29,7 @@ public static class KeybindUtils
}
/// <summary>
/// Gets the keycode for an action with <see cref="ReInput"/>.
/// Gets the keycode for an action with ReInput.
/// </summary>
/// <param name="actionId">The action ID.</param>
/// <returns>The keyboard keycode.</returns>
@@ -39,7 +39,7 @@ public static class KeybindUtils
}
/// <summary>
/// Gets a <see cref="ActionElementMap"/> by id with <see cref="ReInput"/>.
/// Gets a <see cref="ActionElementMap"/> by id with ReInput.
/// </summary>
/// <param name="actionId">The action ID.</param>
/// <returns>The keyboard keycode.</returns>
@@ -65,7 +65,7 @@ public static class KeybindUtils
/// <param name="exclude">The KeyCode to skip during the search.</param>
/// <returns>
/// The first available KeyCode not currently used by any registered keybind,
/// or <see cref="KeyboardKeyCode.None"/> if none are available.
/// or KeyCode.None if none are available.
/// </returns>
public static KeyboardKeyCode FindAvailableKey(KeyboardKeyCode exclude)
{
+1 -1
View File
@@ -3,7 +3,7 @@
namespace MiraAPI.Keybinds;
/// <summary>
/// Class containing all default <see cref="MiraKeybind"/>s.
/// Class containing all default keybinds.
/// Recommended for cross-mod compatibility.
/// </summary>
[RegisterCustomKeybinds]
+4 -4
View File
@@ -9,7 +9,7 @@ namespace MiraAPI.Keybinds;
public class MiraKeybind : BaseKeybind
{
/// <summary>
/// Gets name of the keybind.
/// Gets name of the keybind
/// </summary>
public string Name { get; }
@@ -19,8 +19,8 @@ public class MiraKeybind : BaseKeybind
public KeyboardKeyCode DefaultKey { get; }
/// <summary>
/// Gets the <see cref="ModifierKey"/>s assigned to this keybind.
/// Due to Rewired limitations, there can only be 3 <see cref="ModifierKey"/>s.
/// Gets the modifier keys assigned to this keybind.
/// Due to Rewired limitations, there can only be 3 modifier keys.
/// </summary>
public ModifierKey[] ModifierKeys { get; }
@@ -36,7 +36,7 @@ public class MiraKeybind : BaseKeybind
/// </summary>
/// <param name="name">The name of the keybind.</param>
/// <param name="defaultKeycode">The default keycode.</param>
/// <param name="modifierKeys">Up to 3 optional <see cref="ModifierKey"/>s.</param>
/// <param name="modifierKeys">Up to 3 optional modifier keys.</param>
/// <param name="exclusive">Is exclusive.</param>
public MiraKeybind(
string name,
Loaded 100 of 266 files, more files were not shown because too many files have changed in this diff. Show more