mirror of
https://github.com/All-Of-Us-Mods/MiraAPI.git
synced 2026-10-20 01:46:15 +00:00
Merge branch 'dev' into android
# Conflicts: # MiraAPI/Patches/IntroCutscenePatches.cs
This commit is contained in:
commit
4027257b55
37 files changed
+668
-63
No files matched your search
@@ -6,6 +6,7 @@ using MiraAPI.Hud;
|
|||||||
using MiraAPI.Modifiers;
|
using MiraAPI.Modifiers;
|
||||||
using MiraAPI.Utilities;
|
using MiraAPI.Utilities;
|
||||||
using MiraAPI.Utilities.Assets;
|
using MiraAPI.Utilities.Assets;
|
||||||
|
using Rewired;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace MiraAPI.Example.Buttons.Freezer;
|
namespace MiraAPI.Example.Buttons.Freezer;
|
||||||
@@ -19,6 +20,8 @@ public class FreezeButton : CustomActionButton<PlayerControl>
|
|||||||
public override int MaxUses => (int)OptionGroupSingleton<FreezerRoleSettings>.Instance.FreezeUses;
|
public override int MaxUses => (int)OptionGroupSingleton<FreezerRoleSettings>.Instance.FreezeUses;
|
||||||
|
|
||||||
public override LoadableAsset<Sprite> Sprite => ExampleAssets.ExampleButton;
|
public override LoadableAsset<Sprite> Sprite => ExampleAssets.ExampleButton;
|
||||||
|
public override KeyboardKeyCode Defaultkeybind => KeyboardKeyCode.T;
|
||||||
|
public override ModifierKey Modifier1 => ModifierKey.Control;
|
||||||
|
|
||||||
protected override void OnClick()
|
protected override void OnClick()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using MiraAPI.Hud;
|
using MiraAPI.Hud;
|
||||||
using MiraAPI.Modifiers;
|
using MiraAPI.Modifiers;
|
||||||
using MiraAPI.Utilities.Assets;
|
using MiraAPI.Utilities.Assets;
|
||||||
|
using Rewired;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace MiraAPI.Example.Buttons;
|
namespace MiraAPI.Example.Buttons;
|
||||||
@@ -15,6 +16,9 @@ public class MeetingButton : CustomActionButton
|
|||||||
public override int MaxUses => 3;
|
public override int MaxUses => 3;
|
||||||
|
|
||||||
public override LoadableAsset<Sprite> Sprite => ExampleAssets.ExampleButton;
|
public override LoadableAsset<Sprite> Sprite => ExampleAssets.ExampleButton;
|
||||||
|
public override KeyboardKeyCode Defaultkeybind => KeyboardKeyCode.P;
|
||||||
|
public override ModifierKey Modifier1 => ModifierKey.Control;
|
||||||
|
public override ModifierKey Modifier2 => ModifierKey.Shift;
|
||||||
|
|
||||||
public override bool Enabled(RoleBehaviour? role)
|
public override bool Enabled(RoleBehaviour? role)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using MiraAPI.Example.Roles;
|
|||||||
using MiraAPI.GameEnd;
|
using MiraAPI.GameEnd;
|
||||||
using MiraAPI.Hud;
|
using MiraAPI.Hud;
|
||||||
using MiraAPI.Utilities.Assets;
|
using MiraAPI.Utilities.Assets;
|
||||||
|
using Rewired;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace MiraAPI.Example.Buttons;
|
namespace MiraAPI.Example.Buttons;
|
||||||
@@ -12,6 +13,7 @@ public class NeutralKillerButton : CustomActionButton
|
|||||||
public override string Name => "Win Game";
|
public override string Name => "Win Game";
|
||||||
public override float Cooldown => 0f;
|
public override float Cooldown => 0f;
|
||||||
public override LoadableAsset<Sprite> Sprite => ExampleAssets.ExampleButton;
|
public override LoadableAsset<Sprite> Sprite => ExampleAssets.ExampleButton;
|
||||||
|
public override KeyboardKeyCode Defaultkeybind => KeyboardKeyCode.K;
|
||||||
protected override void OnClick()
|
protected override void OnClick()
|
||||||
{
|
{
|
||||||
CustomGameOver.Trigger<NeutralKillerGameOver>([PlayerControl.LocalPlayer.Data]);
|
CustomGameOver.Trigger<NeutralKillerGameOver>([PlayerControl.LocalPlayer.Data]);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using MiraAPI.GameOptions;
|
|||||||
using MiraAPI.Hud;
|
using MiraAPI.Hud;
|
||||||
using MiraAPI.Utilities.Assets;
|
using MiraAPI.Utilities.Assets;
|
||||||
using Reactor.Utilities;
|
using Reactor.Utilities;
|
||||||
|
using Rewired;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace MiraAPI.Example.Buttons.Teleporter;
|
namespace MiraAPI.Example.Buttons.Teleporter;
|
||||||
@@ -21,7 +22,7 @@ public class TeleportButton : CustomActionButton
|
|||||||
public override LoadableAsset<Sprite> Sprite => ExampleAssets.TeleportButton;
|
public override LoadableAsset<Sprite> Sprite => ExampleAssets.TeleportButton;
|
||||||
public override Color TextOutlineColor => new Color32(221, 176, 152, 255);
|
public override Color TextOutlineColor => new Color32(221, 176, 152, 255);
|
||||||
public static bool IsZoom { get; private set; }
|
public static bool IsZoom { get; private set; }
|
||||||
|
public override KeyboardKeyCode Defaultkeybind => KeyboardKeyCode.M;
|
||||||
public override bool Enabled(RoleBehaviour? role)
|
public override bool Enabled(RoleBehaviour? role)
|
||||||
{
|
{
|
||||||
return role is TeleporterRole;
|
return role is TeleporterRole;
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
namespace MiraAPI.Events.Vanilla.Gameplay;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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 IntroCutscene or ExileController.
|
||||||
|
/// </summary>
|
||||||
|
public bool TriggeredByIntro { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="BeforeRoundStartEvent"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="triggeredByIntro">Whether the event was triggered by the intro or not.</param>
|
||||||
|
public BeforeRoundStartEvent(bool triggeredByIntro)
|
||||||
|
{
|
||||||
|
TriggeredByIntro = triggeredByIntro;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
namespace MiraAPI.Events.Vanilla.Gameplay;
|
namespace MiraAPI.Events.Vanilla.Gameplay;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Start Round event, invoked on IntroCutscene.OnDestroy and ExileController.WrapUp.
|
/// Start Round event, invoked on IntroCutscene.OnDestroy and ExileController.WrapUp if the event isn't cancelled.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class RoundStartEvent : MiraEvent
|
public class RoundStartEvent : MiraEvent
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
using System;
|
||||||
|
using MiraAPI.Utilities;
|
||||||
|
using MiraAPI.Voting;
|
||||||
|
|
||||||
|
namespace MiraAPI.Events.Vanilla.Meeting.Voting;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked before the dummy selects their vote. If canceled, the dummy won't vote.
|
||||||
|
/// </summary>
|
||||||
|
public class DummyVoteEvent : MiraCancelableEvent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the dummy that needs to vote.
|
||||||
|
/// </summary>
|
||||||
|
public DummyBehaviour Dummy { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the instance of the dummy's vote data.
|
||||||
|
/// </summary>
|
||||||
|
public PlayerVoteData VoteData { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the dummy can vote to skip the meeting.
|
||||||
|
/// </summary>
|
||||||
|
public bool CanSkip { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a predicate for validating whether the dummy can vote on a player.
|
||||||
|
/// By default, excludes dead and disconnected players.
|
||||||
|
/// </summary>
|
||||||
|
public Predicate<PlayerControl> PlayerIsValid { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="DummyVoteEvent"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dummy">The voting dummy.</param>
|
||||||
|
public DummyVoteEvent(DummyBehaviour dummy)
|
||||||
|
{
|
||||||
|
Dummy = dummy;
|
||||||
|
VoteData = dummy.myPlayer.GetVoteData();
|
||||||
|
CanSkip = true;
|
||||||
|
PlayerIsValid = x => !x.Data.IsDead && !x.Data.Disconnected;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ using MiraAPI.Events.Mira;
|
|||||||
using MiraAPI.Patches;
|
using MiraAPI.Patches;
|
||||||
using MiraAPI.Utilities;
|
using MiraAPI.Utilities;
|
||||||
using MiraAPI.Utilities.Assets;
|
using MiraAPI.Utilities.Assets;
|
||||||
|
using Rewired;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.Events;
|
using UnityEngine.Events;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
@@ -52,6 +53,28 @@ public abstract class CustomActionButton
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual int MaxUses => 0;
|
public virtual int MaxUses => 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the current key binding for this button.
|
||||||
|
/// </summary>
|
||||||
|
public abstract KeyboardKeyCode Defaultkeybind { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The first optional modifier key (e.g., Control, Shift, Alt) that must be held with the main key to activate the button.
|
||||||
|
/// </summary>
|
||||||
|
public virtual ModifierKey Modifier1 => ModifierKey.None;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The second optional modifier key.
|
||||||
|
/// Set to <see cref="ModifierKey.None"/> if unused.
|
||||||
|
/// </summary>
|
||||||
|
public virtual ModifierKey Modifier2 => ModifierKey.None;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The third optional modifier key.
|
||||||
|
/// Set to <see cref="ModifierKey.None"/> if unused.
|
||||||
|
/// </summary>
|
||||||
|
public virtual ModifierKey Modifier3 => ModifierKey.None;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the button's text outline color.
|
/// Gets the button's text outline color.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Rewired;
|
||||||
|
|
||||||
|
namespace MiraAPI.Keybinds
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Lets you register and manage mod keybinds with conflict checks and rebinding support.
|
||||||
|
/// </summary>
|
||||||
|
public static class KeybindManager
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Stores all info for a registered keybind.
|
||||||
|
/// </summary>
|
||||||
|
public class KeybindEntry
|
||||||
|
{
|
||||||
|
/// <summary>Unique identifier for this keybind.</summary>
|
||||||
|
public string Id;
|
||||||
|
|
||||||
|
/// <summary>Text shown in the Among Us rebinding menu to describe this keybind.</summary>
|
||||||
|
public string Description;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The method that runs when this keybind is pressed.
|
||||||
|
/// </summary>
|
||||||
|
public Action Handler;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The key currently assigned to this keybind.
|
||||||
|
/// </summary>
|
||||||
|
public KeyboardKeyCode Key;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, this keybind will be checked for conflicts with other exclusive keybinds using the same key.
|
||||||
|
/// </summary>
|
||||||
|
public bool Exclusive;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The first optional modifier key (e.g., Control, Shift, Alt) that must be held with the main key.
|
||||||
|
/// </summary>
|
||||||
|
public ModifierKey Modifier1 = ModifierKey.None;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The second optional modifier key, if needed. Set to <c>ModifierKey.None</c> if unused.
|
||||||
|
/// </summary>
|
||||||
|
public ModifierKey Modifier2 = ModifierKey.None;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The third optional modifier key, if needed. Set to <c>ModifierKey.None</c> if unused.
|
||||||
|
/// </summary>
|
||||||
|
public ModifierKey Modifier3 = ModifierKey.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly List<KeybindEntry> Registered = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a new mod keybind.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">Unique identifier for the keybind.</param>
|
||||||
|
/// <param name="description">Description to display in UI.</param>
|
||||||
|
/// <param name="key">Default key to use if no rebind exists.</param>
|
||||||
|
/// <param name="handler">Method to invoke when the keybind is pressed.</param>
|
||||||
|
/// <param name="exclusive">If true, conflicts with other exclusive keybinds on the same key.</param>
|
||||||
|
/// <param name="modifier1">Optional first modifier key (e.g., <c>Control</c>, <c>Shift</c>, <c>Alt</c>) that must be held with the main key.</param>
|
||||||
|
/// <param name="modifier2">Optional second modifier key. Use <c>ModifierKey.None</c> if unused.</param>
|
||||||
|
/// <param name="modifier3">Optional third modifier key. Use <c>ModifierKey.None</c> if unused.</param>
|
||||||
|
/// <returns> Returns the key that was registered for this keybind. </returns>
|
||||||
|
public static KeyboardKeyCode Register(string id, string description, KeyboardKeyCode key, Action handler, bool exclusive = true, ModifierKey modifier1 = ModifierKey.None, ModifierKey modifier2 = ModifierKey.None, ModifierKey modifier3 = ModifierKey.None)
|
||||||
|
{
|
||||||
|
Registered.Add(new KeybindEntry
|
||||||
|
{
|
||||||
|
Id = id,
|
||||||
|
Description = description,
|
||||||
|
Key = key,
|
||||||
|
Handler = handler,
|
||||||
|
Exclusive = exclusive,
|
||||||
|
Modifier1 = modifier1,
|
||||||
|
Modifier2 = modifier2,
|
||||||
|
Modifier3 = modifier3,
|
||||||
|
});
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns all conflicts where exclusive keybinds use the same key.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A list of pairs of keybinds that conflict.</returns>
|
||||||
|
public static List<(KeybindEntry, KeybindEntry)> GetConflicts()
|
||||||
|
{
|
||||||
|
var conflicts = new List<(KeybindEntry, KeybindEntry)>();
|
||||||
|
for (int i = 0; i < Registered.Count; i++)
|
||||||
|
{
|
||||||
|
for (int j = i + 1; j < Registered.Count; j++)
|
||||||
|
{
|
||||||
|
var a = Registered[i];
|
||||||
|
var b = Registered[j];
|
||||||
|
if (a.Key == b.Key && a.Exclusive && b.Exclusive)
|
||||||
|
{
|
||||||
|
conflicts.Add((a, b));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return conflicts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns all currently registered keybind entries.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A list of all registered keybind entries.</returns>
|
||||||
|
public static List<KeybindEntry> GetEntries()
|
||||||
|
{
|
||||||
|
return Registered;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using BepInEx;
|
||||||
|
using BepInEx.Configuration;
|
||||||
|
using BepInEx.Unity.IL2CPP;
|
||||||
|
using HarmonyLib;
|
||||||
|
using MiraAPI.PluginLoading;
|
||||||
|
using Reactor;
|
||||||
|
using Reactor.Networking;
|
||||||
|
using Reactor.Networking.Attributes;
|
||||||
|
using Reactor.Utilities;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace MiraAPI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Mira API Config File Handler
|
||||||
|
/// </summary>
|
||||||
|
public class MiraApiConfig(ConfigFile config)
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets whether the modifiers hud should be on the left side of the screen (under roles/task tab). Recommended for streamers.
|
||||||
|
/// </summary>
|
||||||
|
public ConfigEntry<bool> ModifiersHudLeftSide { get; private set; } = config.Bind("Displays", "Show Modifiers HUD on Left Side", false);
|
||||||
|
}
|
||||||
@@ -30,6 +30,10 @@ public partial class MiraApiPlugin : BasePlugin
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static Color DefaultHeaderColor { get; } = new Color32(77, 77, 77, 255);
|
public static Color DefaultHeaderColor { get; } = new Color32(77, 77, 77, 255);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the Mira Api Config Handler.
|
||||||
|
/// </summary>
|
||||||
|
public MiraApiConfig? MiraConfig { get; private set; }
|
||||||
private static MiraPluginManager? PluginManager { get; set; }
|
private static MiraPluginManager? PluginManager { get; set; }
|
||||||
internal Harmony Harmony { get; } = new(Id);
|
internal Harmony Harmony { get; } = new(Id);
|
||||||
|
|
||||||
@@ -40,6 +44,7 @@ public partial class MiraApiPlugin : BasePlugin
|
|||||||
|
|
||||||
ReactorCredits.Register("Mira API", Version, true, ReactorCredits.AlwaysShow);
|
ReactorCredits.Register("Mira API", Version, true, ReactorCredits.AlwaysShow);
|
||||||
|
|
||||||
|
MiraConfig = new MiraApiConfig(Config);
|
||||||
PluginManager = new MiraPluginManager();
|
PluginManager = new MiraPluginManager();
|
||||||
PluginManager.Initialize();
|
PluginManager.Initialize();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,6 +64,11 @@ public abstract class BaseModifier : IOptionable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual bool ShowInFreeplay => false;
|
public virtual bool ShowInFreeplay => false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating the color that should be used for the modifier within freeplay.
|
||||||
|
/// </summary>
|
||||||
|
public virtual Color FreeplayFileColor => Color.gray;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether the modifier is unique. If true, the player can only have one instance of this modifier.
|
/// Gets a value indicating whether the modifier is unique. If true, the player can only have one instance of this modifier.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using System.Linq;
|
|||||||
using Il2CppInterop.Runtime.Attributes;
|
using Il2CppInterop.Runtime.Attributes;
|
||||||
using MiraAPI.Modifiers.ModifierDisplay;
|
using MiraAPI.Modifiers.ModifierDisplay;
|
||||||
using MiraAPI.Modifiers.Types;
|
using MiraAPI.Modifiers.Types;
|
||||||
|
using MiraAPI.Patches.Roles;
|
||||||
using Reactor.Utilities;
|
using Reactor.Utilities;
|
||||||
using Reactor.Utilities.Attributes;
|
using Reactor.Utilities.Attributes;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
@@ -126,7 +127,18 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
|
|||||||
|
|
||||||
if (_player.AmOwner && ModifierDisplay && HudManager.InstanceExists)
|
if (_player.AmOwner && ModifierDisplay && HudManager.InstanceExists)
|
||||||
{
|
{
|
||||||
ModifierDisplay!.gameObject.SetActive(MeetingHud.Instance || HudManager.Instance.TaskPanel.isActiveAndEnabled);
|
var taskPanelOpen = HudManager.Instance.TaskPanel.open;
|
||||||
|
var roleTabOpen = HudManagerPatches.RoleTab != null && HudManagerPatches.RoleTab.open;
|
||||||
|
var leftSideHud = PluginSingleton<MiraApiPlugin>.Instance.MiraConfig!.ModifiersHudLeftSide.Value;
|
||||||
|
|
||||||
|
if (leftSideHud && (taskPanelOpen || roleTabOpen))
|
||||||
|
{
|
||||||
|
ModifierDisplay!.gameObject.SetActive(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ModifierDisplay!.gameObject.SetActive(MeetingHud.Instance || HudManager.Instance.TaskPanel.isActiveAndEnabled);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using Il2CppInterop.Runtime.Attributes;
|
using Il2CppInterop.Runtime.Attributes;
|
||||||
|
using MiraAPI.Patches.Roles;
|
||||||
using MiraAPI.Utilities.Assets;
|
using MiraAPI.Utilities.Assets;
|
||||||
|
using Reactor.Utilities;
|
||||||
using Reactor.Utilities.Attributes;
|
using Reactor.Utilities.Attributes;
|
||||||
using Reactor.Utilities.Extensions;
|
using Reactor.Utilities.Extensions;
|
||||||
using TMPro;
|
using TMPro;
|
||||||
@@ -94,6 +97,16 @@ public class ModifierDisplayComponent(nint cppPtr) : MonoBehaviour(cppPtr)
|
|||||||
|
|
||||||
IsOpen = false;
|
IsOpen = false;
|
||||||
_children.gameObject.SetActive(false);
|
_children.gameObject.SetActive(false);
|
||||||
|
|
||||||
|
if (PluginSingleton<MiraApiPlugin>.Instance.MiraConfig!.ModifiersHudLeftSide.Value)
|
||||||
|
{
|
||||||
|
var aspect = GetComponent<AspectPosition>();
|
||||||
|
_toggleButton.transform.localPosition = new Vector3(-1f, 2.7f, 0f);
|
||||||
|
_children.transform.localPosition = new Vector3(-0.2f, -0.05f, 0f);
|
||||||
|
aspect.Alignment = AspectPosition.EdgeAlignments.LeftTop;
|
||||||
|
aspect.DistanceFromEdge = new Vector3(1.8f, 2.55f, -20f);
|
||||||
|
aspect.AdjustPosition();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -109,7 +122,16 @@ public class ModifierDisplayComponent(nint cppPtr) : MonoBehaviour(cppPtr)
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void ToggleTab()
|
public void ToggleTab()
|
||||||
{
|
{
|
||||||
IsOpen = !IsOpen;
|
SetOpened(!IsOpen);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Use this to set the visibility of the modifiers tab.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="val">Whether it should be visible or not.</param>
|
||||||
|
public void SetOpened(bool val)
|
||||||
|
{
|
||||||
|
IsOpen = val;
|
||||||
_children.gameObject.SetActive(IsOpen);
|
_children.gameObject.SetActive(IsOpen);
|
||||||
RefreshModifiers();
|
RefreshModifiers();
|
||||||
}
|
}
|
||||||
@@ -118,7 +140,6 @@ public class ModifierDisplayComponent(nint cppPtr) : MonoBehaviour(cppPtr)
|
|||||||
{
|
{
|
||||||
_modifiers.Remove(component.Modifier!);
|
_modifiers.Remove(component.Modifier!);
|
||||||
component.gameObject.DestroyImmediate();
|
component.gameObject.DestroyImmediate();
|
||||||
|
|
||||||
RefreshModifiers();
|
RefreshModifiers();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,13 +215,7 @@ public class ModifierDisplayComponent(nint cppPtr) : MonoBehaviour(cppPtr)
|
|||||||
|
|
||||||
_toggleBtnText.text = $"Modifiers ({filteredModifiers.Count})";
|
_toggleBtnText.text = $"Modifiers ({filteredModifiers.Count})";
|
||||||
|
|
||||||
if (filteredModifiers.Count == 0)
|
_toggleButton.gameObject.SetActive(filteredModifiers.Count != 0);
|
||||||
{
|
|
||||||
_toggleButton.gameObject.SetActive(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_toggleButton.gameObject.SetActive(true);
|
|
||||||
|
|
||||||
foreach (var mod in modifiersToAdd)
|
foreach (var mod in modifiersToAdd)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using System.Runtime.Serialization;
|
|||||||
using MiraAPI.Modifiers.Types;
|
using MiraAPI.Modifiers.Types;
|
||||||
using MiraAPI.PluginLoading;
|
using MiraAPI.PluginLoading;
|
||||||
using MiraAPI.Roles;
|
using MiraAPI.Roles;
|
||||||
|
using MiraAPI.Utilities;
|
||||||
using Reactor.Utilities;
|
using Reactor.Utilities;
|
||||||
using Reactor.Utilities.Extensions;
|
using Reactor.Utilities.Extensions;
|
||||||
using Random = System.Random;
|
using Random = System.Random;
|
||||||
@@ -125,6 +126,7 @@ public static class ModifierManager
|
|||||||
.Select(x => Activator.CreateInstance(x.Value) as GameModifier)
|
.Select(x => Activator.CreateInstance(x.Value) as GameModifier)
|
||||||
.OfType<GameModifier>()
|
.OfType<GameModifier>()
|
||||||
.Where(x => x.GetAmountPerGame() > 0 && x.GetAssignmentChance() > 0)
|
.Where(x => x.GetAmountPerGame() > 0 && x.GetAssignmentChance() > 0)
|
||||||
|
.Shuffle()
|
||||||
.OrderByDescending(x => x.Priority())
|
.OrderByDescending(x => x.Priority())
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace MiraAPI.Patches;
|
namespace MiraAPI.Patches;
|
||||||
|
|
||||||
|
#pragma warning disable SA1629
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Allows players to paste text into chat.
|
/// Allows players to paste text into chat.
|
||||||
/// Source: https://github.com/CallOfCreator/NewMod/blob/main/NewMod/Patches/ClipboardPatch.cs
|
/// Source: https://github.com/CallOfCreator/NewMod/blob/main/NewMod/Patches/ClipboardPatch.cs
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
#pragma warning restore SA1629
|
||||||
[HarmonyPatch(typeof(ChatController), nameof(ChatController.Update))]
|
[HarmonyPatch(typeof(ChatController), nameof(ChatController.Update))]
|
||||||
public static class ChatControllerPatch
|
public static class ChatControllerPatch
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using HarmonyLib;
|
||||||
|
using MiraAPI.Events;
|
||||||
|
using MiraAPI.Events.Vanilla.Gameplay;
|
||||||
|
|
||||||
|
namespace MiraAPI.Patches.Events;
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(TutorialManager._RunTutorial_d__3), nameof(TutorialManager._RunTutorial_d__3.MoveNext))]
|
||||||
|
public static class FreeplayRoundStartPatch
|
||||||
|
{
|
||||||
|
public static void Postfix(TutorialManager._RunTutorial_d__3 __instance, ref bool __result)
|
||||||
|
{
|
||||||
|
if (!__result)
|
||||||
|
{
|
||||||
|
MiraEventManager.InvokeEvent(new RoundStartEvent(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,7 +9,12 @@ internal static class SpawnInMinigamePatch
|
|||||||
{
|
{
|
||||||
public static void Postfix()
|
public static void Postfix()
|
||||||
{
|
{
|
||||||
var @event = new RoundStartEvent(false);
|
var @event = new BeforeRoundStartEvent(false);
|
||||||
MiraEventManager.InvokeEvent(@event);
|
MiraEventManager.InvokeEvent(@event);
|
||||||
|
|
||||||
|
if (@event.IsCancelled) return;
|
||||||
|
|
||||||
|
var @event2 = new RoundStartEvent(false);
|
||||||
|
MiraEventManager.InvokeEvent(@event2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25,7 +25,12 @@ internal static class SubmergedSpawnGamePatch
|
|||||||
|
|
||||||
public static void Postfix()
|
public static void Postfix()
|
||||||
{
|
{
|
||||||
var @event = new RoundStartEvent(false);
|
var @event = new BeforeRoundStartEvent(false);
|
||||||
MiraEventManager.InvokeEvent(@event);
|
MiraEventManager.InvokeEvent(@event);
|
||||||
|
|
||||||
|
if (@event.IsCancelled) return;
|
||||||
|
|
||||||
|
var @event2 = new RoundStartEvent(false);
|
||||||
|
MiraEventManager.InvokeEvent(@event2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,15 +22,4 @@ internal static class TaskAddButtonPatches
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HarmonyPostfix]
|
|
||||||
[HarmonyPatch(nameof(TaskAddButton.Role), MethodType.Setter)]
|
|
||||||
public static void RoleGetterPatch(TaskAddButton __instance)
|
|
||||||
{
|
|
||||||
if (__instance.role is ICustomRole { Team: ModdedRoleTeams.Custom } customRole)
|
|
||||||
{
|
|
||||||
__instance.FileImage.color = customRole.IntroConfiguration?.IntroTeamColor ?? Color.gray;
|
|
||||||
}
|
|
||||||
__instance.RolloverHandler.OutColor = __instance.FileImage.color;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,7 @@ using MiraAPI.Utilities;
|
|||||||
using MiraAPI.Utilities.Assets;
|
using MiraAPI.Utilities.Assets;
|
||||||
using TMPro;
|
using TMPro;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using UnityEngine.Events;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
using Object = UnityEngine.Object;
|
using Object = UnityEngine.Object;
|
||||||
|
|
||||||
@@ -19,7 +20,6 @@ namespace MiraAPI.Patches.Freeplay;
|
|||||||
internal static class TaskAdderPatches
|
internal static class TaskAdderPatches
|
||||||
{
|
{
|
||||||
private static Scroller _scroller = null!;
|
private static Scroller _scroller = null!;
|
||||||
|
|
||||||
[HarmonyPostfix]
|
[HarmonyPostfix]
|
||||||
[HarmonyPatch(nameof(TaskAdderGame.Begin))]
|
[HarmonyPatch(nameof(TaskAdderGame.Begin))]
|
||||||
public static void AddRolesFolder(TaskAdderGame __instance)
|
public static void AddRolesFolder(TaskAdderGame __instance)
|
||||||
@@ -32,6 +32,11 @@ internal static class TaskAdderPatches
|
|||||||
_scroller.allowY = true;
|
_scroller.allowY = true;
|
||||||
_scroller.Inner = inner.transform;
|
_scroller.Inner = inner.transform;
|
||||||
|
|
||||||
|
var scrollerHelper = __instance.TaskParent.gameObject.AddComponent<ManualScrollHelper>();
|
||||||
|
scrollerHelper.scroller = _scroller;
|
||||||
|
scrollerHelper.verticalAxis = RewiredConstsEnum.Action.TaskRVertical;
|
||||||
|
scrollerHelper.scrollSpeed = 10f;
|
||||||
|
|
||||||
GameObject hitbox = new("Hitbox")
|
GameObject hitbox = new("Hitbox")
|
||||||
{
|
{
|
||||||
layer = 5,
|
layer = 5,
|
||||||
@@ -295,7 +300,7 @@ internal static class TaskAdderPatches
|
|||||||
|
|
||||||
if (_scroller)
|
if (_scroller)
|
||||||
{
|
{
|
||||||
_scroller.CalculateAndSetYBounds(__instance.ActiveItems.Count, 6, 5, 1.4f);
|
_scroller.CalculateAndSetYBounds(__instance.ActiveItems.Count, 6, 4.5f, 1.65f);
|
||||||
_scroller.SetYBoundsMin(0.0f);
|
_scroller.SetYBoundsMin(0.0f);
|
||||||
_scroller.ScrollToTop();
|
_scroller.ScrollToTop();
|
||||||
}
|
}
|
||||||
@@ -332,7 +337,8 @@ internal static class TaskAdderPatches
|
|||||||
taskAddButton.SafePositionWorld = instance.SafePositionWorld;
|
taskAddButton.SafePositionWorld = instance.SafePositionWorld;
|
||||||
taskAddButton.Text.text = modifier.ModifierName;
|
taskAddButton.Text.text = modifier.ModifierName;
|
||||||
taskAddButton.Text.EnableMasking();
|
taskAddButton.Text.EnableMasking();
|
||||||
|
taskAddButton.FileImage.color = modifier.FreeplayFileColor;
|
||||||
|
taskAddButton.RolloverHandler.OutColor = modifier.FreeplayFileColor;
|
||||||
taskAddButton.Button.OnClick = new Button.ButtonClickedEvent();
|
taskAddButton.Button.OnClick = new Button.ButtonClickedEvent();
|
||||||
taskAddButton.Button.OnClick.AddListener((UnityEngine.Events.UnityAction)(() =>
|
taskAddButton.Button.OnClick.AddListener((UnityEngine.Events.UnityAction)(() =>
|
||||||
{
|
{
|
||||||
@@ -384,6 +390,20 @@ internal static class TaskAdderPatches
|
|||||||
|
|
||||||
instance.AddFileAsChildCustom(taskAddButton2, ref num, ref num2, ref num3);
|
instance.AddFileAsChildCustom(taskAddButton2, ref num, ref num2, ref num3);
|
||||||
taskAddButton2.Role = roleBehaviour;
|
taskAddButton2.Role = roleBehaviour;
|
||||||
|
if (roleBehaviour is ICustomRole custom)
|
||||||
|
{
|
||||||
|
var customColor = custom.IntroConfiguration?.IntroTeamColor ?? Color.gray;
|
||||||
|
if (custom.Team is ModdedRoleTeams.Crewmate)
|
||||||
|
{
|
||||||
|
customColor = Palette.CrewmateBlue;
|
||||||
|
}
|
||||||
|
else if (custom.Team is ModdedRoleTeams.Impostor)
|
||||||
|
{
|
||||||
|
customColor = Palette.ImpostorRed;
|
||||||
|
}
|
||||||
|
taskAddButton2.FileImage.color = customColor;
|
||||||
|
taskAddButton2.RolloverHandler.OutColor = customColor;
|
||||||
|
}
|
||||||
if (taskAddButton2.Button == null)
|
if (taskAddButton2.Button == null)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using HarmonyLib;
|
||||||
|
using MiraAPI.Keybinds;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace MiraAPI.Patches;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks keybind conflicts in the lobby and disables secondary ones.
|
||||||
|
/// </summary>
|
||||||
|
[HarmonyPatch(typeof(GameStartManager))]
|
||||||
|
public static class GameStartManagerPatch
|
||||||
|
{
|
||||||
|
public static bool conflictShown = false;
|
||||||
|
|
||||||
|
[HarmonyPatch(nameof(GameStartManager.Start))]
|
||||||
|
[HarmonyPostfix]
|
||||||
|
public static void StartPostfix(GameStartManager __instance)
|
||||||
|
{
|
||||||
|
if (conflictShown) return;
|
||||||
|
|
||||||
|
var conflicts = KeybindManager.GetConflicts();
|
||||||
|
if (conflicts.Count == 0) return;
|
||||||
|
|
||||||
|
conflictShown = true;
|
||||||
|
|
||||||
|
var message = "The following keybinds are disabled due to conflicts:\n\n";
|
||||||
|
|
||||||
|
foreach (var (a, b) in conflicts)
|
||||||
|
{
|
||||||
|
message += $"• <color=#FF5555>{b.Description}</color> (conflicts with {a.Description})\n";
|
||||||
|
b.Handler = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
HudManager.Instance.ShowPopUp(message.TrimEnd());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
using HarmonyLib;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using HarmonyLib;
|
||||||
using MiraAPI.Hud;
|
using MiraAPI.Hud;
|
||||||
|
using MiraAPI.Keybinds;
|
||||||
using Reactor.Utilities;
|
using Reactor.Utilities;
|
||||||
using Reactor.Utilities.Extensions;
|
using Reactor.Utilities.Extensions;
|
||||||
|
using Rewired;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using Object = UnityEngine.Object;
|
using Object = UnityEngine.Object;
|
||||||
|
|
||||||
@@ -135,4 +139,26 @@ public static class HudManagerPatches
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch(nameof(HudManager.Update))]
|
||||||
|
[HarmonyPostfix]
|
||||||
|
public static void StartPostfix()
|
||||||
|
{
|
||||||
|
if (AmongUsClient.Instance.GameState != InnerNet.InnerNetClient.GameStates.Started) return;
|
||||||
|
if (PlayerControl.LocalPlayer.Data.IsDead) return;
|
||||||
|
|
||||||
|
foreach (var entry in KeybindManager.GetEntries())
|
||||||
|
{
|
||||||
|
var player = ReInput.players.GetPlayer(0);
|
||||||
|
var keyboard = player.controllers.Keyboard;
|
||||||
|
|
||||||
|
if (player.GetButtonDown(entry.Id) &&
|
||||||
|
(entry.Modifier1 == ModifierKey.None || keyboard.GetModifierKey(entry.Modifier1)) &&
|
||||||
|
(entry.Modifier2 == ModifierKey.None || keyboard.GetModifierKey(entry.Modifier2)) &&
|
||||||
|
(entry.Modifier3 == ModifierKey.None || keyboard.GetModifierKey(entry.Modifier3)))
|
||||||
|
{
|
||||||
|
entry.Handler?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -65,7 +65,12 @@ public static class IntroCutscenePatches
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MiraEventManager.InvokeEvent(new IntroEndEvent(__instance.__4__this));
|
MiraEventManager.InvokeEvent(new IntroEndEvent(__instance));
|
||||||
|
|
||||||
|
var @event = new BeforeRoundStartEvent(true);
|
||||||
|
MiraEventManager.InvokeEvent(@event);
|
||||||
|
|
||||||
|
if (@event.IsCancelled) return;
|
||||||
MiraEventManager.InvokeEvent(new RoundStartEvent(true));
|
MiraEventManager.InvokeEvent(new RoundStartEvent(true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using HarmonyLib;
|
||||||
|
using MiraAPI.Keybinds;
|
||||||
|
using MiraAPI.Utilities;
|
||||||
|
using Rewired;
|
||||||
|
|
||||||
|
namespace MiraAPI.Patches.Keybinds
|
||||||
|
{
|
||||||
|
[HarmonyPatch(typeof(InputManager_Base), nameof(InputManager_Base.Awake))]
|
||||||
|
public static class KeybindMenuPatch
|
||||||
|
{
|
||||||
|
[HarmonyPrefix]
|
||||||
|
private static void StartPrefix(InputManager_Base __instance)
|
||||||
|
{
|
||||||
|
foreach (var entry in KeybindManager.GetEntries())
|
||||||
|
{
|
||||||
|
__instance.userData.RegisterModBind(entry.Id, entry.Description, entry.Key, modifier1: entry.Modifier1, modifier2: entry.Modifier2, modifier3: entry.Modifier3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -84,7 +84,7 @@ public static class HatsTabPatches
|
|||||||
text.gameObject.transform.localScale = Vector3.one;
|
text.gameObject.transform.localScale = Vector3.one;
|
||||||
text.GetComponent<TextTranslatorTMP>().Destroy();
|
text.GetComponent<TextTranslatorTMP>().Destroy();
|
||||||
text.EnableStencilMasking();
|
text.EnableStencilMasking();
|
||||||
text.text = $"{groupName}\nPress Ctrl or Tab to cycle pages";
|
text.text = $"{groupName} ({currentPage + 1}/{sortedHats.Count})\nPress Ctrl or Tab to cycle pages";
|
||||||
text.alignment = TextAlignmentOptions.Center;
|
text.alignment = TextAlignmentOptions.Center;
|
||||||
text.fontSize = 3f;
|
text.fontSize = 3f;
|
||||||
text.fontSizeMax = 3f;
|
text.fontSizeMax = 3f;
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ public static class NameplatesTabPatches
|
|||||||
text.gameObject.transform.localScale = Vector3.one;
|
text.gameObject.transform.localScale = Vector3.one;
|
||||||
text.GetComponent<TextTranslatorTMP>().Destroy();
|
text.GetComponent<TextTranslatorTMP>().Destroy();
|
||||||
text.EnableStencilMasking();
|
text.EnableStencilMasking();
|
||||||
text.text = $"{groupName}\nPress Ctrl or Tab to cycle pages";
|
text.text = $"{groupName} ({currentPage + 1}/{SortedNameplates.Count})\nPress Ctrl or Tab to cycle pages";
|
||||||
text.alignment = TextAlignmentOptions.Center;
|
text.alignment = TextAlignmentOptions.Center;
|
||||||
text.fontSize = 3f;
|
text.fontSize = 3f;
|
||||||
text.fontSizeMax = 3f;
|
text.fontSizeMax = 3f;
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ public static class SkinsTabPatches
|
|||||||
text.gameObject.transform.localScale = Vector3.one;
|
text.gameObject.transform.localScale = Vector3.one;
|
||||||
text.GetComponent<TextTranslatorTMP>().Destroy();
|
text.GetComponent<TextTranslatorTMP>().Destroy();
|
||||||
text.EnableStencilMasking();
|
text.EnableStencilMasking();
|
||||||
text.text = $"{groupName}\nPress Ctrl or Tab to cycle pages";
|
text.text = $"{groupName} ({currentPage + 1}/{sortedSkins.Count})\nPress Ctrl or Tab to cycle pages";
|
||||||
text.alignment = TextAlignmentOptions.Center;
|
text.alignment = TextAlignmentOptions.Center;
|
||||||
text.fontSize = 3f;
|
text.fontSize = 3f;
|
||||||
text.fontSizeMax = 3f;
|
text.fontSizeMax = 3f;
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ public static class VisorsTabPatches
|
|||||||
text.gameObject.transform.localScale = Vector3.one;
|
text.gameObject.transform.localScale = Vector3.one;
|
||||||
text.GetComponent<TextTranslatorTMP>().Destroy();
|
text.GetComponent<TextTranslatorTMP>().Destroy();
|
||||||
text.EnableStencilMasking();
|
text.EnableStencilMasking();
|
||||||
text.text = $"{groupName}\nPress Ctrl or Tab to cycle pages";
|
text.text = $"{groupName} ({currentPage + 1}/{SortedVisors.Count})\nPress Ctrl or Tab to cycle pages";
|
||||||
text.alignment = TextAlignmentOptions.Center;
|
text.alignment = TextAlignmentOptions.Center;
|
||||||
text.fontSize = 3f;
|
text.fontSize = 3f;
|
||||||
text.fontSizeMax = 3f;
|
text.fontSizeMax = 3f;
|
||||||
|
|||||||
@@ -38,8 +38,13 @@ internal static class EjectionPatches
|
|||||||
{
|
{
|
||||||
public static void Postfix()
|
public static void Postfix()
|
||||||
{
|
{
|
||||||
var @event = new RoundStartEvent(false);
|
var @event = new BeforeRoundStartEvent(false);
|
||||||
MiraEventManager.InvokeEvent(@event);
|
MiraEventManager.InvokeEvent(@event);
|
||||||
|
|
||||||
|
if (@event.IsCancelled) return;
|
||||||
|
|
||||||
|
var @event2 = new RoundStartEvent(false);
|
||||||
|
MiraEventManager.InvokeEvent(@event2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,7 @@ namespace MiraAPI.Patches.Roles;
|
|||||||
public static class HudManagerPatches
|
public static class HudManagerPatches
|
||||||
{
|
{
|
||||||
// Custom role tab.
|
// Custom role tab.
|
||||||
private static TaskPanelBehaviour? _roleTab;
|
public static TaskPanelBehaviour? RoleTab;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fixes Kill Button not showing for Neutral killing role.
|
/// Fixes Kill Button not showing for Neutral killing role.
|
||||||
@@ -38,9 +38,9 @@ public static class HudManagerPatches
|
|||||||
__instance.SabotageButton.gameObject.SetActive(isActive && customRole.Configuration.CanUseSabotage);
|
__instance.SabotageButton.gameObject.SetActive(isActive && customRole.Configuration.CanUseSabotage);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_roleTab)
|
if (RoleTab)
|
||||||
{
|
{
|
||||||
_roleTab?.gameObject.SetActive(isActive);
|
RoleTab?.gameObject.SetActive(isActive);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,16 +62,16 @@ public static class HudManagerPatches
|
|||||||
|
|
||||||
if (role is ICustomRole { Configuration.RoleHintType: RoleHintType.RoleTab } customRole)
|
if (role is ICustomRole { Configuration.RoleHintType: RoleHintType.RoleTab } customRole)
|
||||||
{
|
{
|
||||||
if (_roleTab == null)
|
if (RoleTab == null)
|
||||||
{
|
{
|
||||||
_roleTab = CustomRoleManager.CreateRoleTab(customRole);
|
RoleTab = CustomRoleManager.CreateRoleTab(customRole);
|
||||||
}
|
}
|
||||||
|
|
||||||
CustomRoleManager.UpdateRoleTab(_roleTab, customRole);
|
CustomRoleManager.UpdateRoleTab(RoleTab, customRole);
|
||||||
}
|
}
|
||||||
else if (_roleTab)
|
else if (RoleTab)
|
||||||
{
|
{
|
||||||
_roleTab?.gameObject.Destroy();
|
RoleTab?.gameObject.Destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.Events;
|
using UnityEngine.Events;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
|
|
||||||
namespace MiraAPI.Patches;
|
namespace MiraAPI.Patches;
|
||||||
|
|
||||||
@@ -14,6 +15,8 @@ public static class ServerDropdownPatch
|
|||||||
|
|
||||||
foreach (var regionInfo in DestroyableSingleton<ServerManager>.Instance.AvailableRegions)
|
foreach (var regionInfo in DestroyableSingleton<ServerManager>.Instance.AvailableRegions)
|
||||||
{
|
{
|
||||||
|
var findingGame = SceneManager.GetActiveScene().name is "FindAGame";
|
||||||
|
|
||||||
if (DestroyableSingleton<ServerManager>.Instance.CurrentRegion.Equals(regionInfo))
|
if (DestroyableSingleton<ServerManager>.Instance.CurrentRegion.Equals(regionInfo))
|
||||||
{
|
{
|
||||||
__instance.defaultButtonSelected = __instance.firstOption;
|
__instance.defaultButtonSelected = __instance.firstOption;
|
||||||
@@ -27,6 +30,10 @@ public static class ServerDropdownPatch
|
|||||||
var region = regionInfo;
|
var region = regionInfo;
|
||||||
var serverListButton = __instance.ButtonPool.Get<ServerListButton>();
|
var serverListButton = __instance.ButtonPool.Get<ServerListButton>();
|
||||||
var x = num % 2 == 0 ? -2 : 2;
|
var x = num % 2 == 0 ? -2 : 2;
|
||||||
|
if (findingGame)
|
||||||
|
{
|
||||||
|
x += 2;
|
||||||
|
}
|
||||||
var y = -0.55f * (num / 2);
|
var y = -0.55f * (num / 2);
|
||||||
serverListButton.transform.localPosition = new Vector3(x, __instance.y_posButton + y, -1f);
|
serverListButton.transform.localPosition = new Vector3(x, __instance.y_posButton + y, -1f);
|
||||||
serverListButton.transform.localScale = Vector3.one;
|
serverListButton.transform.localScale = Vector3.one;
|
||||||
@@ -39,7 +46,7 @@ public static class ServerDropdownPatch
|
|||||||
serverListButton.Button.OnClick.AddListener((UnityAction)(() => { __instance.ChooseOption(region); }));
|
serverListButton.Button.OnClick.AddListener((UnityAction)(() => { __instance.ChooseOption(region); }));
|
||||||
__instance.controllerSelectable.Add(serverListButton.Button);
|
__instance.controllerSelectable.Add(serverListButton.Button);
|
||||||
__instance.background.transform.localPosition = new Vector3(
|
__instance.background.transform.localPosition = new Vector3(
|
||||||
0f,
|
findingGame ? 2f : 0f,
|
||||||
__instance.initialYPos + (-0.3f * (num / 2)),
|
__instance.initialYPos + (-0.3f * (num / 2)),
|
||||||
0f);
|
0f);
|
||||||
__instance.background.size = new Vector2(__instance.background.size.x, 1.2f + (0.6f * (num / 2)));
|
__instance.background.size = new Vector2(__instance.background.size.x, 1.2f + (0.6f * (num / 2)));
|
||||||
|
|||||||
@@ -1,16 +1,79 @@
|
|||||||
using HarmonyLib;
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using BepInEx.Unity.IL2CPP.Utils.Collections;
|
||||||
|
using HarmonyLib;
|
||||||
|
using MiraAPI.Events;
|
||||||
|
using MiraAPI.Events.Vanilla.Meeting.Voting;
|
||||||
using MiraAPI.Utilities;
|
using MiraAPI.Utilities;
|
||||||
|
using MiraAPI.Voting;
|
||||||
|
using Reactor.Utilities;
|
||||||
|
using Reactor.Utilities.Extensions;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
namespace MiraAPI.Patches.Voting;
|
namespace MiraAPI.Patches.Voting;
|
||||||
|
|
||||||
[HarmonyPatch(typeof(DummyBehaviour), nameof(DummyBehaviour.Update))]
|
[HarmonyPatch(typeof(DummyBehaviour))]
|
||||||
internal static class DummyBehaviourPatch
|
internal static class DummyBehaviourPatches
|
||||||
{
|
{
|
||||||
public static void Postfix(DummyBehaviour __instance)
|
[HarmonyPrefix]
|
||||||
|
[HarmonyPatch(nameof(DummyBehaviour.Update))]
|
||||||
|
public static bool DummyUpdatePatch(DummyBehaviour __instance)
|
||||||
{
|
{
|
||||||
if (MeetingHud.Instance && __instance.myPlayer.GetVoteData())
|
NetworkedPlayerInfo data = __instance.myPlayer.Data;
|
||||||
|
if (data == null || data.IsDead) return false;
|
||||||
|
|
||||||
|
if (MeetingHud.Instance)
|
||||||
{
|
{
|
||||||
__instance.voted = __instance.myPlayer.GetVoteData().VotesRemaining == 0;
|
if (!__instance.voted)
|
||||||
|
{
|
||||||
|
__instance.StartCoroutine(CoDoVote(__instance).WrapToIl2Cpp());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
__instance.voted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerator CoDoVote(DummyBehaviour dummy)
|
||||||
|
{
|
||||||
|
Logger<MiraApiPlugin>.Warning(dummy.voted);
|
||||||
|
dummy.voted = true;
|
||||||
|
yield return new WaitForSeconds(dummy.voteTime.Next());
|
||||||
|
|
||||||
|
var dummyVoteEvent = new DummyVoteEvent(dummy);
|
||||||
|
MiraEventManager.InvokeEvent(dummyVoteEvent);
|
||||||
|
|
||||||
|
if (dummyVoteEvent.IsCancelled)
|
||||||
|
{
|
||||||
|
dummy.voted = false;
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<byte> potentialSuspects = new();
|
||||||
|
potentialSuspects.AddRange(PlayerControl.AllPlayerControls
|
||||||
|
.ToArray()
|
||||||
|
.Where(p => p != dummy.myPlayer)
|
||||||
|
.Where(p => dummyVoteEvent.PlayerIsValid(p))
|
||||||
|
.Select(p => p.PlayerId));
|
||||||
|
|
||||||
|
if (dummyVoteEvent.CanSkip)
|
||||||
|
{
|
||||||
|
potentialSuspects.Add(253);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CanVote(dummy))
|
||||||
|
{
|
||||||
|
VotingUtils.RpcCastVote(PlayerControl.LocalPlayer, dummy.myPlayer.PlayerId, potentialSuspects.Random());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool CanVote(this DummyBehaviour dummy)
|
||||||
|
{
|
||||||
|
var dummyVoteData = dummy.myPlayer.GetVoteData();
|
||||||
|
return dummyVoteData.VotesRemaining > 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -11,12 +11,14 @@ using MiraAPI.GameEnd;
|
|||||||
using MiraAPI.GameOptions;
|
using MiraAPI.GameOptions;
|
||||||
using MiraAPI.GameOptions.Attributes;
|
using MiraAPI.GameOptions.Attributes;
|
||||||
using MiraAPI.Hud;
|
using MiraAPI.Hud;
|
||||||
|
using MiraAPI.Keybinds;
|
||||||
using MiraAPI.Modifiers;
|
using MiraAPI.Modifiers;
|
||||||
using MiraAPI.Presets;
|
using MiraAPI.Presets;
|
||||||
using MiraAPI.Roles;
|
using MiraAPI.Roles;
|
||||||
using MiraAPI.Utilities;
|
using MiraAPI.Utilities;
|
||||||
using Reactor.Networking;
|
using Reactor.Networking;
|
||||||
using Reactor.Utilities;
|
using Reactor.Utilities;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
namespace MiraAPI.PluginLoading;
|
namespace MiraAPI.PluginLoading;
|
||||||
|
|
||||||
@@ -129,9 +131,16 @@ public sealed class MiraPluginManager
|
|||||||
CustomButtonManager.Buttons = new ReadOnlyCollection<CustomActionButton>(CustomButtonManager.CustomButtons);
|
CustomButtonManager.Buttons = new ReadOnlyCollection<CustomActionButton>(CustomButtonManager.CustomButtons);
|
||||||
|
|
||||||
// Cache all the registered plugins into an array for easy access
|
// Cache all the registered plugins into an array for easy access
|
||||||
RegisteredPlugins = [.._registeredPlugins.Values];
|
RegisteredPlugins = [.. _registeredPlugins.Values];
|
||||||
|
|
||||||
ModifierManager.Modifiers = new ReadOnlyCollection<BaseModifier>(ModifierManager.InternalModifiers);
|
ModifierManager.Modifiers = new ReadOnlyCollection<BaseModifier>(ModifierManager.InternalModifiers);
|
||||||
|
|
||||||
|
foreach (var button in CustomButtonManager.Buttons)
|
||||||
|
{
|
||||||
|
KeybindManager.Register($"{button.Name}_Keybind", $"Keybind for {button.Name}", button.Defaultkeybind, button.ClickHandler, modifier1: button.Modifier1, modifier2: button.Modifier2, modifier3: button.Modifier3);
|
||||||
|
|
||||||
|
Logger<MiraApiPlugin>.Info($"Registered keybind for button '{button.GetType().Name}' with default key {button.Defaultkeybind}.");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace MiraAPI.Utilities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extensions for IEnumerable.
|
||||||
|
/// </summary>
|
||||||
|
public static class EnumerableExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Shuffle the elements of the source enumerable using a new instance of Random.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source">The source enumerable.</param>
|
||||||
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
||||||
|
/// <returns>>The shuffled enumerable.</returns>
|
||||||
|
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source)
|
||||||
|
{
|
||||||
|
return source.Shuffle(new Random());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shuffle the elements of the source enumerable using the provided Random instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source">The source enumerable.</param>
|
||||||
|
/// <param name="rng">The Random instance to use for shuffling.</param>
|
||||||
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
||||||
|
/// <returns>>The shuffled enumerable.</returns>
|
||||||
|
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(source);
|
||||||
|
ArgumentNullException.ThrowIfNull(rng);
|
||||||
|
|
||||||
|
return source.ShuffleIterator(rng);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<T> ShuffleIterator<T>(this IEnumerable<T> source, Random rng)
|
||||||
|
{
|
||||||
|
var buffer = source.ToList();
|
||||||
|
for (var i = 0; i < buffer.Count; i++)
|
||||||
|
{
|
||||||
|
var j = rng.Next(i, buffer.Count);
|
||||||
|
yield return buffer[j];
|
||||||
|
|
||||||
|
buffer[j] = buffer[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,8 @@ using MiraAPI.Networking;
|
|||||||
using MiraAPI.Roles;
|
using MiraAPI.Roles;
|
||||||
using MiraAPI.Voting;
|
using MiraAPI.Voting;
|
||||||
using Reactor.Utilities;
|
using Reactor.Utilities;
|
||||||
|
using Rewired;
|
||||||
|
using Rewired.Data;
|
||||||
using TMPro;
|
using TMPro;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
@@ -92,7 +94,7 @@ public static class Extensions
|
|||||||
/// <param name="timer">The current timer value.</param>
|
/// <param name="timer">The current timer value.</param>
|
||||||
/// <param name="maxTimer">The maximum timer value.</param>
|
/// <param name="maxTimer">The maximum timer value.</param>
|
||||||
/// <param name="format">The format string to use for the timer text.</param>
|
/// <param name="format">The format string to use for the timer text.</param>
|
||||||
public static void SetCooldownFormat(this ActionButton button, float timer, float maxTimer, string format="0")
|
public static void SetCooldownFormat(this ActionButton button, float timer, float maxTimer, string format = "0")
|
||||||
{
|
{
|
||||||
var num = Mathf.Clamp(timer / maxTimer, 0f, 1f);
|
var num = Mathf.Clamp(timer / maxTimer, 0f, 1f);
|
||||||
button.isCoolingDown = num > 0f;
|
button.isCoolingDown = num > 0f;
|
||||||
@@ -113,7 +115,7 @@ public static class Extensions
|
|||||||
/// <param name="timer">The current timer value.</param>
|
/// <param name="timer">The current timer value.</param>
|
||||||
/// <param name="maxTimer">The maximum timer value.</param>
|
/// <param name="maxTimer">The maximum timer value.</param>
|
||||||
/// <param name="format">The format string to use for the timer text.</param>
|
/// <param name="format">The format string to use for the timer text.</param>
|
||||||
public static void SetFillUpFormat(this ActionButton button, float timer, float maxTimer, string format="0")
|
public static void SetFillUpFormat(this ActionButton button, float timer, float maxTimer, string format = "0")
|
||||||
{
|
{
|
||||||
var num = Mathf.Clamp(timer / maxTimer, 0f, 1f);
|
var num = Mathf.Clamp(timer / maxTimer, 0f, 1f);
|
||||||
button.isCoolingDown = num > 0f;
|
button.isCoolingDown = num > 0f;
|
||||||
@@ -513,6 +515,7 @@ public static class Extensions
|
|||||||
/// <param name="includeImpostors">Whether impostors should be included in the search.</param>
|
/// <param name="includeImpostors">Whether impostors should be included in the search.</param>
|
||||||
/// <param name="distance">The radius to search within.</param>
|
/// <param name="distance">The radius to search within.</param>
|
||||||
/// <param name="ignoreColliders">Whether colliders should be ignored when searching.</param>
|
/// <param name="ignoreColliders">Whether colliders should be ignored when searching.</param>
|
||||||
|
/// <param name="includeGhosts">Determines if Ghosts are included.</param>
|
||||||
/// <param name="predicate">Optional predicate to test if the object is valid.</param>
|
/// <param name="predicate">Optional predicate to test if the object is valid.</param>
|
||||||
/// <returns>The closest player if there is one, false otherwise.</returns>
|
/// <returns>The closest player if there is one, false otherwise.</returns>
|
||||||
public static PlayerControl? GetClosestPlayer(
|
public static PlayerControl? GetClosestPlayer(
|
||||||
@@ -520,16 +523,16 @@ public static class Extensions
|
|||||||
bool includeImpostors,
|
bool includeImpostors,
|
||||||
float distance,
|
float distance,
|
||||||
bool ignoreColliders = false,
|
bool ignoreColliders = false,
|
||||||
|
bool includeGhosts = false,
|
||||||
Predicate<PlayerControl>? predicate = null)
|
Predicate<PlayerControl>? predicate = null)
|
||||||
{
|
{
|
||||||
var filteredPlayers = Helpers.GetClosestPlayers(playerControl, distance, ignoreColliders)
|
var filteredPlayers = Helpers.GetClosestPlayers(playerControl, distance, ignoreColliders)
|
||||||
.Where(
|
.Where(
|
||||||
playerInfo => !playerInfo.Data.Disconnected &&
|
playerInfo => !playerInfo.Data.Disconnected &&
|
||||||
playerInfo.PlayerId != playerControl.PlayerId &&
|
playerInfo.PlayerId != playerControl.PlayerId &&
|
||||||
!playerInfo.Data.IsDead &&
|
(includeGhosts || !playerInfo.Data.IsDead) &&
|
||||||
(includeImpostors || !playerInfo.Data.Role.IsImpostor))
|
(includeImpostors || !playerInfo.Data.Role.IsImpostor))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
return predicate != null ? filteredPlayers.Find(predicate) : filteredPlayers.FirstOrDefault();
|
return predicate != null ? filteredPlayers.Find(predicate) : filteredPlayers.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -544,4 +547,49 @@ public static class Extensions
|
|||||||
renderer.material.SetColor(ShaderID.OutlineColor, color ?? Color.clear);
|
renderer.material.SetColor(ShaderID.OutlineColor, color ?? Color.clear);
|
||||||
renderer.material.SetColor(ShaderID.AddColor, color ?? Color.clear);
|
renderer.material.SetColor(ShaderID.AddColor, color ?? Color.clear);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inspired by: https://github.com/eDonnes124/Town-Of-Us-R/blob/master/source/Patches/Keybinds.cs#L29
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a new mod keybind as a user-assignable button action in Rewired.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userData">The Rewired user data to add the action to.</param>
|
||||||
|
/// <param name="actionName">The internal name of the action.</param>
|
||||||
|
/// <param name="description">Text shown in the rebinding UI.</param>
|
||||||
|
/// <param name="key">The default key to assign to this action.</param>
|
||||||
|
/// <param name="category">Category ID to group actions in Rewired (default is 0).</param>
|
||||||
|
/// <param name="elementIdentifierId">The element identifier ID (default is -1, meaning none specified).</param>
|
||||||
|
/// <param name="type">The <see cref="InputActionType"/> for this action (default is Button).</param>
|
||||||
|
/// <param name="modifier1">The first optional modifier key (e.g., <c>Control</c>, <c>Shift</c>, <c>Alt</c>) that must be held together with the main key.</param>
|
||||||
|
/// <param name="modifier2">The second optional modifier key. Set to <see cref="ModifierKey.None"/> if not used.</param>
|
||||||
|
/// <param name="modifier3">The third optional modifier key. Set to <see cref="ModifierKey.None"/> if not used.</param>
|
||||||
|
/// <returns>The action ID of the newly registered action.</returns>
|
||||||
|
public static int RegisterModBind(this UserData userData, string actionName, string description, KeyboardKeyCode key, int category = 0, int elementIdentifierId = -1, InputActionType type = InputActionType.Button, ModifierKey modifier1 = ModifierKey.None, ModifierKey modifier2 = ModifierKey.None, ModifierKey modifier3 = ModifierKey.None)
|
||||||
|
{
|
||||||
|
userData.AddAction(category);
|
||||||
|
|
||||||
|
var action = userData.GetAction(userData.actions.Count - 1)!;
|
||||||
|
|
||||||
|
action.name = actionName;
|
||||||
|
action.descriptiveName = description;
|
||||||
|
action.categoryId = category;
|
||||||
|
action.type = type;
|
||||||
|
action.userAssignable = true;
|
||||||
|
|
||||||
|
var map = new ActionElementMap
|
||||||
|
{
|
||||||
|
_elementIdentifierId = elementIdentifierId,
|
||||||
|
_actionId = action.id,
|
||||||
|
_elementType = ControllerElementType.Button,
|
||||||
|
_axisContribution = Pole.Positive,
|
||||||
|
_keyboardKeyCode = key,
|
||||||
|
_modifierKey1 = modifier1,
|
||||||
|
_modifierKey2 = modifier2,
|
||||||
|
_modifierKey3 = modifier3,
|
||||||
|
};
|
||||||
|
userData.keyboardMaps[0].actionElementMaps.Add(map);
|
||||||
|
userData.joystickMaps[0].actionElementMaps.Add(map);
|
||||||
|
|
||||||
|
return action.id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
using System.Collections;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Il2CppSystem.IO;
|
||||||
|
using MiraAPI.Keybinds;
|
||||||
|
using Rewired;
|
||||||
using TMPro;
|
using TMPro;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using Object = UnityEngine.Object;
|
using Object = UnityEngine.Object;
|
||||||
@@ -19,7 +23,7 @@ public static class Helpers
|
|||||||
/// <returns>A list of alive players.</returns>
|
/// <returns>A list of alive players.</returns>
|
||||||
public static List<PlayerControl> GetAlivePlayers()
|
public static List<PlayerControl> GetAlivePlayers()
|
||||||
{
|
{
|
||||||
return [.. GameData.Instance.AllPlayers.ToArray().Where(x => !x.IsDead && !x.Disconnected && x.Object).Select(x=>x.Object)];
|
return [.. GameData.Instance.AllPlayers.ToArray().Where(x => !x.IsDead && !x.Disconnected && x.Object).Select(x => x.Object)];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -36,10 +40,10 @@ public static class Helpers
|
|||||||
case 100:
|
case 100:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
var num = Random.RandomRangeInt(1, 101);
|
var num = UnityEngine.Random.RandomRangeInt(1, 101);
|
||||||
return num <= probability;
|
return num <= probability;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -398,6 +402,25 @@ public static class Helpers
|
|||||||
public static string RandomString(int length, string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
|
public static string RandomString(int length, string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
|
||||||
{
|
{
|
||||||
return new string(Enumerable.Repeat(chars, length)
|
return new string(Enumerable.Repeat(chars, length)
|
||||||
.Select(s => s[Random.RandomRangeInt(0, s.Length)]).ToArray());
|
.Select(s => s[UnityEngine.Random.RandomRangeInt(0, s.Length)]).ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds and returns an unused KeyCode that is not equal to the excluded key.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="exclude">The KeyCode to skip during the search.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// The first available KeyCode not currently used by any registered keybind,
|
||||||
|
/// or KeyCode.None if none are available.
|
||||||
|
/// </returns>
|
||||||
|
public static KeyboardKeyCode FindAvailableKey(KeyboardKeyCode exclude)
|
||||||
|
{
|
||||||
|
foreach (KeyboardKeyCode key in Enum.GetValues(typeof(KeyboardKeyCode)))
|
||||||
|
{
|
||||||
|
if (key == exclude) continue;
|
||||||
|
bool used = KeybindManager.GetEntries().Exists(e => e.Key == key);
|
||||||
|
if (!used) return key;
|
||||||
|
}
|
||||||
|
return KeyboardKeyCode.None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user