Compare commits

..
Author SHA1 Message Date
XtraCube bc188191de Initial commit 2025-05-18 11:34:41 -04:00
140 changed files with 707 additions and 5524 deletions

No files matched your search

+2 -2
View File
@@ -1,8 +1,8 @@
<Project> <Project>
<ItemGroup> <ItemGroup>
<PackageReference Include="Reactor" Version="2.3.1" /> <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="BepInEx.Unity.IL2CPP" Version="6.0.0-be.697" Private="false" ExcludeAssets="runtime;native" />
<PackageReference Include="AmongUs.GameLibs.Steam" Version="2025.4.15" PrivateAssets="all" /> <PackageReference Include="AmongUs.GameLibs.Steam" Version="2025.3.25" PrivateAssets="all" />
<PackageReference Include="BepInEx.AutoPlugin" Version="1.1.0" PrivateAssets="all" /> <PackageReference Include="BepInEx.AutoPlugin" Version="1.1.0" PrivateAssets="all" />
<PackageReference Include="BepInEx.IL2CPP.MSBuild" Version="2.1.0-rc.1" PrivateAssets="all" ExcludeAssets="runtime" /> <PackageReference Include="BepInEx.IL2CPP.MSBuild" Version="2.1.0-rc.1" PrivateAssets="all" ExcludeAssets="runtime" />
+1 -1
View File
@@ -8,7 +8,7 @@
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile> <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<VersionPrefix>0.2.1</VersionPrefix> <VersionPrefix>0.2.0</VersionPrefix>
<VersionSuffix>dev</VersionSuffix> <VersionSuffix>dev</VersionSuffix>
<Authors>All Of Us, XtraCube, Angxl</Authors> <Authors>All Of Us, XtraCube, Angxl</Authors>
@@ -1,19 +1,19 @@
using MiraAPI.Example.GameOver; using MiraAPI.Example.Extension.GameOver;
using MiraAPI.Example.Roles; using MiraAPI.Example.Extension.Roles;
using MiraAPI.GameEnd; using MiraAPI.GameEnd;
using MiraAPI.Hud; using MiraAPI.Hud;
using MiraAPI.Keybinds;
using MiraAPI.Utilities.Assets; using MiraAPI.Utilities.Assets;
using UnityEngine; using UnityEngine;
namespace MiraAPI.Example.Buttons; namespace MiraAPI.Example.Extension.Buttons;
public class NeutralKillerButton : CustomActionButton 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 MiraKeybind Keybind => ExampleKeybinds.NeutralWinKeybind;
protected override void OnClick() protected override void OnClick()
{ {
@@ -0,0 +1,31 @@
using System;
using BepInEx;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using MiraAPI.PluginLoading;
using Reactor;
using Reactor.Networking;
using Reactor.Networking.Attributes;
namespace MiraAPI.Example.Extension;
[BepInAutoPlugin("mira.example.extension", "MiraExampleExtensionMod")]
[BepInProcess("Among Us.exe")]
[BepInDependency(ReactorPlugin.Id)]
[BepInDependency(MiraApiPlugin.Id)]
[BepInDependency(ExamplePlugin.Id)]
[ReactorModFlags(ModFlags.RequireOnAllClients)]
public partial class ExampleExtensionPlugin : BasePlugin, IMiraExtension
{
public Harmony Harmony { get; } = new(Id);
public override void Load()
{
Harmony.PatchAll();
}
public Type GetBasePluginType()
{
return typeof(ExamplePlugin);
}
}
@@ -1,9 +1,9 @@
using MiraAPI.Example.Roles; using MiraAPI.Example.Extension.Roles;
using MiraAPI.GameEnd; using MiraAPI.GameEnd;
using MiraAPI.Utilities; using MiraAPI.Utilities;
using UnityEngine; using UnityEngine;
namespace MiraAPI.Example.GameOver; namespace MiraAPI.Example.Extension.GameOver;
public class NeutralKillerGameOver : CustomGameOver public class NeutralKillerGameOver : CustomGameOver
{ {
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version>1.0.0</Version>
<Description>Example extension for Mira API</Description>
<Authors>Angxl, XtraCube</Authors>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MiraAPI\MiraAPI.csproj" />
<ProjectReference Include="..\MiraAPI.Example\MiraAPI.Example.csproj" />
</ItemGroup>
<Import Project="..\AmongUs.props" />
</Project>
@@ -0,0 +1,16 @@
using MiraAPI.Example.Extension.Options;
using MiraAPI.GameOptions;
using MiraAPI.Modifiers.Types;
namespace MiraAPI.Example.Extension.Modifiers;
public class ExtensionModifier : GameModifier
{
public override string ModifierName => "Extension Modifier";
public override string GetDescription() => "This is from the extension mod!";
public override int GetAssignmentChance() => (int)OptionGroupSingleton<ExtensionModifierOptions>.Instance.ModifierChance;
public override int GetAmountPerGame() => (int)OptionGroupSingleton<ExtensionModifierOptions>.Instance.ModifierLimit;
}
@@ -0,0 +1,13 @@
using MiraAPI.Example.Roles;
using MiraAPI.GameOptions;
using MiraAPI.GameOptions.Attributes;
namespace MiraAPI.Example.Extension.Options;
public class ExtendedFreezerOptions : AbstractOptionGroup<FreezerRole>
{
public override string GroupName => "Extended Freezer Role Options";
[ModdedNumberOption("Freeze Uses", 1, 5)]
public float FreezeUses { get; set; } = 1;
}
@@ -0,0 +1,17 @@
using MiraAPI.Example.Extension.Modifiers;
using MiraAPI.GameOptions;
using MiraAPI.GameOptions.Attributes;
using MiraAPI.Utilities;
namespace MiraAPI.Example.Extension.Options;
public class ExtensionModifierOptions : AbstractOptionGroup<ExtensionModifier>
{
public override string GroupName => "Extension Modifier Options";
[ModdedNumberOption("Modifier Chance", 0, 100, 10, MiraNumberSuffixes.Percent)]
public float ModifierChance { get; set; } = 100f;
[ModdedNumberOption("Modifier Limit", 0, 10)]
public float ModifierLimit { get; set; } = 0;
}
@@ -0,0 +1,15 @@
using HarmonyLib;
using MiraAPI.Example.Buttons.Freezer;
using MiraAPI.Example.Extension.Options;
using MiraAPI.GameOptions;
namespace MiraAPI.Example.Extension.Patches;
[HarmonyPatch(typeof(FreezeButton), nameof(FreezeButton.MaxUses), MethodType.Getter)]
public static class FreezeButtonPatch
{
public static void Postfix(ref int __result)
{
__result = (int)OptionGroupSingleton<ExtendedFreezerOptions>.Instance.FreezeUses;
}
}
@@ -1,16 +1,20 @@
using MiraAPI.Example.GameOver; using MiraAPI.Example.Extension.GameOver;
using MiraAPI.GameEnd; using MiraAPI.GameEnd;
using MiraAPI.Roles; using MiraAPI.Roles;
using UnityEngine; using UnityEngine;
namespace MiraAPI.Example.Roles; namespace MiraAPI.Example.Extension.Roles;
public class NeutralKillerRole : ImpostorRole, ICustomRole public class NeutralKillerRole : ImpostorRole, ICustomRole
{ {
public string RoleName => "Outcast Killer"; public string RoleName => "Outcast Killer";
public string RoleDescription => "Outcast who can kill."; public string RoleDescription => "Outcast who can kill.";
public string RoleLongDescription => RoleDescription;
public string RoleLongDescription => this.RoleDescription;
public Color RoleColor => Color.magenta; public Color RoleColor => Color.magenta;
public ModdedRoleTeams Team => ModdedRoleTeams.Custom; public ModdedRoleTeams Team => ModdedRoleTeams.Custom;
public CustomRoleConfiguration Configuration => new(this) public CustomRoleConfiguration Configuration => new(this)
@@ -3,7 +3,6 @@ using MiraAPI.Example.Options.Roles;
using MiraAPI.Example.Roles; using MiraAPI.Example.Roles;
using MiraAPI.GameOptions; using MiraAPI.GameOptions;
using MiraAPI.Hud; using MiraAPI.Hud;
using MiraAPI.Keybinds;
using MiraAPI.Modifiers; using MiraAPI.Modifiers;
using MiraAPI.Utilities; using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets; using MiraAPI.Utilities.Assets;
@@ -16,10 +15,10 @@ public class FreezeButton : CustomActionButton<PlayerControl>
public override string Name => "Freeze"; public override string Name => "Freeze";
public override float Cooldown => OptionGroupSingleton<FreezerRoleSettings>.Instance.FreezeDuration; public override float Cooldown => OptionGroupSingleton<FreezerRoleSettings>.Instance.FreezeDuration;
public override bool PauseTimerInVent => true;
public override int MaxUses => 1;
public override LoadableAsset<Sprite> Sprite => ExampleAssets.ExampleButton; public override LoadableAsset<Sprite> Sprite => ExampleAssets.ExampleButton;
public override MiraKeybind? Keybind => MiraGlobalKeybinds.PrimaryAbility;
protected override void OnClick() protected override void OnClick()
{ {
-2
View File
@@ -1,6 +1,5 @@
using MiraAPI.Example.Modifiers; using MiraAPI.Example.Modifiers;
using MiraAPI.Hud; using MiraAPI.Hud;
using MiraAPI.Keybinds;
using MiraAPI.Modifiers; using MiraAPI.Modifiers;
using MiraAPI.Utilities.Assets; using MiraAPI.Utilities.Assets;
using UnityEngine; using UnityEngine;
@@ -16,7 +15,6 @@ 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 MiraKeybind? Keybind => MiraGlobalKeybinds.ModifierPrimaryAbility;
public override bool Enabled(RoleBehaviour? role) public override bool Enabled(RoleBehaviour? role)
{ {
@@ -3,7 +3,6 @@ using MiraAPI.Example.Options.Roles;
using MiraAPI.Example.Roles; using MiraAPI.Example.Roles;
using MiraAPI.GameOptions; using MiraAPI.GameOptions;
using MiraAPI.Hud; using MiraAPI.Hud;
using MiraAPI.Keybinds;
using MiraAPI.Utilities.Assets; using MiraAPI.Utilities.Assets;
using Reactor.Utilities; using Reactor.Utilities;
using UnityEngine; using UnityEngine;
@@ -22,7 +21,6 @@ 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 MiraKeybind? Keybind => MiraGlobalKeybinds.PrimaryAbility;
public override bool Enabled(RoleBehaviour? role) public override bool Enabled(RoleBehaviour? role)
{ {
-35
View File
@@ -1,35 +0,0 @@
using MiraAPI.Hud;
using MiraAPI.Utilities.Assets;
using UnityEngine;
namespace MiraAPI.Example.Buttons;
public class ThinkButton : CustomActionButton
{
public override string Name => "Think";
public override float Cooldown => 15f;
public override float EffectDuration => 10f;
public override int MaxUses => 1;
public override ButtonUsesMode UsesMode => ButtonUsesMode.PerRound;
public override LoadableAsset<Sprite> Sprite => ExampleAssets.ExampleButton;
protected override void OnClick()
{
// This button does absolutely nothing besides changing its text
Button!.OverrideText("Thinking...");
}
public override void OnEffectEnd()
{
Button!.OverrideText("Think");
}
public override bool IsEffectCancellable()
{
return true;
}
public override bool Enabled(RoleBehaviour? role)
{
return role != null && role.IsImpostor;
}
}
+1 -4
View File
@@ -6,10 +6,7 @@ namespace MiraAPI.Example;
[RegisterCustomColors] [RegisterCustomColors]
public static class ExampleColors public static class ExampleColors
{ {
public static CustomColor Cerulean { get; } = new("Cerulean", new Color(0.0f, 0.48f, 0.65f)) public static CustomColor Cerulean { get; } = new("Cerulean", new Color(0.0f, 0.48f, 0.65f));
{
ColorBrightness = CustomColorBrightness.Lighter,
};
public static CustomColor Rose { get; } = new("Rose", new Color(0.98f, 0.26f, 0.62f)); public static CustomColor Rose { get; } = new("Rose", new Color(0.98f, 0.26f, 0.62f));
+1 -1
View File
@@ -51,7 +51,7 @@ public static class ExampleEventHandlers
[RegisterEvent(15)] [RegisterEvent(15)]
public static void HandleVoteEvent(HandleVoteEvent @event) public static void HandleVoteEvent(HandleVoteEvent @event)
{ {
if (@event.VoteData.Owner.Data.Role is not NeutralKillerRole) return; if (@event.VoteData.Owner.Data.Role is not FreezerRole) return;
@event.VoteData.SetRemainingVotes(0); @event.VoteData.SetRemainingVotes(0);
-10
View File
@@ -1,10 +0,0 @@
using MiraAPI.Keybinds;
using Rewired;
namespace MiraAPI.Example;
[RegisterCustomKeybinds]
public static class ExampleKeybinds
{
public static MiraKeybind NeutralWinKeybind { get; } = new("Neutral Win", KeyboardKeyCode.C, [ModifierKey.Shift]);
}
-53
View File
@@ -1,53 +0,0 @@
using BepInEx.Configuration;
using MiraAPI.LocalSettings;
using MiraAPI.LocalSettings.Attributes;
using MiraAPI.Utilities;
using UnityEngine;
namespace MiraAPI.Example;
public class ExampleLocalSettings(ConfigFile config) : LocalSettingsTab(config)
{
public override string TabName => "Mira Example";
protected override bool ShouldCreateLabels => false;
public override LocalSettingTabAppearance TabAppearance => new()
{
ToggleActiveColor = Color.blue,
ToggleInactiveColor = Color.red,
};
[LocalSettingsButton]
public LocalSettingsButton ExampleButton { get; private set; } = new("Example Button", OnExampleButtonClick);
[LocalToggleSetting]
public ConfigEntry<bool> ExampleToggle { get; private set; } = config.Bind("General", "Example Bool", true);
[LocalToggleSetting]
public ConfigEntry<bool> ExampleToggle2 { get; private set; } = config.Bind("General", "Example Bool 2", false);
[LocalSliderSetting(suffixType: MiraNumberSuffixes.Percent, formatString: "0", roundValue: true)]
public ConfigEntry<float> ExampleSlider { get; private set; } = config.Bind("General", "Slider", 50f);
[LocalNumberSetting]
public ConfigEntry<float> ExampleNumber { get; private set; } = config.Bind("General", "Example Number", 4f);
[LocalNumberSetting(min: 0.1f, max: 2.5f, increment: 0.1f, formatString: "0.0", suffixType: MiraNumberSuffixes.Multiplier)]
public ConfigEntry<float> ExampleFloatNumber { get; private set; } = config.Bind("General", "Example Multplier", 1f);
[LocalEnumSetting]
public ConfigEntry<ExampleEnumSetting> ExampleEnum { get; private set; } = config.Bind("General", "Example Enum", ExampleEnumSetting.Fries);
private static void OnExampleButtonClick()
{
LocalSettingsTabSingleton<MiraApiSettings>.Instance.Open();
}
}
public enum ExampleEnumSetting
{
Cheeseburger,
Sandwich,
Fries,
ChickenNuggets,
}
+1 -2
View File
@@ -1,5 +1,4 @@
using System.Linq; using BepInEx;
using BepInEx;
using BepInEx.Configuration; using BepInEx.Configuration;
using BepInEx.Unity.IL2CPP; using BepInEx.Unity.IL2CPP;
using HarmonyLib; using HarmonyLib;
@@ -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 uint GroupPriority => 0; // This group will be displayed first. The default value is uint.MaxValue.
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); public ModdedToggleOption ToggleOpt1 { get; } = new("Toggle Option 1", false);
public ModdedToggleOption ToggleOpt2 { get; } = new("Toggle Option 2", false) public ModdedToggleOption ToggleOpt2 { get; } = new("Toggle Option 2", false)
@@ -11,7 +11,4 @@ public class FreezerRoleSettings : AbstractOptionGroup<FreezerRole>
[ModdedNumberOption("Freeze Duration", 1, 15, 1, MiraNumberSuffixes.Seconds)] [ModdedNumberOption("Freeze Duration", 1, 15, 1, MiraNumberSuffixes.Seconds)]
public float FreezeDuration { get; set; } = 5; public float FreezeDuration { get; set; } = 5;
[ModdedNumberOption("Freeze Uses", 1, 5)]
public float FreezeUses { get; set; } = 1;
} }
+6
View File
@@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MiraAPI", "MiraAPI\MiraAPI.
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiraAPI.Example", "MiraAPI.Example\MiraAPI.Example.csproj", "{19B76855-16CC-43CC-A0A6-5A7FFB43F077}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiraAPI.Example", "MiraAPI.Example\MiraAPI.Example.csproj", "{19B76855-16CC-43CC-A0A6-5A7FFB43F077}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiraAPI.Example.Extension", "MiraAPI.Example.Extension\MiraAPI.Example.Extension.csproj", "{526FF017-80E1-4094-804D-741AF35AA226}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -21,6 +23,10 @@ Global
{19B76855-16CC-43CC-A0A6-5A7FFB43F077}.Debug|Any CPU.Build.0 = Debug|Any CPU {19B76855-16CC-43CC-A0A6-5A7FFB43F077}.Debug|Any CPU.Build.0 = Debug|Any CPU
{19B76855-16CC-43CC-A0A6-5A7FFB43F077}.Release|Any CPU.ActiveCfg = Release|Any CPU {19B76855-16CC-43CC-A0A6-5A7FFB43F077}.Release|Any CPU.ActiveCfg = Release|Any CPU
{19B76855-16CC-43CC-A0A6-5A7FFB43F077}.Release|Any CPU.Build.0 = Release|Any CPU {19B76855-16CC-43CC-A0A6-5A7FFB43F077}.Release|Any CPU.Build.0 = Release|Any CPU
{526FF017-80E1-4094-804D-741AF35AA226}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{526FF017-80E1-4094-804D-741AF35AA226}.Debug|Any CPU.Build.0 = Debug|Any CPU
{526FF017-80E1-4094-804D-741AF35AA226}.Release|Any CPU.ActiveCfg = Release|Any CPU
{526FF017-80E1-4094-804D-741AF35AA226}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
+6 -27
View File
@@ -13,24 +13,19 @@ namespace MiraAPI.Colors;
public sealed class CustomColor(StringNames name, Color32 mainColor, Color32 shadowColor) public sealed class CustomColor(StringNames name, Color32 mainColor, Color32 shadowColor)
{ {
/// <summary> /// <summary>
/// Gets or sets the main color. /// Gets the main color.
/// </summary> /// </summary>
public Color32 MainColor { get; set; } = mainColor; public Color32 MainColor { get; } = mainColor;
/// <summary> /// <summary>
/// Gets or sets the shadow color. /// Gets the shadow color.
/// </summary> /// </summary>
public Color32 ShadowColor { get; set; } = shadowColor; public Color32 ShadowColor { get; } = shadowColor;
/// <summary> /// <summary>
/// Gets or sets the name of the color. /// Gets the name of the color.
/// </summary> /// </summary>
public StringNames Name { get; set; } = name; public StringNames Name { get; } = name;
/// <summary>
/// Gets or sets whether the color is lighter or darker. Can be used to help with colorblind stuff.
/// </summary>
public CustomColorBrightness ColorBrightness { get; set; } = CustomColorBrightness.Darker;
/// <inheritdoc /> /// <inheritdoc />
public CustomColor(StringNames name, Color32 mainColor) : this(name, mainColor, mainColor.GetShadowColor(60)) public CustomColor(StringNames name, Color32 mainColor) : this(name, mainColor, mainColor.GetShadowColor(60))
@@ -47,19 +42,3 @@ public sealed class CustomColor(StringNames name, Color32 mainColor, Color32 sha
{ {
} }
} }
/// <summary>
/// Can be used to help with colorblind stuff.
/// </summary>
public enum CustomColorBrightness
{
/// <summary>
/// A lighter color.
/// </summary>
Lighter,
/// <summary>
/// A darker color.
/// </summary>
Darker,
}
@@ -1,21 +0,0 @@
namespace MiraAPI.Events.Vanilla.Gameplay;
/// <summary>
/// Invoked before RpcEndGame is called, allowing cancellation of the game end entirely.
/// </summary>
public class BeforeGameEndEvent : MiraCancelableEvent
{
/// <summary>
/// Gets the reason for the game end.
/// </summary>
public GameOverReason Reason { get; }
/// <summary>
/// Initializes a new instance of the <see cref="BeforeGameEndEvent"/> class.
/// </summary>
/// <param name="reason">The reason for the game end.</param>
public BeforeGameEndEvent(GameOverReason reason)
{
Reason = reason;
}
}
@@ -1,21 +0,0 @@
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,21 +0,0 @@
namespace MiraAPI.Events.Vanilla.Gameplay;
/// <summary>
/// The event that is invoked when the player's role is shown on the intro cutscene. Non cancelable.
/// </summary>
public class IntroRoleRevealEvent : MiraEvent
{
/// <summary>
/// Gets the instance of the IntroCutscene.
/// </summary>
public IntroCutscene IntroCutscene { get; }
/// <summary>
/// Initializes a new instance of the <see cref="IntroRoleRevealEvent"/> class.
/// </summary>
/// <param name="introCutscene">The intro cutscene.</param>
public IntroRoleRevealEvent(IntroCutscene introCutscene)
{
IntroCutscene = introCutscene;
}
}
@@ -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 if the event isn't cancelled. /// Start Round event, invoked on IntroCutscene.OnDestroy and ExileController.WrapUp.
/// </summary> /// </summary>
public class RoundStartEvent : MiraEvent public class RoundStartEvent : MiraEvent
{ {
@@ -1,30 +0,0 @@
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.
/// </summary>
public class AfterVoteEvent : MiraEvent
{
/// <summary>
/// Gets the instance of the voter's vote data.
/// </summary>
public PlayerVoteArea VoteArea { get; }
/// <summary>
/// Gets the player who voted.
/// </summary>
public PlayerControl Player { get; }
/// <summary>
/// Initializes a new instance of the <see cref="AfterVoteEvent"/> class.
/// </summary>
/// <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)
{
VoteArea = playerVoteArea;
Player = voter;
}
}
@@ -1,30 +0,0 @@
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.
/// </summary>
public class BeforeVoteEvent : MiraCancelableEvent
{
/// <summary>
/// Gets the instance of the voter's vote data.
/// </summary>
public PlayerVoteArea VoteArea { get; }
/// <summary>
/// Gets the player who voted.
/// </summary>
public PlayerControl Player { get; }
/// <summary>
/// Initializes a new instance of the <see cref="BeforeVoteEvent"/> class.
/// </summary>
/// <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)
{
VoteArea = playerVoteArea;
Player = voter;
}
}
@@ -1,44 +0,0 @@
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;
}
}
-2
View File
@@ -1,2 +0,0 @@
[*.cs]
dotnet_diagnostic.CS1591.severity = none
+1 -26
View File
@@ -46,11 +46,6 @@ public interface IModdedOption
/// </summary> /// </summary>
Func<bool> Visible { get; set; } Func<bool> Visible { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the option should be included with presets.
/// </summary>
bool IncludeInPreset { get; set; }
/// <summary> /// <summary>
/// Gets or sets the ConfigDefinition for the option, used for BepInEx configuration. /// Gets or sets the ConfigDefinition for the option, used for BepInEx configuration.
/// </summary> /// </summary>
@@ -62,10 +57,9 @@ public interface IModdedOption
/// <param name="toggleOpt">The ToggleOption template.</param> /// <param name="toggleOpt">The ToggleOption template.</param>
/// <param name="numberOpt">The NumberOption template.</param> /// <param name="numberOpt">The NumberOption template.</param>
/// <param name="stringOpt">The StringOption 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> /// <param name="container">>The Transform container for the option.</param>
/// <returns>>The created OptionBehaviour object.</returns> /// <returns>>The created OptionBehaviour object.</returns>
OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, PlayerOption playerOpt, Transform container); OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container);
/// <summary> /// <summary>
/// Gets the value as a float. /// Gets the value as a float.
@@ -84,23 +78,4 @@ public interface IModdedOption
/// </summary> /// </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); void HandleNetData(byte[] data);
/// <summary>
/// Saves the option to a preset configuration file.
/// </summary>
/// <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 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 ConfigFile representing the preset configuration.</param>
void LoadFromPreset(ConfigFile presetConfig);
} }
+6 -13
View File
@@ -1,12 +1,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using BepInEx.Configuration; using BepInEx.Configuration;
using HarmonyLib; using HarmonyLib;
using MiraAPI.GameOptions.Attributes; using MiraAPI.GameOptions.Attributes;
using MiraAPI.GameOptions.OptionTypes;
using MiraAPI.Networking; using MiraAPI.Networking;
using MiraAPI.PluginLoading; using MiraAPI.PluginLoading;
using MiraAPI.Utilities; using MiraAPI.Utilities;
@@ -23,7 +21,6 @@ public static class ModdedOptionsManager
private static readonly Dictionary<PropertyInfo, ModdedOptionAttribute> OptionAttributes = []; private static readonly Dictionary<PropertyInfo, ModdedOptionAttribute> OptionAttributes = [];
private static readonly Dictionary<Type, AbstractOptionGroup> TypeToGroup = []; private static readonly Dictionary<Type, AbstractOptionGroup> TypeToGroup = [];
internal static readonly Dictionary<OptionBehaviour, ModdedPlayerOption> CreatedPlayerOptions = [];
internal static readonly Dictionary<uint, IModdedOption> ModdedOptions = []; internal static readonly Dictionary<uint, IModdedOption> ModdedOptions = [];
internal static readonly List<AbstractOptionGroup> Groups = []; internal static readonly List<AbstractOptionGroup> Groups = [];
@@ -45,7 +42,7 @@ public static class ModdedOptionsManager
Groups.Add(group); Groups.Add(group);
TypeToGroup.Add(type, group); TypeToGroup.Add(type, group);
pluginInfo.InternalOptionGroups.Add(group); pluginInfo.OptionGroups.Add(group);
typeof(OptionGroupSingleton<>).MakeGenericType(type) typeof(OptionGroupSingleton<>).MakeGenericType(type)
#pragma warning disable S3011 #pragma warning disable S3011
@@ -124,7 +121,7 @@ public static class ModdedOptionsManager
option.ConfigDefinition = new ConfigDefinition(groupName, propertyName); option.ConfigDefinition = new ConfigDefinition(groupName, propertyName);
option.ParentMod = pluginInfo.MiraPlugin; option.ParentMod = pluginInfo.MiraPlugin;
pluginInfo.InternalOptions.Add(option); pluginInfo.Options.Add(option);
ModdedOptions.Add(option.Id, option); ModdedOptions.Add(option.Id, option);
group.Options.Add(option); group.Options.Add(option);
} }
@@ -145,7 +142,7 @@ public static class ModdedOptionsManager
// we dont know how other plugins handle their configs // we dont know how other plugins handle their configs
// this way, all the options are saved at once, instead of one by one // this way, all the options are saved at once, instead of one by one
var oldConfigSetting = new Dictionary<MiraPluginInfo, bool>(); var oldConfigSetting = new Dictionary<MiraPluginInfo, bool>();
foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins) foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins())
{ {
oldConfigSetting.Add(plugin, plugin.PluginConfig.SaveOnConfigSet); oldConfigSetting.Add(plugin, plugin.PluginConfig.SaveOnConfigSet);
plugin.PluginConfig.SaveOnConfigSet = false; plugin.PluginConfig.SaveOnConfigSet = false;
@@ -161,7 +158,7 @@ public static class ModdedOptionsManager
option.HandleNetData(netData.Data); option.HandleNetData(netData.Data);
} }
foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins) foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins())
{ {
plugin.PluginConfig.Save(); plugin.PluginConfig.Save();
plugin.PluginConfig.SaveOnConfigSet = oldConfigSetting[plugin]; plugin.PluginConfig.SaveOnConfigSet = oldConfigSetting[plugin];
@@ -178,10 +175,8 @@ public static class ModdedOptionsManager
/// </summary> /// </summary>
/// <param name="__originalMethod">The original setter method.</param> /// <param name="__originalMethod">The original setter method.</param>
/// <param name="value">The new object value.</param> /// <param name="value">The new object value.</param>
#pragma warning disable CA1707 // ReSharper disable InconsistentNaming
[SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Harmony naming convention")]
public static void PropertySetterPatch(MethodBase __originalMethod, object value) public static void PropertySetterPatch(MethodBase __originalMethod, object value)
#pragma warning restore CA1707
{ {
var attribute = OptionAttributes.First(pair => pair.Key.GetSetMethod() == __originalMethod).Value; var attribute = OptionAttributes.First(pair => pair.Key.GetSetMethod() == __originalMethod).Value;
attribute.SetValue(value); attribute.SetValue(value);
@@ -193,10 +188,8 @@ public static class ModdedOptionsManager
/// <param name="__originalMethod">The original getter method.</param> /// <param name="__originalMethod">The original getter method.</param>
/// <param name="__result">The result of the property getter.</param> /// <param name="__result">The result of the property getter.</param>
/// <returns>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) public static bool PropertyGetterPatch(MethodBase __originalMethod, ref object __result)
#pragma warning restore CA1707 // ReSharper restore InconsistentNaming
{ {
var attribute = OptionAttributes.First(pair => pair.Key.GetGetMethod() == __originalMethod).Value; var attribute = OptionAttributes.First(pair => pair.Key.GetGetMethod() == __originalMethod).Value;
__result = attribute.GetValue(); __result = attribute.GetValue();
@@ -18,7 +18,7 @@ public class ModdedEnumOption : ModdedOption<int>
/// <summary> /// <summary>
/// Gets the string values of the enum. /// Gets the string values of the enum.
/// </summary> /// </summary>
public string[] Values { get; } public string[]? Values { get; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ModdedEnumOption"/> class. /// Initializes a new instance of the <see cref="ModdedEnumOption"/> class.
@@ -27,8 +27,7 @@ public class ModdedEnumOption : ModdedOption<int>
/// <param name="defaultValue">The default value as an int.</param> /// <param name="defaultValue">The default value as an int.</param>
/// <param name="enumType">The Enum type.</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="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) : base(title, defaultValue)
public ModdedEnumOption(string title, int defaultValue, Type enumType, string[]? values = null, bool includeInPreset=true) : base(title, defaultValue, includeInPreset)
{ {
Values = values ?? Enum.GetNames(enumType); Values = values ?? Enum.GetNames(enumType);
Data = ScriptableObject.CreateInstance<StringGameSetting>(); Data = ScriptableObject.CreateInstance<StringGameSetting>();
@@ -38,13 +37,13 @@ public class ModdedEnumOption : ModdedOption<int>
data.Type = global::OptionTypes.String; data.Type = global::OptionTypes.String;
data.Values = values is null ? data.Values = values is null ?
Enum.GetNames(enumType).Select(CustomStringName.CreateAndRegister).ToArray() Enum.GetNames(enumType).Select(CustomStringName.CreateAndRegister).ToArray()
: [.. values.Select(CustomStringName.CreateAndRegister)]; : values.Select(CustomStringName.CreateAndRegister).ToArray();
data.Index = Value; data.Index = Value;
} }
/// <inheritdoc /> /// <inheritdoc />
public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, PlayerOption playerOpt, Transform container) public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container)
{ {
var stringOption = Object.Instantiate(stringOpt, container); var stringOption = Object.Instantiate(stringOpt, container);
@@ -88,7 +87,7 @@ public class ModdedEnumOption : ModdedOption<int>
/// <inheritdoc /> /// <inheritdoc />
protected override void OnValueChanged(int newValue) protected override void OnValueChanged(int newValue)
{ {
HudManager.Instance.Notifier.AddSettingsChangeMessage(StringName, Data.GetValueString(newValue), false); HudManager.Instance.Notifier.AddSettingsChangeMessage(StringName, Data?.GetValueString(newValue), false);
if (!OptionBehaviour) if (!OptionBehaviour)
{ {
return; return;
@@ -110,7 +109,7 @@ public class ModdedEnumOption<T> : ModdedOption<T> where T : Enum
/// <summary> /// <summary>
/// Gets the string values of the enum. /// Gets the string values of the enum.
/// </summary> /// </summary>
public string[] Values { get; } public string[]? Values { get; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ModdedEnumOption{T}"/> class. /// Initializes a new instance of the <see cref="ModdedEnumOption{T}"/> class.
@@ -118,8 +117,7 @@ public class ModdedEnumOption<T> : ModdedOption<T> where T : Enum
/// <param name="title">The title of the option.</param> /// <param name="title">The title of the option.</param>
/// <param name="defaultValue">The default value as an int.</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="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) : base(title, defaultValue)
public ModdedEnumOption(string title, T defaultValue, string[]? values = null, bool includeInPreset=true) : base(title, defaultValue, includeInPreset)
{ {
Values = values ?? Enum.GetNames(typeof(T)); Values = values ?? Enum.GetNames(typeof(T));
Data = ScriptableObject.CreateInstance<StringGameSetting>(); Data = ScriptableObject.CreateInstance<StringGameSetting>();
@@ -129,13 +127,13 @@ public class ModdedEnumOption<T> : ModdedOption<T> where T : Enum
data.Type = global::OptionTypes.String; data.Type = global::OptionTypes.String;
data.Values = values is null ? data.Values = values is null ?
Enum.GetNames(typeof(T)).Select(CustomStringName.CreateAndRegister).ToArray() Enum.GetNames(typeof(T)).Select(CustomStringName.CreateAndRegister).ToArray()
: [.. values.Select(CustomStringName.CreateAndRegister)]; : values.Select(CustomStringName.CreateAndRegister).ToArray();
data.Index = Convert.ToInt32(Value, NumberFormatInfo.InvariantInfo); data.Index = Convert.ToInt32(Value, NumberFormatInfo.InvariantInfo);
} }
/// <inheritdoc /> /// <inheritdoc />
public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, PlayerOption playerOpt, Transform container) public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container)
{ {
var stringOption = Object.Instantiate(stringOpt, container); var stringOption = Object.Instantiate(stringOpt, container);
@@ -48,7 +48,6 @@ public class ModdedNumberOption : ModdedOption<float>
/// <param name="suffixType">The suffix type.</param> /// <param name="suffixType">The suffix type.</param>
/// <param name="formatString">Optional format string for the option screen.</param> /// <param name="formatString">Optional format string for the option screen.</param>
/// <param name="zeroInfinity">Whether zero is infinity or not.</param> /// <param name="zeroInfinity">Whether zero is infinity or not.</param>
/// <param name="includeInPreset">Whether to include this option in the preset or not.</param>
public ModdedNumberOption( public ModdedNumberOption(
string title, string title,
float defaultValue, float defaultValue,
@@ -57,8 +56,7 @@ public class ModdedNumberOption : ModdedOption<float>
float increment, float increment,
MiraNumberSuffixes suffixType, MiraNumberSuffixes suffixType,
string? formatString = null, string? formatString = null,
bool zeroInfinity = false, bool zeroInfinity = false) : base(title, defaultValue)
bool includeInPreset = true) : base(title, defaultValue, includeInPreset)
{ {
Min = min; Min = min;
Max = max; Max = max;
@@ -93,7 +91,6 @@ public class ModdedNumberOption : ModdedOption<float>
ToggleOption toggleOpt, ToggleOption toggleOpt,
NumberOption numberOpt, NumberOption numberOpt,
StringOption stringOpt, StringOption stringOpt,
PlayerOption playerOpt,
Transform container) Transform container)
{ {
var numberOption = Object.Instantiate(numberOpt, Vector3.zero, Quaternion.identity, container); var numberOption = Object.Instantiate(numberOpt, Vector3.zero, Quaternion.identity, container);
+47 -55
View File
@@ -4,7 +4,6 @@ using MiraAPI.Networking;
using MiraAPI.PluginLoading; using MiraAPI.PluginLoading;
using Reactor.Localization.Utilities; using Reactor.Localization.Utilities;
using Reactor.Networking.Rpc; using Reactor.Networking.Rpc;
using Reactor.Utilities;
using UnityEngine; using UnityEngine;
namespace MiraAPI.GameOptions.OptionTypes; namespace MiraAPI.GameOptions.OptionTypes;
@@ -17,19 +16,29 @@ public abstract class ModdedOption<T> : IModdedOption
{ {
private IMiraPlugin? _parentMod; private IMiraPlugin? _parentMod;
/// <inheritdoc /> /// <summary>
/// Gets the unique identifier of the option.
/// </summary>
public uint Id { get; } public uint Id { get; }
/// <inheritdoc /> /// <summary>
/// Gets the title of the option.
/// </summary>
public string Title { get; } public string Title { get; }
/// <inheritdoc /> /// <summary>
/// Gets the StringName object of the option.
/// </summary>
public StringNames StringName { get; } public StringNames StringName { get; }
/// <inheritdoc /> /// <summary>
/// Gets or sets the BaseGameSetting data of the option.
/// </summary>
public BaseGameSetting Data { get; protected set; } = null!; public BaseGameSetting Data { get; protected set; } = null!;
/// <inheritdoc /> /// <summary>
/// Gets or sets the parent mod of the option.
/// </summary>
public IMiraPlugin? ParentMod public IMiraPlugin? ParentMod
{ {
get => _parentMod; get => _parentMod;
@@ -37,7 +46,6 @@ public abstract class ModdedOption<T> : IModdedOption
{ {
if (_parentMod != null || value == null) return; if (_parentMod != null || value == null) return;
_parentMod = value; _parentMod = value;
var entry = _parentMod.GetConfigFile().Bind(ConfigDefinition, DefaultValue); var entry = _parentMod.GetConfigFile().Bind(ConfigDefinition, DefaultValue);
Value = entry.Value; Value = entry.Value;
} }
@@ -58,16 +66,19 @@ public abstract class ModdedOption<T> : IModdedOption
/// </summary> /// </summary>
public Action<T>? ChangedEvent { get; set; } public Action<T>? ChangedEvent { get; set; }
/// <inheritdoc /> /// <summary>
/// Gets or sets the visibility of the option.
/// </summary>
public Func<bool> Visible { get; set; } public Func<bool> Visible { get; set; }
/// <inheritdoc /> /// <summary>
public bool IncludeInPreset { get; set; } /// Gets or sets the option behaviour of the option.
/// </summary>
/// <inheritdoc />
public OptionBehaviour? OptionBehaviour { get; protected set; } public OptionBehaviour? OptionBehaviour { get; protected set; }
/// <inheritdoc /> /// <summary>
/// Gets or sets the config definition of the option.
/// </summary>
public ConfigDefinition? ConfigDefinition { get; set; } public ConfigDefinition? ConfigDefinition { get; set; }
/// <summary> /// <summary>
@@ -75,8 +86,7 @@ public abstract class ModdedOption<T> : IModdedOption
/// </summary> /// </summary>
/// <param name="title">The option title.</param> /// <param name="title">The option title.</param>
/// <param name="defaultValue">The default value.</param> /// <param name="defaultValue">The default value.</param>
/// <param name="includeInPreset">Whether to include the option in the preset.</param> protected ModdedOption(string title, T defaultValue)
protected ModdedOption(string title, T defaultValue, bool includeInPreset = true)
{ {
Id = ModdedOptionsManager.NextId; Id = ModdedOptionsManager.NextId;
Title = title; Title = title;
@@ -84,7 +94,6 @@ public abstract class ModdedOption<T> : IModdedOption
Value = defaultValue; Value = defaultValue;
StringName = CustomStringName.CreateAndRegister(Title); StringName = CustomStringName.CreateAndRegister(Title);
Visible = () => true; Visible = () => true;
IncludeInPreset = includeInPreset;
} }
internal void ValueChanged(OptionBehaviour optionBehaviour) internal void ValueChanged(OptionBehaviour optionBehaviour)
@@ -96,8 +105,7 @@ public abstract class ModdedOption<T> : IModdedOption
/// Sets the value of the option. /// Sets the value of the option.
/// </summary> /// </summary>
/// <param name="newValue">The new value.</param> /// <param name="newValue">The new value.</param>
/// <param name="sendRpc">Whether to send the value to other players.</param> public void SetValue(T newValue)
public void SetValue(T newValue, bool sendRpc = true)
{ {
var oldVal = Value; var oldVal = Value;
Value = newValue; Value = newValue;
@@ -107,7 +115,7 @@ public abstract class ModdedOption<T> : IModdedOption
ChangedEvent?.Invoke(Value); ChangedEvent?.Invoke(Value);
} }
if (sendRpc && AmongUsClient.Instance.AmHost) if (AmongUsClient.Instance.AmHost)
{ {
if (ParentMod?.GetConfigFile().TryGetEntry<T>(ConfigDefinition, out var entry) == true) if (ParentMod?.GetConfigFile().TryGetEntry<T>(ConfigDefinition, out var entry) == true)
{ {
@@ -120,44 +128,22 @@ public abstract class ModdedOption<T> : IModdedOption
OnValueChanged(newValue); OnValueChanged(newValue);
} }
/// <inheritdoc /> /// <summary>
public void SaveToPreset(ConfigFile presetConfig, bool saveDefault=false) /// Gets the float data of the option.
{ /// </summary>
if (ConfigDefinition is null) /// <returns>A float object representing the option's value.</returns>
{
Logger<MiraApiPlugin>.Error($"Attempted to save {Title} to preset, but ConfigDefinition is null.");
return;
}
Bind(presetConfig);
presetConfig[ConfigDefinition].BoxedValue = saveDefault ? DefaultValue : Value;
}
/// <inheritdoc />
public void Bind(ConfigFile config)
{
config.Bind(ConfigDefinition, DefaultValue);
}
/// <inheritdoc />
public void LoadFromPreset(ConfigFile presetConfig)
{
if (presetConfig.TryGetEntry(ConfigDefinition, out ConfigEntry<T> entry))
{
SetValue(entry.Value, false);
}
else
{
Logger<MiraApiPlugin>.Error($"Attempted to load {Title} from preset, but ConfigDefinition is not found in preset.");
}
}
/// <inheritdoc />
public abstract float GetFloatData(); public abstract float GetFloatData();
/// <inheritdoc /> /// <summary>
/// Gets the net data of the option.
/// </summary>
/// <returns>A NetData object representing this option's data.</returns>
public abstract NetData GetNetData(); public abstract NetData GetNetData();
/// <inheritdoc /> /// <summary>
/// Handles incoming net data.
/// </summary>
/// <param name="data">The NetData's byte array.</param>
public abstract void HandleNetData(byte[] data); public abstract void HandleNetData(byte[] data);
/// <summary> /// <summary>
@@ -173,12 +159,18 @@ public abstract class ModdedOption<T> : IModdedOption
/// <returns>The value.</returns> /// <returns>The value.</returns>
public abstract T GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour); public abstract T GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour);
/// <inheritdoc /> /// <summary>
/// Creates the option behaviour.
/// </summary>
/// <param name="toggleOpt">The ToggleOption prefab.</param>
/// <param name="numberOpt">The NumberOption prefab.</param>
/// <param name="stringOpt">The StringOption prefab.</param>
/// <param name="container">The options container.</param>
/// <returns>A new OptionBehaviour for this modded option.</returns>
public abstract OptionBehaviour CreateOption( public abstract OptionBehaviour CreateOption(
ToggleOption toggleOpt, ToggleOption toggleOpt,
NumberOption numberOpt, NumberOption numberOpt,
StringOption stringOpt, StringOption stringOpt,
PlayerOption playerOpt,
Transform container); Transform container);
/// <summary> /// <summary>
@@ -1,138 +0,0 @@
using System;
using System.Linq;
using Il2CppSystem.Collections.Generic;
using MiraAPI.Networking;
using MiraAPI.Utilities;
using UnityEngine;
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 Values list.
/// </summary>
public class ModdedPlayerOption : ModdedOption<int>
{
/// <summary>
/// Gets or sets the included players.
/// </summary>
public List<NetworkedPlayerInfo> Values { get; set; }
/// <summary>
/// Gets or sets a filter for the players included in the option.
/// </summary>
public Func<NetworkedPlayerInfo, bool>? PlayerFilter { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the option includes a "None" option. If None is selected, value will return -1.
/// </summary>
public bool AllowNone { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="ModdedPlayerOption"/> class.
/// </summary>
/// <param name="title">The title of the option.</param>
/// <param name="allowNone">Whether the option includes a none option. If None is selected, value will return -1.</param>
public ModdedPlayerOption(string title, bool allowNone = true) : base(title, allowNone ? -1 : 0, false)
{
Data = ScriptableObject.CreateInstance<PlayerSelectionGameSetting>();
var data = (PlayerSelectionGameSetting)Data;
data.Title = StringName;
data.Type = global::OptionTypes.Player;
Values = new List<NetworkedPlayerInfo>();
AllowNone = allowNone;
Value = DefaultValue;
}
/// <inheritdoc />
public override OptionBehaviour CreateOption(
ToggleOption toggleOpt,
NumberOption numberOpt,
StringOption stringOpt,
PlayerOption playerOpt,
Transform container)
{
var playerOption = Object.Instantiate(playerOpt, container);
var filteredList = GetFilteredPlayers();
Values = filteredList.ToIl2CppList();
playerOption.SetUpFromData(Data, 20);
playerOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged;
playerOption.Title = StringName;
playerOption.TitleText.text = TranslationController.Instance.GetString(StringName);
playerOption.Values = Values;
playerOption.Value = Value;
playerOption.playerIndex = filteredList.FindIndex(p => p.PlayerId == Value);
OptionBehaviour = playerOption;
ModdedOptionsManager.CreatedPlayerOptions.TryAdd(playerOption, this);
playerOption.SetValueText();
playerOption.AdjustButtonsActiveState();
return playerOption;
}
/// <summary>
/// Returns a player ran through the filter.
/// </summary>
/// <returns>A list of filtered players.</returns>
public System.Collections.Generic.List<NetworkedPlayerInfo> GetFilteredPlayers()
{
return GameData.Instance.AllPlayers.ToArray()
.Where(x => PlayerFilter?.Invoke(x) ?? true)
.ToList();
}
/// <summary>
/// Returns a player from the value. If AllowNone is enabled and none is enabled this will return null.
/// </summary>
/// <returns>A player or null if chosen none.</returns>
public NetworkedPlayerInfo? GetPlayerValue()
{
return Value < 0 ? null : Values[Value];
}
/// <inheritdoc />
public override float GetFloatData()
{
return Value;
}
/// <inheritdoc />
public override NetData GetNetData()
{
return new NetData(Id, BitConverter.GetBytes(Value));
}
/// <inheritdoc />
public override void HandleNetData(byte[] data)
{
SetValue(BitConverter.ToInt32(data));
}
/// <inheritdoc />
public override int GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour)
{
return optionBehaviour.GetInt();
}
/// <inheritdoc />
protected override void OnValueChanged(int newValue)
{
HudManager.Instance.Notifier.AddSettingsChangeMessage(StringName, Data.GetValueString(newValue), false);
if (!OptionBehaviour)
{
return;
}
if (OptionBehaviour is PlayerOption opt)
{
opt.Value = newValue;
}
}
}
@@ -15,8 +15,7 @@ public class ModdedToggleOption : ModdedOption<bool>
/// </summary> /// </summary>
/// <param name="title">The option title.</param> /// <param name="title">The option title.</param>
/// <param name="defaultValue">The default value.</param> /// <param name="defaultValue">The default value.</param>
/// <param name="includeInPreset">Whether to include this option in the preset or not.</param> public ModdedToggleOption(string title, bool defaultValue) : base(title, defaultValue)
public ModdedToggleOption(string title, bool defaultValue, bool includeInPreset=true) : base(title, defaultValue, includeInPreset)
{ {
Data = ScriptableObject.CreateInstance<CheckboxGameSetting>(); Data = ScriptableObject.CreateInstance<CheckboxGameSetting>();
@@ -26,7 +25,7 @@ public class ModdedToggleOption : ModdedOption<bool>
} }
/// <inheritdoc /> /// <inheritdoc />
public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, PlayerOption playerOpt, Transform container) public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container)
{ {
var toggleOption = Object.Instantiate(toggleOpt, Vector3.zero, Quaternion.identity, container); var toggleOption = Object.Instantiate(toggleOpt, Vector3.zero, Quaternion.identity, container);
-17
View File
@@ -1,17 +0,0 @@
namespace MiraAPI.Hud;
/// <summary>
/// The uses mode for custom buttons.
/// </summary>
public enum ButtonUsesMode
{
/// <summary>
/// Uses per game.
/// </summary>
PerGame,
/// <summary>
/// Uses per round.
/// </summary>
PerRound,
}
+14 -96
View File
@@ -2,14 +2,8 @@
using System.Globalization; using System.Globalization;
using MiraAPI.Events; using MiraAPI.Events;
using MiraAPI.Events.Mira; using MiraAPI.Events.Mira;
using MiraAPI.Keybinds;
using MiraAPI.LocalSettings;
using MiraAPI.Patches; using MiraAPI.Patches;
using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets; using MiraAPI.Utilities.Assets;
using Reactor.Utilities;
using Rewired;
using TMPro;
using UnityEngine; using UnityEngine;
using UnityEngine.Events; using UnityEngine.Events;
using UnityEngine.UI; using UnityEngine.UI;
@@ -37,29 +31,13 @@ public abstract class CustomActionButton
/// </summary> /// </summary>
public abstract float Cooldown { get; } public abstract float Cooldown { get; }
/// <summary>
/// Gets a value indicating whether the timer should stop decreasing when the player is venting.
/// Note that both cooldown decrease and effect timer decrease is disabled.
/// </summary>
public virtual bool PauseTimerInVent => false;
/// <summary> /// <summary>
/// Gets the sprite of the button. Use <see cref="LoadableResourceAsset"/> to load a sprite from a resource path. Use <see cref="LoadableBundleAsset{T}"/> to load a sprite from an asset bundle. /// Gets the sprite of the button. Use <see cref="LoadableResourceAsset"/> to load a sprite from a resource path. Use <see cref="LoadableBundleAsset{T}"/> to load a sprite from an asset bundle.
/// </summary> /// </summary>
public abstract LoadableAsset<Sprite> Sprite { get; } public abstract LoadableAsset<Sprite> Sprite { get; }
/// <summary> /// <summary>
/// Gets the format string for the cooldown timer. /// Gets the button's effect duration in seconds. If the button has no effect, set to 0.
/// </summary>
public virtual string CooldownTimerFormatString => "0";
/// <summary>
/// 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;
/// <summary>
/// Gets the button's effect duration in seconds. If 0, the effect needs to be ended manually.
/// </summary> /// </summary>
public virtual float EffectDuration => 0; public virtual float EffectDuration => 0;
@@ -68,16 +46,6 @@ public abstract class CustomActionButton
/// </summary> /// </summary>
public virtual int MaxUses => 0; public virtual int MaxUses => 0;
/// <summary>
/// Gets the value indicating uses mode.
/// </summary>
public virtual ButtonUsesMode UsesMode => ButtonUsesMode.PerGame;
/// <summary>
/// Gets the keybind for this button. If null, no keybind will be added.
/// </summary>
public virtual BaseKeybind? Keybind => null;
/// <summary> /// <summary>
/// Gets the button's text outline color. /// Gets the button's text outline color.
/// </summary> /// </summary>
@@ -88,6 +56,11 @@ public abstract class CustomActionButton
/// </summary> /// </summary>
public virtual ButtonLocation Location { get; set; } = ButtonLocation.BottomLeft; public virtual ButtonLocation Location { get; set; } = ButtonLocation.BottomLeft;
/// <summary>
/// Gets a value indicating whether the button has an effect ability.
/// </summary>
public bool HasEffect => EffectDuration > 0;
/// <summary> /// <summary>
/// Gets a value indicating whether the button has limited uses. /// Gets a value indicating whether the button has limited uses.
/// </summary> /// </summary>
@@ -118,16 +91,6 @@ public abstract class CustomActionButton
/// </summary> /// </summary>
public ActionButton? Button { get; set; } public ActionButton? Button { get; set; }
/// <summary>
/// Gets the gameObject used for the keybind icon.
/// </summary>
public GameObject? KeybindIcon { get; private set; }
/// <summary>
/// Gets or sets the keybind icon text.
/// </summary>
private TextMeshPro? KeybindText { get; set; }
/// <summary> /// <summary>
/// The method used to create the button. /// The method used to create the button.
/// </summary> /// </summary>
@@ -199,26 +162,6 @@ public abstract class CustomActionButton
} }
} }
})); }));
if (Keybind != null)
{
Keybind.OnActivate(() =>
{
if (Enabled(PlayerControl.LocalPlayer.Data.Role))
{
ClickHandler();
}
});
KeybindIcon =
Helpers.CreateKeybindIcon(
Button.gameObject,
Keybind.CurrentKey,
new Vector3(MaxUses <= 0 ? -0.4f : 0.4f, 0.45f, -9f)
);
KeybindText = KeybindIcon.transform.GetChild(0).GetComponent<TextMeshPro>();
Button.usesRemainingSprite.transform.localPosition = new(-0.341f, 0.45f, -0.1f);
}
} }
/// <summary> /// <summary>
@@ -235,10 +178,7 @@ public abstract class CustomActionButton
this.Location = location; this.Location = location;
if (!moveButton) if (!moveButton) return;
{
return;
}
if (HudManagerPatches.BottomLeft == null || HudManagerPatches.BottomRight == null) if (HudManagerPatches.BottomLeft == null || HudManagerPatches.BottomRight == null)
{ {
@@ -405,13 +345,6 @@ public abstract class CustomActionButton
{ {
} }
/// <summary>
/// Returns a value indicating whether the effect can be ended early by the player.
/// Always false by default.
/// </summary>
/// <returns>Can the effect be canceled.</returns>
public virtual bool IsEffectCancellable() => false;
/// <summary> /// <summary>
/// When the button is usable, this method is called to determine if the button can be clicked. /// When the button is usable, this method is called to determine if the button can be clicked.
/// By default, it takes into account the timer, effect, and uses. /// By default, it takes into account the timer, effect, and uses.
@@ -420,7 +353,7 @@ public abstract class CustomActionButton
/// <returns>A value that represents whether the button can be clicked or not.</returns> /// <returns>A value that represents whether the button can be clicked or not.</returns>
public virtual bool CanClick() public virtual bool CanClick()
{ {
return (EffectActive ? IsEffectCancellable() : Timer <= 0) && CanUse(); return Timer <= 0 && !EffectActive && CanUse();
} }
/// <summary> /// <summary>
@@ -430,8 +363,7 @@ public abstract class CustomActionButton
/// <returns>A value that represents whether the button should light up or not.</returns> /// <returns>A value that represents whether the button should light up or not.</returns>
public virtual bool CanUse() public virtual bool CanUse()
{ {
return PlayerControl.LocalPlayer.moveable && return PlayerControl.LocalPlayer.moveable && (!LimitedUses || UsesLeft > 0);
((EffectActive && IsEffectCancellable()) || (!EffectActive && (!LimitedUses || UsesLeft > 0)));
} }
/// <summary> /// <summary>
@@ -453,12 +385,6 @@ public abstract class CustomActionButton
/// </summary> /// </summary>
public virtual void ClickHandler() public virtual void ClickHandler()
{ {
if (EffectActive && IsEffectCancellable())
{
ResetCooldownAndOrEffect();
return;
}
if (!CanClick()) if (!CanClick())
{ {
return; return;
@@ -491,22 +417,14 @@ public abstract class CustomActionButton
/// <param name="playerControl">The local PlayerControl.</param> /// <param name="playerControl">The local PlayerControl.</param>
public virtual void FixedUpdateHandler(PlayerControl playerControl) public virtual void FixedUpdateHandler(PlayerControl playerControl)
{ {
if (Keybind != null && KeybindText != null)
{
KeybindText.text = Keybind.CurrentKey.ToString();
KeybindIcon?.SetActive(ActiveInputManager.currentControlType == ActiveInputManager.InputType.Keyboard &&
LocalSettingsTabSingleton<MiraApiSettings>.Instance.ShowKeybinds.Value &&
Keybind.CurrentKey != KeyboardKeyCode.None);
}
if (Timer >= 0) if (Timer >= 0)
{ {
if (!TimerPaused && (!PauseTimerInVent || !playerControl.inVent)) if (!TimerPaused)
{ {
Timer -= Time.deltaTime; Timer -= Time.deltaTime;
} }
} }
else if (HasEffect && EffectActive && EffectDuration > 0) else if (HasEffect && EffectActive)
{ {
EffectActive = false; EffectActive = false;
Timer = Cooldown; Timer = Cooldown;
@@ -524,16 +442,16 @@ public abstract class CustomActionButton
Button!.SetDisabled(); Button!.SetDisabled();
} }
if (EffectActive && EffectDuration > 0) if (EffectActive)
{ {
Button.SetFillUp(Timer, EffectDuration); Button.SetFillUp(Timer, EffectDuration);
Button.cooldownTimerText.text = Timer.ToString(CooldownTimerFormatString, NumberFormatInfo.InvariantInfo); Button.cooldownTimerText.text = Mathf.CeilToInt(Timer).ToString(NumberFormatInfo.InvariantInfo);
Button.cooldownTimerText.gameObject.SetActive(true); Button.cooldownTimerText.gameObject.SetActive(true);
} }
else else
{ {
Button.SetCooldownFormat(Timer, Cooldown, CooldownTimerFormatString); Button.SetCoolDown(Timer, Cooldown);
} }
} }
+1 -2
View File
@@ -4,7 +4,6 @@ using System.Collections.ObjectModel;
using System.Reflection; using System.Reflection;
using MiraAPI.Events.Mira; using MiraAPI.Events.Mira;
using MiraAPI.PluginLoading; using MiraAPI.PluginLoading;
using UnityEngine;
namespace MiraAPI.Hud; namespace MiraAPI.Hud;
@@ -30,7 +29,7 @@ public static class CustomButtonManager
} }
CustomButtons.Add(button); CustomButtons.Add(button);
pluginInfo.InternalButtons.Add(button); pluginInfo.Buttons.Add(button);
typeof(CustomButtonSingleton<>).MakeGenericType(buttonType) typeof(CustomButtonSingleton<>).MakeGenericType(buttonType)
#pragma warning disable S3011 #pragma warning disable S3011
.GetField("_instance", BindingFlags.Static | BindingFlags.NonPublic)! .GetField("_instance", BindingFlags.Static | BindingFlags.NonPublic)!
-78
View File
@@ -1,78 +0,0 @@
using System.Linq;
using HarmonyLib;
using Reactor.Utilities.Extensions;
using UnityEngine;
namespace MiraAPI.Hud;
/// <summary>
/// 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
{
/// <summary>
/// Gets or sets the list of targets of the button.
/// </summary>
public T[] Targets { get; protected set; } = [];
/// <inheritdoc cref="CustomActionButton{T}.Distance"/>
public virtual float Distance => PlayerControl.LocalPlayer.Data.Role.GetAbilityDistance();
/// <summary>
/// Gets the maximum amount of targets the button can have.
/// </summary>
public virtual int MaxTargets { get; } = int.MaxValue;
/// <inheritdoc cref="CustomActionButton{T}.IsTargetValid"/>
public virtual bool IsTargetValid(T target)
{
return target != null;
}
/// <summary>
/// The method used to get the target objects.
/// </summary>
/// <returns>The list of target objects.</returns>
public abstract T[] GetTargets();
/// <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"/>
public override bool CanUse()
{
var newTargets = GetTargets();
foreach (var target in Targets)
{
if (!newTargets.Contains(target))
{
SetOutline(target, false);
}
}
Targets = newTargets
.Where(IsTargetValid)
.Take(MaxTargets)
.ToArray();
Targets.Do(t => SetOutline(t, true));
return base.CanUse() && Targets.Length > 0;
}
/// <inheritdoc cref="CustomActionButton{T}.CanClick"/>
public override bool CanClick()
{
return base.CanClick() && Targets.Length > 0;
}
/// <summary>
/// Use this to reset the button's targets after used.
/// </summary>
public virtual void ResetTarget()
{
Targets.Do(t => SetOutline(t, false));
Targets = [];
}
}
-72
View File
@@ -1,72 +0,0 @@
using System;
using Rewired;
namespace MiraAPI.Keybinds;
/// <summary>
/// Base keybind class.
/// </summary>
public class BaseKeybind
{
/// <summary>
/// Gets the Rewired <see cref="InputAction"/> assinged for this keybind.
/// </summary>
public InputAction? RewiredInputAction { get; internal set; }
/// <summary>
/// Gets the unique identifier for this keybind. Used in Rewired.
/// </summary>
public string Id { get; protected set; }
/// <summary>
/// Gets the currently assigned keycode.
/// </summary>
public KeyboardKeyCode CurrentKey => KeybindUtils.GetKeycodeByKeybind(this);
/// <summary>
/// Gets or sets the handler of the keybind. Invoked when the keybind is activated.
/// </summary>
protected Action Handler { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="BaseKeybind"/> class.
/// </summary>
/// <param name="id">The unique identifier for this keybind.</param>
public BaseKeybind(string id)
{
Id = id;
Handler = () => { };
}
public BaseKeybind()
{
Handler = () => { };
}
/// <summary>
/// Triggers the keybind, invoking its handler.
/// </summary>
public void Invoke()
{
Handler.Invoke();
}
/// <summary>
/// Adds an <see cref="Action"/> that will be invoked when the keybind is used.
/// </summary>
/// <param name="action">The <see cref="Action"/> to add.</param>
public void OnActivate(Action action)
{
Handler += action;
}
/// <summary>
/// Removes an <see cref="Action"/> that would be invoked when the keybind is used.
/// Not recommended to use - add checks in the method added in <see cref="OnActivate"/> itself rather than removing it from the keybind.
/// </summary>
/// <param name="action">The <see cref="Action"/> to remove.</param>
public void RemoveOnActivate(Action action)
{
Handler -= action;
}
}
-98
View File
@@ -1,98 +0,0 @@
using System;
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 keybinds with conflict checks and rebinding support.
/// </summary>
public static class KeybindManager
{
/// <summary>
/// Gets a list of all registered keybinds.
/// </summary>
public static List<MiraKeybind> Keybinds { get; } = new();
[HideFromIl2Cpp]
internal static Dictionary<Type, VanillaKeybind> VanillaKeybinds { get; set; } = [];
internal static void RewiredInit()
{
try
{
var instance = KeybindUtils.RewiredInputManager!;
foreach (var keybind in KeybindManager.Keybinds)
{
if (instance.userData.actions.ToArray().Any(x => x.name == keybind.Id))
{
Logger<MiraApiPlugin>.Warning($"Keybind of id {keybind.Id} already exists. Skipping it");
continue;
}
keybind.RewiredInputAction = instance.userData.RegisterModBind(
keybind.Id,
keybind.Name,
keybind.SourcePluginName,
keybind.DefaultKey,
modifiers: keybind.ModifierKeys);
}
}
catch (Exception e)
{
Logger<MiraApiPlugin>.Error($"Error while registering keybinds in Rewired: {e}");
}
}
/// <summary>
/// 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()
{
var conflicts = new Dictionary<KeyboardKeyCode, List<MiraKeybind>>();
foreach (var keybind in Keybinds)
{
var all = GetKeybindsForKey(keybind.CurrentKey, true);
if (all.Length > 1)
{
if (!conflicts.TryGetValue(keybind.CurrentKey, out var group))
{
conflicts.Add(keybind.CurrentKey, new List<MiraKeybind>());
group = conflicts[keybind.CurrentKey];
}
if (!group.Contains(keybind))
{
group.Add(keybind);
}
}
}
return conflicts;
}
/// <summary>
/// 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 keybinds.</returns>
public static MiraKeybind[] GetKeybindsForKey(KeyboardKeyCode keyCode, bool exclusiveCheck = false)
{
var all = new List<MiraKeybind>();
foreach (var keybind in Keybinds)
{
if (keybind.CurrentKey == keyCode && (keybind.Exclusive || !exclusiveCheck))
{
all.Add(keybind);
}
}
return all.ToArray();
}
}
-80
View File
@@ -1,80 +0,0 @@
using System;
using Rewired;
namespace MiraAPI.Keybinds;
/// <summary>
/// Collection of keybind-related utility classes.
/// </summary>
public static class KeybindUtils
{
/// <summary>
/// Gets the Rewired <see cref="InputManager_Base"/> instance.
/// </summary>
public static InputManager_Base? RewiredInputManager { get; internal set; }
/// <summary>
/// Returns the currently assigned keycode of a keybind.
/// </summary>
/// <param name="keybind">The keybind to get the keycode from.</param>
/// <returns>The currently assigned keycode.</returns>
public static KeyboardKeyCode GetKeycodeByKeybind(BaseKeybind keybind)
{
if (keybind.RewiredInputAction == null)
{
return KeyboardKeyCode.None;
}
return GetKeycodeByActionId(keybind.RewiredInputAction.id);
}
/// <summary>
/// Gets the keycode for an action with ReInput.
/// </summary>
/// <param name="actionId">The action ID.</param>
/// <returns>The keyboard keycode.</returns>
public static KeyboardKeyCode GetKeycodeByActionId(int actionId)
{
return GetActionElementMap(actionId)?.keyboardKeyCode ?? KeyboardKeyCode.None;
}
/// <summary>
/// Gets a <see cref="ActionElementMap"/> by id with ReInput.
/// </summary>
/// <param name="actionId">The action ID.</param>
/// <returns>The keyboard keycode.</returns>
public static ActionElementMap? GetActionElementMap(int actionId)
{
var player = ReInput.players.GetPlayer(0);
return player.controllers.maps.GetFirstElementMapWithAction(ControllerType.Keyboard, actionId, false);
}
/// <summary>
/// Gets an <see cref="InputAction"/> by id.
/// </summary>
/// <param name="id">The action identifier name.</param>
/// <returns>The keyboard keycode.</returns>
public static InputAction? GetInputActionById(int id)
{
return RewiredInputManager?.userData?.GetActionById(id);
}
/// <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<KeyboardKeyCode>())
{
if (key == exclude) continue;
bool used = KeybindManager.Keybinds.Exists(e => e.DefaultKey == key);
if (!used) return key;
}
return KeyboardKeyCode.None;
}
}
-41
View File
@@ -1,41 +0,0 @@
using Rewired;
namespace MiraAPI.Keybinds;
/// <summary>
/// Class containing all default keybinds.
/// Recommended for cross-mod compatibility.
/// </summary>
[RegisterCustomKeybinds]
public static class MiraGlobalKeybinds
{
/// <summary>
/// Gets the keybind for primary abilities.
/// </summary>
public static MiraKeybind PrimaryAbility { get; } = new("Primary Ability", KeyboardKeyCode.T);
/// <summary>
/// Gets the keybind for secondary abilities.
/// </summary>
public static MiraKeybind SecondaryAbility { get; } = new("Secondary Ability", KeyboardKeyCode.Y);
/// <summary>
/// Gets the keybind for Tetiary abilities.
/// </summary>
public static MiraKeybind TertiaryAbility { get; } = new("Tertiary Ability", KeyboardKeyCode.U);
/// <summary>
/// Gets the keybind for primary modifier abilities.
/// </summary>
public static MiraKeybind ModifierPrimaryAbility { get; } = new("Modifier Primary Ability", KeyboardKeyCode.I);
/// <summary>
/// Gets the keybind for secondary modifier abilities.
/// </summary>
public static MiraKeybind ModifierSecondaryAbility { get; } = new("Modifier Secondary Ability", KeyboardKeyCode.O);
/// <summary>
/// Gets the keybind for Tetiary modifier abilities.
/// </summary>
public static MiraKeybind ModifierTertiaryAbility { get; } = new("Modifier Tertiary Ability", KeyboardKeyCode.P);
}
-58
View File
@@ -1,58 +0,0 @@
using System.Globalization;
using Rewired;
namespace MiraAPI.Keybinds;
/// <summary>
/// Represents a registered keybind.
/// </summary>
public class MiraKeybind : BaseKeybind
{
/// <summary>
/// Gets name of the keybind
/// </summary>
public string Name { get; }
/// <summary>
/// Gets the default keycode of this keybind.
/// </summary>
public KeyboardKeyCode DefaultKey { get; }
/// <summary>
/// Gets the modifier keys assigned to this keybind.
/// Due to Rewired limitations, there can only be 3 modifier keys.
/// </summary>
public ModifierKey[] ModifierKeys { get; }
/// <summary>
/// Gets a value indicating whether this keybind should be checked for conflicts with other exclusive keybinds using the same key.
/// </summary>
public bool Exclusive { get; }
internal string? SourcePluginName { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="MiraKeybind"/> class.
/// </summary>
/// <param name="name">The name of the keybind.</param>
/// <param name="defaultKeycode">The default keycode.</param>
/// <param name="modifierKeys">Up to 3 optional modifier keys.</param>
/// <param name="exclusive">Is exclusive.</param>
public MiraKeybind(
string name,
KeyboardKeyCode? defaultKeycode,
ModifierKey[]? modifierKeys = null,
bool exclusive = true) : base(name.ToLower(CultureInfo.InvariantCulture).Replace(' ', '_'))
{
Name = name;
DefaultKey = defaultKeycode ?? KeyboardKeyCode.None;
ModifierKeys = modifierKeys ?? [];
Exclusive = exclusive;
}
/// <inheritdoc/>
public override string ToString()
{
return $"{Name} ({Id})";
}
}
@@ -1,9 +0,0 @@
using System;
namespace MiraAPI.Keybinds;
/// <summary>
/// Used to mark a class for custom keybinds registration.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class RegisterCustomKeybindsAttribute : Attribute;
-24
View File
@@ -1,24 +0,0 @@
using Rewired;
namespace MiraAPI.Keybinds;
/// <summary>
/// Represents a vanilla keybind.
/// </summary>
public class VanillaKeybind : BaseKeybind
{
/// <summary>
/// Gets the <see cref="ActionButton"/> this keybind is binded to.
/// </summary>
public ActionButton? Button { get; internal set; }
/// <summary>
/// Initializes a new instance of the <see cref="VanillaKeybind"/> class.
/// </summary>
/// <param name="id">The keybind id.</param>
public VanillaKeybind(int id)
{
RewiredInputAction = KeybindUtils.GetInputActionById(id);
Id = RewiredInputAction?.name;
}
}
@@ -1,17 +0,0 @@
namespace MiraAPI.Keybinds;
/// <summary>
/// Singleton for getting a vanilla keybind by <see cref="ActionButton"/>.
/// </summary>
/// <typeparam name="T">ActionButton type.</typeparam>
public static class VanillaKeybinding<T> where T : ActionButton
{
private static VanillaKeybind? _instance;
/// <summary>
/// Gets the instance of the option group.
/// </summary>
#pragma warning disable CA1000
public static VanillaKeybind Instance => _instance ??= KeybindManager.VanillaKeybinds[typeof(T)];
#pragma warning restore CA1000
}
@@ -1,27 +0,0 @@
using System;
using BepInEx.Configuration;
using MiraAPI.LocalSettings.SettingTypes;
namespace MiraAPI.LocalSettings.Attributes;
/// <summary>
/// Creates an enum setting for the <see cref="ConfigEntry{T}"/>.
/// </summary>
/// <param name="names">Optional custom enum names.</param>
/// <inheritdoc/>
[AttributeUsage(AttributeTargets.Property)]
public class LocalEnumSettingAttribute(
string? name = null,
string? description = null,
string[]? names = null
) : LocalSettingAttribute(name, description)
{
private readonly string? _name = name;
private readonly string? _description = description;
/// <inheritdoc/>
internal override LocalEnumSetting CreateSetting(Type tab, ConfigEntryBase configEntryBase)
{
return new LocalEnumSetting(tab, configEntryBase, configEntryBase.SettingType, _name, _description, names);
}
}
@@ -1,36 +0,0 @@
using System;
using BepInEx.Configuration;
using MiraAPI.LocalSettings.SettingTypes;
using MiraAPI.Utilities;
namespace MiraAPI.LocalSettings.Attributes;
/// <summary>
/// Creates a number setting for the <see cref="ConfigEntry{T}"/>.
/// </summary>
/// <param name="min">Minimum range.</param>
/// <param name="max">Maximum range.</param>
/// <param name="increment">Increment per use.</param>
/// <param name="suffixType">Suffix for the value.</param>
/// <param name="formatString">Format string used when formating.</param>
/// <inheritdoc/>
[AttributeUsage(AttributeTargets.Property)]
public class LocalNumberSettingAttribute(
string? name = null,
string? description = null,
float min = 1,
float max = 5,
float increment = 1,
MiraNumberSuffixes suffixType = MiraNumberSuffixes.None,
string? formatString = null
) : LocalSettingAttribute(name, description)
{
private readonly string? _name = name;
private readonly string? _description = description;
/// <inheritdoc/>
internal override LocalNumberSetting CreateSetting(Type tab, ConfigEntryBase configEntryBase)
{
return new LocalNumberSetting(tab, configEntryBase, _name, _description, new FloatRange(min, max), increment, suffixType, formatString);
}
}
@@ -1,24 +0,0 @@
using System;
using BepInEx.Configuration;
namespace MiraAPI.LocalSettings.Attributes;
/// <summary>
/// Base class for all local settings attributes.
/// </summary>
/// <param name="name">The name of the setting. Defaults to entry key.</param>
/// <param name="description">The description of the setting. Defalts to entry description.</param>
[AttributeUsage(AttributeTargets.Property)]
public abstract class LocalSettingAttribute(
string? name = null,
string? description = null
) : Attribute
{
/// <summary>
/// Returns the created setting object.
/// </summary>
/// <param name="tab">Gets the tab where the setting is located.</param>
/// <param name="configEntryBase">Gets the config entry the setting is attached to.</param>
/// <returns>The created class.</returns>
internal abstract ILocalSetting CreateSetting(Type tab, ConfigEntryBase configEntryBase);
}
@@ -1,12 +0,0 @@
using System;
using BepInEx.Configuration;
namespace MiraAPI.LocalSettings.Attributes;
/// <summary>
/// Attribute for local setting buttons.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class LocalSettingsButtonAttribute : Attribute
{
}
@@ -1,36 +0,0 @@
using System;
using BepInEx.Configuration;
using MiraAPI.LocalSettings.SettingTypes;
using MiraAPI.Utilities;
namespace MiraAPI.LocalSettings.Attributes;
/// <summary>
/// Creates a slider setting for the <see cref="ConfigEntry{T}"/>.
/// </summary>
/// <inheritdoc/>
/// <param name="min">Minimum range.</param>
/// <param name="max">Maximum range.</param>
/// <param name="roundValue">Should the value be rounded.</param>
/// <param name="suffixType">Suffix for the value.</param>
[AttributeUsage(AttributeTargets.Property)]
public class LocalSliderSettingAttribute(
string? name = null,
float min = 0,
float max = 100,
string? description = null,
bool displayValue = false,
string? formatString = null,
bool roundValue = false,
MiraNumberSuffixes suffixType = MiraNumberSuffixes.None
) : LocalSettingAttribute(name, description)
{
private readonly string? _name = name;
private readonly string? _description = description;
/// <inheritdoc/>
internal override LocalSliderSetting CreateSetting(Type tab, ConfigEntryBase configEntryBase)
{
return new LocalSliderSetting(tab, configEntryBase, _name, _description, new FloatRange(min, max), displayValue, suffixType, formatString, roundValue);
}
}
@@ -1,25 +0,0 @@
using System;
using BepInEx.Configuration;
using MiraAPI.LocalSettings.SettingTypes;
namespace MiraAPI.LocalSettings.Attributes;
/// <summary>
/// Creates a toggle setting for the <see cref="ConfigEntry{T}"/>.
/// </summary>
/// <inheritdoc/>
[AttributeUsage(AttributeTargets.Property)]
public class LocalToggleSettingAttribute(
string? name = null,
string? description = null
) : LocalSettingAttribute(name, description)
{
private readonly string? _name = name;
private readonly string? _description = description;
/// <inheritdoc/>
internal override LocalToggleSetting CreateSetting(Type tab, ConfigEntryBase configEntryBase)
{
return new LocalToggleSetting(tab, configEntryBase, _name, _description);
}
}
-42
View File
@@ -1,42 +0,0 @@
using BepInEx.Configuration;
using UnityEngine;
namespace MiraAPI.LocalSettings;
/// <summary>
/// Interface for all local settings.
/// </summary>
public interface ILocalSetting
{
/// <summary>
/// Gets the preferred name of a setting.
/// </summary>
string Name { get; }
/// <summary>
/// Gets the description of a setting.
/// </summary>
string Description { get; }
/// <summary>
/// Gets the setting's gameobject.
/// </summary>
GameObject? Setting { get; }
/// <summary>
/// Gets the setting's config entry.
/// </summary>
ConfigEntryBase ConfigEntry { get; }
/// <summary>
/// Used to create the setting.
/// </summary>
/// <param name="toggle">Toggle template.</param>
/// <param name="slider">Slider template.</param>
/// <param name="parent">The parent of the setting.</param>
/// <param name="offset">The offset.</param>
/// <param name="order">The order.</param>
/// <param name="last">Whether it's the last on the row.</param>
/// <returns>The created setting.</returns>
GameObject? CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last);
}
@@ -1,78 +0,0 @@
using System;
using MiraAPI.Utilities;
using Reactor.Localization.Utilities;
using Reactor.Utilities.Extensions;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using Object = UnityEngine.Object;
namespace MiraAPI.LocalSettings;
/// <summary>
/// Represents a local settings button.
/// </summary>
public class LocalSettingsButton(string text, Action onClick)
{
/// <summary>
/// Gets or sets the button text.
/// </summary>
public string Text { get; set; } = text;
/// <summary>
/// Gets or sets the button on-click action.
/// </summary>
public Action OnClick { get; set; } = onClick;
/// <summary>
/// Gets the tab instance this button belongs to.
/// </summary>
public LocalSettingsTab? Tab { get; internal set; }
internal GameObject CreateButton(ToggleButtonBehaviour toggle, Transform parent, ref float offset, ref int order, bool last)
{
var button = Object.Instantiate(toggle, parent).GetComponent<PassiveButton>();
var tmp = button.transform.FindChild("Text_TMP").GetComponent<TextMeshPro>();
var rollover = button.GetComponent<ButtonRolloverHandler>();
tmp.GetComponent<TextTranslatorTMP>().Destroy();
button.gameObject.SetActive(true);
var toggleComp = button.GetComponent<ToggleButtonBehaviour>();
var background = toggleComp.Background;
button.transform.FindChild("ButtonHighlight")?.gameObject.DestroyImmediate();
toggleComp.Destroy();
if (last && order == 1)
{
// Button in the middle
button.transform.localPosition = new Vector3(0, 1.85f - offset, -7);
}
else
{
button.transform.localPosition = new Vector3(order == 1 ? -1.185f : 1.185f, 1.85f - offset, -7);
}
tmp.text = Text;
button.name = Text;
button.OnClick = new UnityEngine.UI.Button.ButtonClickedEvent();
rollover.OutColor = Tab!.TabAppearance.ButtonColor;
rollover.OverColor = Tab!.TabAppearance.ButtonHoverColor;
rollover.Target = background;
background.color = Tab!.TabAppearance.ButtonColor;
button.OnClick.AddListener((UnityAction)OnClick);
background.transform.localScale = new Vector3(1, 1.25f, 1);
Helpers.DivideSize(button.gameObject, 1.1f);
order++;
if (order > 2 && !last)
{
offset += 0.5f;
order = 1;
}
if (last)
offset += 0.6f;
return button.gameObject;
}
}
@@ -1,42 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using BepInEx.Unity.IL2CPP;
using MiraAPI.GameOptions;
using Reactor.Utilities;
namespace MiraAPI.LocalSettings;
/// <summary>
/// Manages local settings.
/// </summary>
public static class LocalSettingsManager
{
internal static readonly Dictionary<Type, LocalSettingsTab> TypeToTab = [];
internal static readonly List<LocalSettingsTab> Tabs = [];
internal static bool RegisterTab(Type type, BasePlugin pluginInfo)
{
if (Activator.CreateInstance(type, pluginInfo.Config) is not LocalSettingsTab tab)
{
return false;
}
if (TypeToTab.ContainsKey(type))
{
Logger<MiraApiPlugin>.Error($"Local settings tab {type.Name} already exists.");
return false;
}
Tabs.Add(tab);
TypeToTab.Add(type, tab);
typeof(LocalSettingsTabSingleton<>).MakeGenericType(type)
#pragma warning disable S3011
.GetField("_instance", BindingFlags.Static | BindingFlags.NonPublic)!
#pragma warning restore S3011
.SetValue(null, tab);
return true;
}
}
-297
View File
@@ -1,297 +0,0 @@
using System;
using System.Collections.Generic;
using BepInEx.Configuration;
using HarmonyLib;
using MiraAPI.Patches.LocalSettings;
using MiraAPI.Utilities;
using Reactor.Utilities.Extensions;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using Object = UnityEngine.Object;
namespace MiraAPI.LocalSettings;
/// <summary>
/// Represents a local settings tab. Gets a button by default.
/// </summary>
public abstract class LocalSettingsTab(ConfigFile config)
{
/// <summary>
/// Gets the name of the tab.
/// </summary>
public abstract string TabName { get; }
/// <summary>
/// Gets the tab appearance.
/// </summary>
public virtual LocalSettingTabAppearance TabAppearance { get; } = new();
/// <summary>
/// Gets a value indicating whether a button should be created for the tab.
/// </summary>
public virtual bool ShouldCreateButton => true;
/// <summary>
/// Gets a value indicating whether the tab should have category labels.
/// </summary>
protected virtual bool ShouldCreateLabels => true;
/// <summary>
/// Gets the mod's config file. Automatically found in the plugin loader.
/// </summary>
public ConfigFile Config { get; } = config ?? throw new ArgumentNullException(nameof(config));
/// <summary>
/// Gets the tab button instance.
/// </summary>
public TabGroup? TabButton { get; internal set; }
/// <summary>
/// Gets the list of settings.
/// </summary>
public List<ILocalSetting> Settings { get; } = [];
/// <summary>
/// Gets the list of buttons.
/// </summary>
public List<LocalSettingsButton> Buttons { get; } = [];
/// <summary>
/// Gets the index of this tab.
/// </summary>
protected int TabIndex => LocalSettingsManager.Tabs.IndexOf(this) + 10;
private Scroller? Scroller { get; set; }
/// <summary>
/// Invoked when an option value is changed.
/// </summary>
/// <param name="configEntry">The config entry which was changed.</param>
public virtual void OnOptionChanged(ConfigEntryBase configEntry)
{
}
/// <summary>
/// Attempts to open the tab.
/// Override for custom behavior.
/// </summary>
public virtual void Open()
{
OptionsMenuPatches.Instance?.OpenTabGroup(TabIndex);
}
/// <summary>
/// Creates the tab <see cref="GameObject"/> and it's content.
/// </summary>
/// <param name="instance">The <see cref="OptionsMenuBehaviour"/> instace.</param>
/// <returns>The created tab <see cref="GameObject"/>.</returns>
public virtual GameObject CreateTab(OptionsMenuBehaviour instance)
{
var tab = Object.Instantiate(instance.transform.FindChild("GeneralTab").gameObject, instance.transform);
tab.name = $"{TabName}Tab";
tab.transform.DestroyChildren();
tab.gameObject.SetActive(false);
if (Scroller == null)
{
Scroller = Helpers.CreateScroller(tab.transform, OptionsMenuPatches.MaskCollider);
}
if (TabButton != null)
{
TabButton.Content = tab.gameObject;
}
var generalLabel = instance.transform.FindChild("GeneralTab").FindChild("ControlGroup")
.FindChild("ControlText_TMP").gameObject;
var toggle = instance.transform.FindChild("GeneralTab").FindChild("ChatGroup").FindChild("CensorChatButton").GetComponent<ToggleButtonBehaviour>();
var slider = instance.transform.FindChild("GeneralTab").FindChild("SoundGroup").FindChild("SFXSlider").GetComponent<SlideBar>();
Dictionary<string, List<ILocalSetting>> entriesByGroup = new();
foreach (var entry in Settings)
{
var group = entry.ConfigEntry.Definition.Section;
if (!entriesByGroup.ContainsKey(group))
entriesByGroup.Add(group, []);
entriesByGroup[group].Add(entry);
}
float contentOffset = 0;
var contentOrder = 1;
var contentIndex = 1;
foreach (var pair in entriesByGroup)
{
if (ShouldCreateLabels)
{
CreateLabel(generalLabel, Scroller.Inner, pair.Key, ref contentOffset);
contentOrder = 1;
}
foreach (var setting in pair.Value)
{
var obj = setting.CreateOption(
toggle,
slider,
Scroller.Inner,
ref contentOffset,
ref contentOrder,
contentIndex == pair.Value.Count);
obj!.GetComponentsInChildren<SpriteRenderer>(true).Do(x =>
{
x.maskInteraction = SpriteMaskInteraction.VisibleInsideMask;
x.sortingOrder = 150;
x.sortingLayerName = "Default";
});
obj.GetComponentsInChildren<MeshRenderer>(true).Do(x =>
{
x.sortingOrder = 151;
x.sortingLayerName = "Default";
});
obj.GetComponentsInChildren<PassiveButton>().Do(x => { x.ClickMask = OptionsMenuPatches.MaskCollider; });
contentIndex++;
}
contentOrder = 1;
}
contentIndex = 1;
contentOrder = 1;
foreach (var button in Buttons)
{
var obj = button.CreateButton(
toggle,
Scroller.Inner,
ref contentOffset,
ref contentOrder,
contentIndex == Buttons.Count);
obj!.GetComponentsInChildren<SpriteRenderer>(true).Do(x =>
{
x.maskInteraction = SpriteMaskInteraction.VisibleInsideMask;
x.sortingOrder = 150;
x.sortingLayerName = "Default";
});
obj.GetComponentsInChildren<MeshRenderer>(true).Do(x =>
{
x.sortingOrder = 151;
x.sortingLayerName = "Default";
});
obj.GetComponentsInChildren<PassiveButton>().Do(x => { x.ClickMask = OptionsMenuPatches.MaskCollider; });
contentIndex++;
}
Scroller.SetBounds(new FloatRange(0, (Settings.Count + Buttons.Count) * 0.5f - 5f), new FloatRange(0, 0));
return tab;
}
/// <summary>
/// Creates the tab button.
/// </summary>
/// <param name="instance">The <see cref="OptionsMenuBehaviour"/> instance.</param>
/// <param name="tabIdx">The tab index.</param>
/// <param name="offset">The current button offset.</param>
/// <returns>The created button <see cref="GameObject"/>.</returns>
public virtual GameObject CreateTabButton(OptionsMenuBehaviour instance, ref int tabIdx, ref float offset)
{
var tabButtonObject = Object.Instantiate(instance.Tabs[0], instance.transform);
tabButtonObject.name = $"{TabName} Button";
tabButtonObject.transform.localPosition = new Vector3(2.4f, 2.1f - offset, 5.5f);
tabButtonObject.transform.localScale = new Vector3(1.25f, 1.25f, 1);
tabButtonObject.Button.color = TabAppearance.TabButtonColor;
TabButton = tabButtonObject;
var tabButtonText = tabButtonObject.transform.FindChild("Text_TMP").GetComponent<TextMeshPro>();
tabButtonText.GetComponent<TextTranslatorTMP>().Destroy(); // i hate text translators
tabButtonText.transform.localPosition = new Vector3(0.078f, 0, 0);
tabButtonText.transform.localScale = new Vector3(0.9f, 0.9f);
tabButtonText.alignment = TextAlignmentOptions.Right;
tabButtonText.text = $"<b>{GetShortName(TabName)}</b>";
var tabButton = tabButtonObject.GetComponent<PassiveButton>();
var rollover = tabButtonObject.Rollover;
rollover.OverColor = TabAppearance.TabButtonHoverColor;
rollover.OutColor = TabAppearance.TabButtonColor;
SpriteRenderer? tabButtonRend = null;
if (TabAppearance.TabIcon != null)
{
tabButtonRend = new GameObject("sprite").AddComponent<SpriteRenderer>();
tabButtonRend.gameObject.layer = 5; // ui layer
tabButtonRend.transform.SetParent(tabButtonObject.transform);
tabButtonRend.transform.localPosition = new Vector3(0.5f, 0, -2);
tabButtonRend.transform.localScale = new Vector3(0.2f, 0.2f, 1);
tabButtonRend.sprite = TabAppearance.TabIcon.LoadAsset();
tabButtonText.gameObject.SetActive(false);
}
var tabIndex = tabIdx; // Local copies of the tab variables so we can use them in the lambdas
var tabOffset = offset;
tabButton.OnClick.AddListener((UnityAction)(() => { instance.OpenTabGroup(tabIndex + 10); }));
tabButton.OnMouseOver.AddListener((UnityAction)(() =>
{
tabButton.transform.localPosition = new Vector3(3.55f, 2.1f - tabOffset, 5.5f);
tabButtonText.gameObject.SetActive(true);
tabButtonText.transform.localPosition = new Vector3(-0.024f, 0, 0);
tabButtonText.maxVisibleCharacters = int.MaxValue;
tabButtonText.text = TabName;
tabButtonText.alignment = TextAlignmentOptions.Left;
}));
tabButton.OnMouseOut.AddListener((UnityAction)(() =>
{
if (!tabButtonObject.Content.gameObject.activeSelf)
{
//tabButtonObject.Button.color = TabColor;
//tabButtonObject.Rollover.OutColor = TabColor;
}
tabButton.transform.localPosition = new Vector3(2.4f, 2.1f - tabOffset, 5.5f);
tabButtonText.alignment = TextAlignmentOptions.Right;
if (tabButtonRend != null)
{
tabButtonRend.gameObject.SetActive(true);
tabButtonText.gameObject.SetActive(false);
}
tabButtonText.transform.localPosition = new Vector3(0.1f, 0, 0);
tabButtonText.maxVisibleCharacters = 4;
tabButtonText.text = $"<b>{GetShortName(TabName)}</b>";
}));
return tabButtonObject.gameObject;
}
private static void CreateLabel(GameObject template, Transform parent, string text, ref float offset)
{
var label = Object.Instantiate(template, parent);
label.transform.localPosition = new Vector3(0, 1.85f - offset);
label.GetComponent<TextTranslatorTMP>().Destroy();
label.name = text;
var tmp = label.GetComponent<TextMeshPro>();
tmp.text = $"<font=\"LiberationSans SDF\" material=\"LiberationSans SDF - Chat Message Masked\"><b>{text}</b></font>";
tmp.alignment = TextAlignmentOptions.Center;
var meshRend = label.GetComponent<MeshRenderer>();
meshRend.sortingLayerName = "Default";
meshRend.sortingOrder = 151;
offset += 0.5f;
}
private static string GetShortName(string name)
{
var shortName = string.Empty;
name.Split(' ').Do(x => shortName += x[0]);
return shortName;
}
}
@@ -1,91 +0,0 @@
using MiraAPI.Utilities.Assets;
using UnityEngine;
namespace MiraAPI.LocalSettings;
/// <summary>
/// Struct for modyfing tab apparance.
/// Defines how the settings look.
/// </summary>
public record struct LocalSettingTabAppearance()
{
/// <summary>
/// Gets or sets main color of the tab.
/// </summary>
public Color TabColor { get; set; } = Color.white;
/// <summary>
/// Gets or sets tab button icon. No icon by default.
/// </summary>
public LoadableAsset<Sprite>? TabIcon { get; set; }
/// <summary>
/// Gets or sets color of the tab button.
/// </summary>
public Color TabButtonColor { get; set; } = Color.white;
/// <summary>
/// Gets or sets hover color of the tab button.
/// </summary>
public Color TabButtonHoverColor { get; set; } = Palette.AcceptedGreen;
/// <summary>
/// Gets or sets active color of the tab button.
/// </summary>
public Color TabButtonActiveColor { get; set; } = Palette.AcceptedGreen;
/// <summary>
/// Gets or sets hover color of toggles.
/// </summary>
public Color ToggleHoverColor { get; set; } = Palette.AcceptedGreen;
/// <summary>
/// Gets or sets active color of toggles.
/// </summary>
public Color ToggleActiveColor { get; set; } = Palette.AcceptedGreen;
/// <summary>
/// Gets or sets inactive color of toggles.
/// </summary>
public Color ToggleInactiveColor { get; set; } = Color.red;
/// <summary>
/// Gets or sets color of number buttons.
/// </summary>
public Color NumberColor { get; set; } = Color.white;
/// <summary>
/// Gets or sets hover color of number buttons.
/// </summary>
public Color NumberHoverColor { get; set; } = Palette.AcceptedGreen;
/// <summary>
/// Gets or sets color of sliders.
/// </summary>
public Color SliderColor { get; set; } = Color.white;
/// <summary>
/// Gets or sets hover color of sliders.
/// </summary>
public Color SliderHoverColor { get; set; } = Palette.AcceptedGreen;
/// <summary>
/// Gets or sets color of enum buttons.
/// </summary>
public Color EnumColor { get; set; } = Color.white;
/// <summary>
/// Gets or sets hover color of enum buttons.
/// </summary>
public Color EnumHoverColor { get; set; } = Palette.AcceptedGreen;
/// <summary>
/// Gets or sets color of buttons.
/// </summary>
public Color ButtonColor { get; set; } = Color.white;
/// <summary>
/// Gets or sets hover color of buttons.
/// </summary>
public Color ButtonHoverColor { get; set; } = Palette.AcceptedGreen;
}
@@ -1,19 +0,0 @@
using System.Linq;
namespace MiraAPI.LocalSettings;
/// <summary>
/// Singleton for local settings tabs.
/// </summary>
/// <typeparam name="T">The settings tab type.</typeparam>
public static class LocalSettingsTabSingleton<T> where T : LocalSettingsTab
{
private static T? _instance;
/// <summary>
/// Gets the instance of the setting tab.
/// </summary>
#pragma warning disable CA1000
public static T Instance => _instance ??= LocalSettingsManager.Tabs.OfType<T>().Single();
#pragma warning restore CA1000
}
@@ -1,132 +0,0 @@
using System;
using System.Linq;
using BepInEx.Configuration;
using MiraAPI.Utilities;
using Reactor.Utilities.Extensions;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using Object = UnityEngine.Object;
namespace MiraAPI.LocalSettings.SettingTypes;
/// <summary>
/// Local setting class for enums.
/// </summary>
public class LocalEnumSetting : LocalSettingBase<int>
{
/// <summary>
/// Gets the enum type of the setting.
/// </summary>
public Type EnumType { get; }
/// <summary>
/// Gets the enum values.
/// </summary>
public string[] Values { get; }
/// <summary>
/// Initializes a new instance of the <see cref="LocalEnumSetting"/> class.
/// </summary>
/// <inheritdoc/>
/// <param name="enumType">The enum type.</param>
/// <param name="values">The optional values array to replace the enum names.</param>
public LocalEnumSetting(
Type tab,
ConfigEntryBase configEntry,
Type enumType,
string? name = null,
string? description = null,
string[]? values = null)
: base(tab, configEntry, name, description)
{
EnumType = enumType;
Values = values ?? Enum
.GetValues(configEntry.SettingType)
.Cast<Enum>()
.Select(x => x.ToDisplayString())
.ToArray();
}
/// <inheritdoc />
public override GameObject CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last)
{
var button = Object.Instantiate(toggle, parent).GetComponent<PassiveButton>();
var tmp = button.transform.FindChild("Text_TMP").GetComponent<TextMeshPro>();
var rollover = button.GetComponent<ButtonRolloverHandler>();
tmp.GetComponent<TextTranslatorTMP>().Destroy();
button.gameObject.SetActive(true);
var toggleComp = button.GetComponent<ToggleButtonBehaviour>();
var background = toggleComp.Background;
var highlight = button.transform.FindChild("ButtonHighlight")?.GetComponent<SpriteRenderer>();
if (highlight != null)
{
highlight.color = Tab!.TabAppearance.EnumHoverColor;
highlight.gameObject.SetActive(false);
}
toggleComp.Destroy();
if (last && order == 1)
{
// Button in the middle
button.transform.localPosition = new Vector3(0, 1.85f - offset, -7);
}
else
{
button.transform.localPosition = new Vector3(order == 1 ? -1.185f : 1.185f, 1.85f - offset, -7);
}
tmp.text = GetValueText();
button.name = Name;
button.OnClick = new UnityEngine.UI.Button.ButtonClickedEvent();
rollover.OutColor = Tab!.TabAppearance.EnumColor;
rollover.OverColor = Tab!.TabAppearance.EnumHoverColor;
background.color = Tab!.TabAppearance.EnumColor;
button.OnClick.AddListener((UnityAction)(() =>
{
int value = GetValue();
value++;
if (value >= Values.Length)
{
value = 0;
}
SetValue(value);
tmp.text = GetValueText();
}));
button.OnMouseOver.AddListener((UnityAction)(() =>
{
if (!Description.IsNullOrWhiteSpace())
{
tmp.text = Description;
}
highlight?.gameObject.SetActive(true);
}));
button.OnMouseOut.AddListener((UnityAction)(() =>
{
tmp.text = GetValueText();
highlight?.gameObject.SetActive(false);
}));
Helpers.DivideSize(button.gameObject, 1.1f);
order++;
if (order > 2 && !last)
{
offset += 0.5f;
order = 1;
}
if (last)
offset += 0.6f;
return button.gameObject;
}
/// <inheritdoc/>
protected override string GetValueText()
{
return $"<font=\"LiberationSans SDF\" material=\"LiberationSans SDF - Chat Message Masked\">{Name}: <b>{Values[GetValue()]}</font></b>";
}
}
@@ -1,145 +0,0 @@
using System;
using BepInEx.Configuration;
using MiraAPI.Utilities;
using Reactor.Utilities.Extensions;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using Object = UnityEngine.Object;
namespace MiraAPI.LocalSettings.SettingTypes;
/// <summary>
/// Local setting class for numbers.
/// </summary>
public class LocalNumberSetting : LocalSettingBase<float>
{
/// <summary>
/// Gets the range of the button.
/// </summary>
public FloatRange NumberRange { get; }
/// <summary>
/// Gets the increment of the value when button is pressed.
/// </summary>
public float Increment { get; }
/// <summary>
/// Gets a format for the text to use to format the number.
/// </summary>
public string FormatString { get; }
/// <summary>
/// Gets the suffix for the number value.
/// </summary>
public MiraNumberSuffixes SuffixType { get; }
/// <summary>
/// Initializes a new instance of the <see cref="LocalNumberSetting"/> class.
/// </summary>
/// <inheritdoc/>
/// <param name="numberRange">The value range.</param>
/// <param name="increment">The increment per click.</param>
/// <param name="suffixType">The suffix used for formating.</param>
/// <param name="formatString">The format string used for formating.</param>
public LocalNumberSetting(
Type tab,
ConfigEntryBase configEntry,
string? name = null,
string? description = null,
FloatRange? numberRange = null,
float? increment = null,
MiraNumberSuffixes? suffixType = null,
string? formatString = null)
: base(tab, configEntry, name, description)
{
SuffixType = suffixType ?? MiraNumberSuffixes.None;
NumberRange = numberRange ?? new FloatRange(1, 5);
Increment = increment ?? 1;
FormatString = formatString ?? "0";
}
/// <inheritdoc />
public override GameObject CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last)
{
var button = Object.Instantiate(toggle, parent).GetComponent<PassiveButton>();
var tmp = button.transform.FindChild("Text_TMP").GetComponent<TextMeshPro>();
var rollover = button.GetComponent<ButtonRolloverHandler>();
tmp.GetComponent<TextTranslatorTMP>().Destroy();
button.gameObject.SetActive(true);
var toggleComp = button.GetComponent<ToggleButtonBehaviour>();
var background = toggleComp.Background;
var highlight = button.transform.FindChild("ButtonHighlight")?.GetComponent<SpriteRenderer>();
if (highlight != null)
{
highlight.color = Tab!.TabAppearance.NumberHoverColor;
highlight.gameObject.SetActive(false);
}
toggleComp.Destroy();
if (last && order == 1)
{
// Button in the middle
button.transform.localPosition = new Vector3(0, 1.85f - offset, -7);
}
else
{
button.transform.localPosition = new Vector3(order == 1 ? -1.185f : 1.185f, 1.85f - offset, -7);
}
tmp.text = GetValueText();
button.name = Name;
button.OnClick = new UnityEngine.UI.Button.ButtonClickedEvent();
rollover.OutColor = Tab!.TabAppearance.NumberColor;
rollover.OverColor = Tab!.TabAppearance.NumberHoverColor;
background.color = Tab!.TabAppearance.NumberColor;
button.OnClick.AddListener((UnityAction)(() =>
{
float value = GetValue();
value += Increment;
if (value > NumberRange.max)
{
value = NumberRange.min;
}
SetValue(value);
tmp.text = GetValueText();
}));
button.OnMouseOver.AddListener((UnityAction)(() =>
{
if (!Description.IsNullOrWhiteSpace())
{
tmp.text = Description;
}
highlight?.gameObject.SetActive(true);
}));
button.OnMouseOut.AddListener((UnityAction)(() =>
{
tmp.text = GetValueText();
highlight?.gameObject.SetActive(false);
}));
Helpers.DivideSize(button.gameObject, 1.1f);
order++;
if (order > 2 && !last)
{
offset += 0.5f;
order = 1;
}
if (last)
offset += 0.6f;
return button.gameObject;
}
/// <inheritdoc/>
protected override string GetValueText()
{
var value = GetValue();
var formated = Helpers.FormatValue(value, SuffixType, FormatString);
return $"<font=\"LiberationSans SDF\" material=\"LiberationSans SDF - Chat Message Masked\">{Name}: <b>{formated}</font></b>";
}
}
@@ -1,73 +0,0 @@
using System;
using System.Linq;
using BepInEx.Configuration;
using UnityEngine;
namespace MiraAPI.LocalSettings.SettingTypes;
/// <inheritdoc />
public abstract class LocalSettingBase<T> : ILocalSetting
{
/// <inheritdoc />
public string Name { get; }
/// <inheritdoc />
public string Description { get; }
/// <inheritdoc />
public GameObject? Setting { get; } = null!;
/// <inheritdoc />
public ConfigEntryBase ConfigEntry { get; }
internal LocalSettingsTab? Tab => LocalSettingsManager.Tabs.FirstOrDefault(x => x.Settings.Contains(this));
/// <summary>
/// Initializes a new instance of the <see cref="LocalSettingBase{T}"/> class.
/// </summary>
/// <param name="tab">The tab to create the setting in.</param>
/// <param name="configEntry">The config entry.</param>
/// <param name="name">The name of the setting.</param>
/// <param name="description">The description of the setting.</param>
protected LocalSettingBase(Type tab, ConfigEntryBase configEntry, string? name = null, string? description = null)
{
ConfigEntry = configEntry;
Name = name ?? ConfigEntry.Definition.Key;
Description = description ?? ConfigEntry.Description.Description;
LocalSettingsManager.TypeToTab[tab].Settings.Add(this);
}
/// <summary>
/// Creates an instance of the setting.
/// </summary>
/// <returns>The created setting.</returns>
public abstract GameObject CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last);
/// <summary>
/// Returns the formated string to use in the text of the setting.
/// </summary>
/// <returns>The value text.</returns>
protected virtual string GetValueText()
{
return string.Empty;
}
/// <summary>
/// Gets the value of the config entry, casted to the setting type.
/// </summary>
/// <returns>The value.</returns>
public virtual T GetValue()
{
return (T)ConfigEntry.BoxedValue;
}
/// <summary>
/// Sets the value of the config entry.
/// </summary>
/// <param name="value">The value to set to.</param>
public virtual void SetValue(T value)
{
ConfigEntry.BoxedValue = value;
Tab?.OnOptionChanged(ConfigEntry);
}
}
@@ -1,120 +0,0 @@
using System;
using BepInEx.Configuration;
using MiraAPI.Utilities;
using Reactor.Utilities.Extensions;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using Object = UnityEngine.Object;
namespace MiraAPI.LocalSettings.SettingTypes;
/// <summary>
/// Local setting class for sliders.
/// </summary>
public class LocalSliderSetting : LocalSettingBase<float>
{
/// <summary>
/// Gets the range of the slider.
/// </summary>
public FloatRange SliderRange { get; }
/// <summary>
/// Gets a value indicating whether the value should be displayed next to name.
/// </summary>
public bool DisplayValue { get; }
/// <summary>
/// Gets a format for the text to use to format the number.
/// </summary>
public string FormatString { get; }
/// <summary>
/// Gets a value indicating whether the value should be rounded.
/// </summary>
public bool RoundValue { get; }
/// <summary>
/// Gets the suffix for the number value.
/// </summary>
public MiraNumberSuffixes SuffixType { get; }
/// <summary>
/// Initializes a new instance of the <see cref="LocalSliderSetting"/> class.
/// </summary>
/// <inheritdoc/>
/// <param name="sliderRange">The value range.</param>
/// <param name="suffixType">The suffix used for formating.</param>
/// <param name="formatString">The format string used for formating.</param>
/// <param name="roundValue">Should the value be rounded.</param>
public LocalSliderSetting(
Type tab,
ConfigEntryBase configEntry,
string? name = null,
string? description = null,
FloatRange? sliderRange = null,
bool displayValue = false,
MiraNumberSuffixes? suffixType = null,
string? formatString = null,
bool roundValue = false)
: base(tab, configEntry, name, description)
{
SliderRange = sliderRange ?? new FloatRange(0, 100);
DisplayValue = displayValue;
SuffixType = suffixType ?? MiraNumberSuffixes.None;
FormatString = formatString ?? "0.0";
RoundValue = roundValue;
}
/// <inheritdoc />
public override GameObject CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last)
{
var newSlider = Object.Instantiate(slider, parent).GetComponent<SlideBar>();
var rollover = newSlider.GetComponent<ButtonRolloverHandler>();
newSlider.Title = newSlider.transform.FindChild("Text_TMP").GetComponent<TextMeshPro>(); // Why the hell slider has a title property that is not even assigned???
newSlider.Title.GetComponent<TextTranslatorTMP>().Destroy();
newSlider.gameObject.SetActive(true);
newSlider.Bar.color = Tab!.TabAppearance.SliderColor;
if (order == 2)
offset += 0.5f;
newSlider.Bar.transform.localPosition = new Vector3(2.85f, 0, 0);
newSlider.transform.localPosition = new Vector3(-2.12f, 1.85f - offset, -7);
newSlider.name = Name;
newSlider.Range = new FloatRange(-1.5f, 1.5f);
newSlider.SetValue(Mathf.InverseLerp(SliderRange.min, SliderRange.max, GetValue()));
rollover.OutColor = Tab!.TabAppearance.SliderColor;
rollover.OverColor = Tab!.TabAppearance.SliderHoverColor;
newSlider.Title.transform.localPosition = new Vector3(0.5f, 0, -1f);
newSlider.Title.horizontalAlignment = DisplayValue ? HorizontalAlignmentOptions.Left : HorizontalAlignmentOptions.Center;
newSlider.Title.text = GetValueText();
newSlider.OnValueChange.AddListener((UnityAction)(() =>
{
SetValue(RoundValue
? Mathf.Round(Mathf.Lerp(SliderRange.min, SliderRange.max, newSlider.Value))
: Mathf.Lerp(SliderRange.min, SliderRange.max, newSlider.Value));
newSlider.Title.text = GetValueText();
}));
order = 1;
offset += 0.5f;
return newSlider.gameObject;
}
/// <inheritdoc/>
protected override string GetValueText()
{
if (DisplayValue)
{
var value = GetValue();
var formated = Helpers.FormatValue(value, SuffixType, FormatString);
var maxFormated = Helpers.FormatValue(SliderRange.max, SuffixType, FormatString);
return $"<font=\"LiberationSans SDF\" material=\"LiberationSans SDF - Chat Message Masked\">{Name}: <b>{formated} / {maxFormated}</font></b>";
}
return $"<font=\"LiberationSans SDF\" material=\"LiberationSans SDF - Chat Message Masked\">{Name}</font></b>";
}
}
@@ -1,88 +0,0 @@
using System;
using BepInEx.Configuration;
using MiraAPI.Utilities;
using Reactor.Localization.Utilities;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using Object = UnityEngine.Object;
namespace MiraAPI.LocalSettings.SettingTypes;
/// <summary>
/// Local setting class for toggles.
/// </summary>
public class LocalToggleSetting : LocalSettingBase<bool>
{
/// <summary>
/// Initializes a new instance of the <see cref="LocalToggleSetting"/> class.
/// </summary>
/// <inheritdoc/>
public LocalToggleSetting(
Type tab,
ConfigEntryBase configEntry,
string? name = null,
string? description = null
) : base(tab, configEntry, name, description)
{
}
/// <inheritdoc />
public override GameObject CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last)
{
var toggleObject = Object.Instantiate(toggle, parent).GetComponent<ToggleButtonBehaviour>();
var tmp = toggleObject.transform.FindChild("Text_TMP").GetComponent<TextMeshPro>();
var passiveButton = toggleObject.GetComponent<PassiveButton>();
var rollover = toggleObject.GetComponent<ButtonRolloverHandler>();
toggleObject.gameObject.SetActive(true);
if (last && order == 1)
{
// Toggle in the middle
toggleObject.transform.localPosition = new Vector3(0, 1.85f - offset, -7);
}
else
{
toggleObject.transform.localPosition = new Vector3(order == 1 ? -1.185f : 1.185f, 1.85f - offset, -7);
}
toggleObject.BaseText = CustomStringName.CreateAndRegister(Name);
toggleObject.UpdateText(GetValue());
toggleObject.name = Name;
toggleObject.Background.color = GetValue() ? Tab!.TabAppearance.ToggleActiveColor : Tab!.TabAppearance.ToggleInactiveColor;
passiveButton.OnClick = new UnityEngine.UI.Button.ButtonClickedEvent();
rollover.OverColor = Tab!.TabAppearance.ToggleHoverColor;
passiveButton.OnClick.AddListener((UnityAction)(() =>
{
SetValue(!GetValue());
toggleObject.UpdateText(GetValue());
toggleObject.Background.color = GetValue() ? Tab!.TabAppearance.ToggleActiveColor : Tab!.TabAppearance.ToggleInactiveColor;
}));
passiveButton.OnMouseOver.AddListener((UnityAction)(() =>
{
if (!Description.IsNullOrWhiteSpace())
{
tmp.text = Description;
}
}));
passiveButton.OnMouseOut.AddListener((UnityAction)(() =>
{
toggleObject.UpdateText(GetValue());
toggleObject.Background.color = GetValue() ? Tab!.TabAppearance.ToggleActiveColor : Tab!.TabAppearance.ToggleInactiveColor;
}));
Helpers.DivideSize(toggleObject.gameObject, 1.1f);
order++;
if (order > 2 && !last)
{
offset += 0.5f;
order = 1;
}
if (last)
offset += 0.6f;
return toggleObject.gameObject;
}
}
-52
View File
@@ -1,52 +0,0 @@
using BepInEx.Configuration;
using MiraAPI.LocalSettings;
using MiraAPI.LocalSettings.Attributes;
using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets;
namespace MiraAPI;
/// <summary>
/// Mira API Config File Handler
/// </summary>
public class MiraApiSettings(ConfigFile config) : LocalSettingsTab(config)
{
/// <inheritdoc />
public override string TabName => "Mira API";
/// <inheritdoc />
public override LocalSettingTabAppearance TabAppearance => new()
{
TabButtonHoverColor = MiraApiPlugin.MiraColor,
TabIcon = MiraAssets.SettingsIcon,
};
/// <summary>
/// Gets whether the modifiers hud should be on the left side of the screen (under roles/task tab). Recommended for streamers.
/// </summary>
[LocalToggleSetting]
public ConfigEntry<bool> ModifiersHudLeftSide { get; private set; } = config.Bind("Displays", "Show Modifiers HUD on Left Side", false);
/// <summary>
/// Gets whether vents and their connections appear on the map.
/// </summary>
[LocalToggleSetting]
public ConfigEntry<bool> ShowVentsOnMap { get; private set; } = config.Bind("Displays", "Show Vent Layout on Map", true);
/// <summary>
/// Gets whether cooldowns for buttons below 10 seconds appear with decimals.
/// </summary>
[LocalToggleSetting]
public ConfigEntry<bool> PreciseCooldowns { get; private set; } = config.Bind("Buttons", "Precise Button Cooldowns Under 10s", false);
/// <summary>
/// Gets the size of ability buttons in-game.
/// </summary>
[LocalSliderSetting(min: 0.5f, max: 1.5f, displayValue: true, formatString: "0.00", suffixType: MiraNumberSuffixes.Multiplier)]
public ConfigEntry<float> ButtonUiScale { get; private set; } = config.Bind("Buttons", "Button Scale Factor", 0.8f);
/// <summary>
/// Gets whether to show keybinds in the control mapper.
/// </summary>
public ConfigEntry<bool> ShowKeybinds { get; private set; } = config.Bind("Keybinds", "Show Keybinds in Control Mapper", true);
}
+2 -7
View File
@@ -40,8 +40,8 @@ public abstract class BaseModifier : IOptionable
/// Gets the parent mod of the modifier. /// Gets the parent mod of the modifier.
/// </summary> /// </summary>
public MiraPluginInfo ParentMod => Array.Find( public MiraPluginInfo ParentMod => Array.Find(
MiraPluginManager.Instance.RegisteredPlugins, MiraPluginManager.Instance.RegisteredPlugins(),
x => x.InternalModifiers.Exists(y => y.TypeId == TypeId) x => x.Modifiers.Exists(y => y.TypeId == TypeId)
) ?? throw new InvalidOperationException("Modifier is not registered."); ) ?? throw new InvalidOperationException("Modifier is not registered.");
/// <summary> /// <summary>
@@ -64,11 +64,6 @@ 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>
+1 -14
View File
@@ -4,10 +4,8 @@ using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using Il2CppInterop.Runtime.Attributes; using Il2CppInterop.Runtime.Attributes;
using MiraAPI.LocalSettings;
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;
@@ -128,18 +126,7 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
if (_player.AmOwner && ModifierDisplay && HudManager.InstanceExists) if (_player.AmOwner && ModifierDisplay && HudManager.InstanceExists)
{ {
var taskPanelOpen = HudManager.Instance.TaskPanel.open; ModifierDisplay!.gameObject.SetActive(MeetingHud.Instance || HudManager.Instance.TaskPanel.isActiveAndEnabled);
var roleTabOpen = HudManagerPatches.RoleTab != null && HudManagerPatches.RoleTab.open;
var leftSideHud = LocalSettingsTabSingleton<MiraApiSettings>.Instance.ModifiersHudLeftSide.Value;
if (leftSideHud && (taskPanelOpen || roleTabOpen))
{
ModifierDisplay!.gameObject.SetActive(false);
}
else
{
ModifierDisplay!.gameObject.SetActive(MeetingHud.Instance || HudManager.Instance.TaskPanel.isActiveAndEnabled);
}
} }
} }
@@ -3,7 +3,6 @@ using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using HarmonyLib; using HarmonyLib;
using Il2CppInterop.Runtime.Attributes; using Il2CppInterop.Runtime.Attributes;
using MiraAPI.LocalSettings;
using MiraAPI.Utilities.Assets; using MiraAPI.Utilities.Assets;
using Reactor.Utilities.Attributes; using Reactor.Utilities.Attributes;
using Reactor.Utilities.Extensions; using Reactor.Utilities.Extensions;
@@ -95,16 +94,6 @@ public class ModifierDisplayComponent(nint cppPtr) : MonoBehaviour(cppPtr)
IsOpen = false; IsOpen = false;
_children.gameObject.SetActive(false); _children.gameObject.SetActive(false);
if (LocalSettingsTabSingleton<MiraApiSettings>.Instance.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>
@@ -120,16 +109,7 @@ public class ModifierDisplayComponent(nint cppPtr) : MonoBehaviour(cppPtr)
/// </summary> /// </summary>
public void ToggleTab() public void ToggleTab()
{ {
SetOpened(!IsOpen); IsOpen = !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();
} }
@@ -138,6 +118,7 @@ public class ModifierDisplayComponent(nint cppPtr) : MonoBehaviour(cppPtr)
{ {
_modifiers.Remove(component.Modifier!); _modifiers.Remove(component.Modifier!);
component.gameObject.DestroyImmediate(); component.gameObject.DestroyImmediate();
RefreshModifiers(); RefreshModifiers();
} }
@@ -213,7 +194,13 @@ public class ModifierDisplayComponent(nint cppPtr) : MonoBehaviour(cppPtr)
_toggleBtnText.text = $"Modifiers ({filteredModifiers.Count})"; _toggleBtnText.text = $"Modifiers ({filteredModifiers.Count})";
_toggleButton.gameObject.SetActive(filteredModifiers.Count != 0); if (filteredModifiers.Count == 0)
{
_toggleButton.gameObject.SetActive(false);
return;
}
_toggleButton.gameObject.SetActive(true);
foreach (var mod in modifiersToAdd) foreach (var mod in modifiersToAdd)
{ {
+1 -1
View File
@@ -30,7 +30,7 @@ public static class ModifierExtensions
_ = ModifierManager.GetModifierType(typeId) ?? throw new InvalidOperationException( _ = ModifierManager.GetModifierType(typeId) ?? throw new InvalidOperationException(
$"Modifier with ID {typeId} is not registered."); $"Modifier with ID {typeId} is not registered.");
Rpc<AddModifierRpc>.Instance.Send(target, new ModifierData(typeId, Guid.NewGuid(), args), true); Rpc<AddModifierRpc>.Instance.Send(target, new ModifierData(typeId, Guid.NewGuid(), args));
} }
/// <summary> /// <summary>
+2 -21
View File
@@ -5,7 +5,6 @@ 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;
@@ -17,18 +16,6 @@ namespace MiraAPI.Modifiers;
/// </summary> /// </summary>
public static class ModifierManager public static class ModifierManager
{ {
/// <summary>
/// Gets or sets a value indicating whether modifiers should be assigned to players.
/// </summary>
public static bool MiraAssignsModifiers { get; set; } = true;
/// <summary>
/// Gets the list of all registered modifiers.
/// </summary>
public static IReadOnlyList<BaseModifier> Modifiers { get; internal set; } = [];
internal static List<BaseModifier> InternalModifiers { get; } = [];
private static readonly Dictionary<uint, Type> IdToTypeModifierMap = []; private static readonly Dictionary<uint, Type> IdToTypeModifierMap = [];
private static readonly Dictionary<Type, uint> TypeToIdModifierMap = []; private static readonly Dictionary<Type, uint> TypeToIdModifierMap = [];
private static readonly Dictionary<int, List<uint>> PrioritiesToIdsMap = []; private static readonly Dictionary<int, List<uint>> PrioritiesToIdsMap = [];
@@ -87,8 +74,7 @@ public static class ModifierManager
modifier = (BaseModifier)FormatterServices.GetUninitializedObject(modifierType); modifier = (BaseModifier)FormatterServices.GetUninitializedObject(modifierType);
} }
info.InternalModifiers.Add(modifier); info.Modifiers.Add(modifier);
InternalModifiers.Add(modifier);
if (modifier is not GameModifier gameModifier) if (modifier is not GameModifier gameModifier)
{ {
@@ -112,11 +98,7 @@ public static class ModifierManager
return true; return true;
} }
/// <summary> internal static void AssignModifiers(List<PlayerControl> plrs)
/// Assigns modifiers to players. ONLY CALL THIS METHOD IF YOU KNOW WHAT YOU ARE DOING.
/// </summary>
/// <param name="plrs">The players to assign modifiers to.</param>
public static void AssignModifiers(List<PlayerControl> plrs)
{ {
var rand = new Random(); var rand = new Random();
@@ -126,7 +108,6 @@ 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();
+1 -1
View File
@@ -29,7 +29,7 @@ public static class CustomMurderRpc
/// <param name="teleportMurderer">Should the killer be snapped to the dead player.</param> /// <param name="teleportMurderer">Should the killer be snapped to the dead player.</param>
/// <param name="showKillAnim">Should the kill animation be shown.</param> /// <param name="showKillAnim">Should the kill animation be shown.</param>
/// <param name="playKillSound">Should the kill sound be played.</param> /// <param name="playKillSound">Should the kill sound be played.</param>
[MethodRpc((uint)MiraRpc.CustomMurder, LocalHandling = RpcLocalHandling.Before)] [MethodRpc((uint)MiraRpc.CustomMurder, LocalHandling = RpcLocalHandling.Before, SendImmediately = true)]
public static void RpcCustomMurder( public static void RpcCustomMurder(
this PlayerControl source, this PlayerControl source,
PlayerControl target, PlayerControl target,
-35
View File
@@ -1,35 +0,0 @@
using HarmonyLib;
using UnityEngine;
namespace MiraAPI.Patches;
#pragma warning disable SA1629
/// <summary>
/// Allows players to paste text into chat.
/// Source: https://github.com/CallOfCreator/NewMod/blob/main/NewMod/Patches/ClipboardPatch.cs
/// </summary>
#pragma warning restore SA1629
[HarmonyPatch(typeof(ChatController), nameof(ChatController.Update))]
public static class ChatControllerPatch
{
public static void Prefix(ChatController __instance)
{
if (!HudManager.Instance.Chat.IsOpenOrOpening) return;
var ctrlPressed = Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl);
if (!ctrlPressed || !Input.GetKeyDown(KeyCode.V)) return;
var clipboard = GUIUtility.systemCopyBuffer;
if (string.IsNullOrWhiteSpace(clipboard)) return;
clipboard = clipboard.Replace("<", string.Empty)
.Replace(">", string.Empty)
.Replace("\r", string.Empty);
if (!Input.GetKey(KeyCode.LeftShift) && !Input.GetKey(KeyCode.RightShift))
clipboard = clipboard.Replace("\n", string.Empty);
__instance.freeChatField.textArea.SetText(__instance.freeChatField.textArea.text + clipboard);
}
}
+5 -7
View File
@@ -12,16 +12,14 @@ public static class CanUsePatches
public static IEnumerable<MethodBase> TargetMethods() public static IEnumerable<MethodBase> TargetMethods()
{ {
yield return AccessTools.Method(typeof(Console), nameof(Console.CanUse)); yield return AccessTools.Method(typeof(Console), nameof(Console.CanUse));
yield return AccessTools.Method(typeof(DeconControl), nameof(DeconControl.CanUse));
yield return AccessTools.Method(typeof(DoorConsole), nameof(DoorConsole.CanUse));
yield return AccessTools.Method(typeof(Ladder), nameof(Ladder.CanUse));
yield return AccessTools.Method(typeof(MapConsole), nameof(MapConsole.CanUse)); yield return AccessTools.Method(typeof(MapConsole), nameof(MapConsole.CanUse));
yield return AccessTools.Method(typeof(OpenDoorConsole), nameof(OpenDoorConsole.CanUse));
yield return AccessTools.Method(typeof(OptionsConsole), nameof(OptionsConsole.CanUse));
yield return AccessTools.Method(typeof(PlatformConsole), nameof(PlatformConsole.CanUse));
yield return AccessTools.Method(typeof(SystemConsole), nameof(SystemConsole.CanUse)); yield return AccessTools.Method(typeof(SystemConsole), nameof(SystemConsole.CanUse));
yield return AccessTools.Method(typeof(Ladder), nameof(Ladder.CanUse));
yield return AccessTools.Method(typeof(PlatformConsole), nameof(PlatformConsole.CanUse));
yield return AccessTools.Method(typeof(OpenDoorConsole), nameof(OpenDoorConsole.CanUse));
yield return AccessTools.Method(typeof(DoorConsole), nameof(DoorConsole.CanUse));
yield return AccessTools.Method(typeof(OptionsConsole), nameof(OptionsConsole.CanUse));
yield return AccessTools.Method(typeof(ZiplineConsole), nameof(ZiplineConsole.CanUse)); yield return AccessTools.Method(typeof(ZiplineConsole), nameof(ZiplineConsole.CanUse));
// vent is not necessary as it has its own patch
} }
[HarmonyPriority(Priority.Last)] [HarmonyPriority(Priority.Last)]
+13 -28
View File
@@ -5,44 +5,29 @@ using MiraAPI.GameEnd;
namespace MiraAPI.Patches.Events; namespace MiraAPI.Patches.Events;
[HarmonyPatch(typeof(EndGameManager), nameof(EndGameManager.SetEverythingUp))]
internal static class EndGamePatches internal static class EndGamePatches
{ {
[HarmonyPatch(typeof(GameManager), nameof(GameManager.RpcEndGame))] public static bool Prefix(EndGameManager __instance)
public static class GameManagerPatches
{ {
public static bool Prefix(GameOverReason endReason) if (CustomGameOver.Instance is not { } gameOver)
{ {
var @event = new BeforeGameEndEvent(endReason); return true;
MiraEventManager.InvokeEvent(@event);
return !@event.IsCancelled;
} }
var result = gameOver.BeforeEndGameSetup(__instance);
return result;
} }
[HarmonyPatch(typeof(EndGameManager), nameof(EndGameManager.SetEverythingUp))] public static void Postfix(EndGameManager __instance)
public static class EndGameManagerPatches
{ {
public static bool Prefix(EndGameManager __instance) if (CustomGameOver.Instance is { } gameOver)
{ {
if (CustomGameOver.Instance is not { } gameOver) gameOver.AfterEndGameSetup(__instance);
{ CustomGameOver.Instance = null;
return true;
}
var result = gameOver.BeforeEndGameSetup(__instance);
return result;
} }
public static void Postfix(EndGameManager __instance) var @event = new GameEndEvent(__instance);
{ MiraEventManager.InvokeEvent(@event);
if (CustomGameOver.Instance is { } gameOver)
{
gameOver.AfterEndGameSetup(__instance);
CustomGameOver.Instance = null;
}
var @event = new GameEndEvent(__instance);
MiraEventManager.InvokeEvent(@event);
}
} }
} }
@@ -1,17 +0,0 @@
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));
}
}
}
@@ -14,17 +14,13 @@ public static class KillAnimationPatches
/// <summary> /// <summary>
/// Used to trigger the <see cref="AfterMurderEvent"/>. /// Used to trigger the <see cref="AfterMurderEvent"/>.
/// </summary> /// </summary>
/// <param name="source">The killer.</param>
/// <param name="target">The target.</param>
[HarmonyPostfix] [HarmonyPostfix]
[HarmonyPatch(typeof(KillAnimation._CoPerformKill_d__2), nameof(KillAnimation._CoPerformKill_d__2.MoveNext))] [HarmonyPatch(typeof(KillAnimation), nameof(KillAnimation.CoPerformKill))]
public static void KillAnimPostfix(KillAnimation._CoPerformKill_d__2 __instance) public static void KillAnimPostfix(PlayerControl source, PlayerControl target)
{ {
// Checking if state is -1 to wait for the end of coroutine. var afterMurderEvent = new AfterMurderEvent(source, target, Helpers.GetBodyById(target.PlayerId));
if (__instance.__1__state != -1)
{
return;
}
var afterMurderEvent = new AfterMurderEvent(__instance.source, __instance.target, Helpers.GetBodyById(__instance.target.PlayerId));
MiraEventManager.InvokeEvent(afterMurderEvent); MiraEventManager.InvokeEvent(afterMurderEvent);
} }
} }
+5 -36
View File
@@ -1,52 +1,21 @@
using AmongUs.InnerNet.GameDataMessages; using HarmonyLib;
using HarmonyLib;
using InnerNet;
using MiraAPI.Events; using MiraAPI.Events;
using MiraAPI.Events.Vanilla.Meeting; using MiraAPI.Events.Vanilla.Meeting;
using MiraAPI.Utilities; using MiraAPI.Utilities;
using Reactor.Utilities;
namespace MiraAPI.Patches.Events; namespace MiraAPI.Patches.Events;
[HarmonyPatch] [HarmonyPatch]
public static class ReportBodyEventPatch public static class ReportBodyEventPatch
{ {
private static bool ReportBodyEventHook(PlayerControl source, NetworkedPlayerInfo? target) [HarmonyPrefix]
[HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.CmdReportDeadBody))]
public static bool PlayerControlCmdReportDeadBodyPrefix(PlayerControl __instance, NetworkedPlayerInfo? target)
{ {
var body = target != null ? Helpers.GetBodyById(target.PlayerId) : null; var body = target != null ? Helpers.GetBodyById(target.PlayerId) : null;
var @event = new ReportBodyEvent(source, target, body); var @event = new ReportBodyEvent(__instance, target, body);
MiraEventManager.InvokeEvent(@event); MiraEventManager.InvokeEvent(@event);
return !@event.IsCancelled; return !@event.IsCancelled;
} }
[HarmonyPrefix]
[HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.ReportDeadBody))]
public static bool PlayerControlCmdReportDeadBodyPrefix(PlayerControl __instance, NetworkedPlayerInfo? target)
{
return ReportBodyEventHook(__instance, target);
}
[HarmonyPrefix]
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.LateBroadcastReliableMessage))]
public static bool LateBroadcastReliableMessagePrefix(IGameDataMessage rpcMessage)
{
var reportBodyMessage = rpcMessage.TryCast<RpcReportDeadBodyMessage>();
if (reportBodyMessage == null)
{
return true;
}
var source = AmongUsClient.Instance.FindObjectByNetId<PlayerControl>(reportBodyMessage.rpcObjectNetId);
if (source == null)
{
Logger<MiraApiPlugin>.Error("Failed to find source player for RpcReportDeadBodyMessage with NetId: " + reportBodyMessage.rpcObjectNetId);
return true;
}
var target = GameData.Instance.GetPlayerById(reportBodyMessage.targetPlayerId);
return ReportBodyEventHook(source, target);
}
} }
@@ -9,12 +9,7 @@ internal static class SpawnInMinigamePatch
{ {
public static void Postfix() public static void Postfix()
{ {
var @event = new BeforeRoundStartEvent(false); var @event = new RoundStartEvent(false);
MiraEventManager.InvokeEvent(@event); MiraEventManager.InvokeEvent(@event);
if (@event.IsCancelled) return;
var @event2 = new RoundStartEvent(false);
MiraEventManager.InvokeEvent(@event2);
} }
} }
@@ -25,12 +25,7 @@ internal static class SubmergedSpawnGamePatch
public static void Postfix() public static void Postfix()
{ {
var @event = new BeforeRoundStartEvent(false); var @event = new RoundStartEvent(false);
MiraEventManager.InvokeEvent(@event); MiraEventManager.InvokeEvent(@event);
if (@event.IsCancelled) return;
var @event2 = new RoundStartEvent(false);
MiraEventManager.InvokeEvent(@event2);
} }
} }
+4 -4
View File
@@ -23,10 +23,10 @@ public static class VentEventPatches
[HarmonyPrefix] [HarmonyPrefix]
[HarmonyPatch(typeof(PlayerPhysics), nameof(PlayerPhysics.RpcEnterVent))] [HarmonyPatch(typeof(PlayerPhysics), nameof(PlayerPhysics.RpcEnterVent))]
public static bool PlayerPhysicsRpcEnterVentPrefix(PlayerPhysics __instance, int ventId) public static bool PlayerPhysicsRpcEnterVentPrefix(PlayerPhysics __instance, int id)
{ {
var pc = __instance.myPlayer; var pc = __instance.myPlayer;
var vent = ShipStatus.Instance.AllVents.FirstOrDefault(v => v.Id == ventId); var vent = ShipStatus.Instance.AllVents.FirstOrDefault(v => v.Id == id);
var @event = new EnterVentEvent(pc, vent); var @event = new EnterVentEvent(pc, vent);
MiraEventManager.InvokeEvent(@event); MiraEventManager.InvokeEvent(@event);
@@ -40,10 +40,10 @@ public static class VentEventPatches
[HarmonyPrefix] [HarmonyPrefix]
[HarmonyPatch(typeof(PlayerPhysics), nameof(PlayerPhysics.RpcExitVent))] [HarmonyPatch(typeof(PlayerPhysics), nameof(PlayerPhysics.RpcExitVent))]
public static bool PlayerPhysicsRpcExitVentPrefix(PlayerPhysics __instance, int ventId) public static bool PlayerPhysicsRpcExitVentPrefix(PlayerPhysics __instance, int id)
{ {
var pc = __instance.myPlayer; var pc = __instance.myPlayer;
var vent = ShipStatus.Instance.AllVents.First(v => v.Id == ventId); var vent = ShipStatus.Instance.AllVents.First(v => v.Id == id);
var @event = new ExitVentEvent(pc, vent); var @event = new ExitVentEvent(pc, vent);
MiraEventManager.InvokeEvent(@event); MiraEventManager.InvokeEvent(@event);
@@ -22,4 +22,15 @@ 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;
}
} }
+11 -39
View File
@@ -1,17 +1,15 @@
using System; using System;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using AmongUs.GameOptions; using AmongUs.GameOptions;
using HarmonyLib; using HarmonyLib;
using MiraAPI.Modifiers; using MiraAPI.Modifiers;
using MiraAPI.Modifiers.Types;
using MiraAPI.PluginLoading; using MiraAPI.PluginLoading;
using MiraAPI.Roles; using MiraAPI.Roles;
using MiraAPI.Utilities; 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;
@@ -21,6 +19,7 @@ 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)
@@ -33,11 +32,6 @@ 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,
@@ -73,9 +67,9 @@ internal static class TaskAdderPatches
modifiersFolder.FolderName = "Modifiers"; modifiersFolder.FolderName = "Modifiers";
modifiersFolder.name = "ModifiersFolder"; modifiersFolder.name = "ModifiersFolder";
foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins) foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins())
{ {
if (plugin.InternalRoles.Any(x => x.Value is ICustomRole { Configuration.ShowInFreeplay: true })) if (plugin.CustomRoles.Any(x => x.Value is ICustomRole { Configuration.ShowInFreeplay: true }))
{ {
var newFolder = Object.Instantiate(__instance.RootFolderPrefab, _scroller.Inner); var newFolder = Object.Instantiate(__instance.RootFolderPrefab, _scroller.Inner);
newFolder.name = plugin.PluginId+":Roles"; newFolder.name = plugin.PluginId+":Roles";
@@ -84,7 +78,7 @@ internal static class TaskAdderPatches
rolesFolder.SubFolders.Add(newFolder); rolesFolder.SubFolders.Add(newFolder);
} }
if (plugin.InternalModifiers.Exists(x => x.ShowInFreeplay)) if (plugin.Modifiers.Exists(x => x.ShowInFreeplay))
{ {
var newFolder = Object.Instantiate(__instance.RootFolderPrefab, _scroller.Inner); var newFolder = Object.Instantiate(__instance.RootFolderPrefab, _scroller.Inner);
newFolder.name = plugin.PluginId+":Modifiers"; newFolder.name = plugin.PluginId+":Modifiers";
@@ -301,7 +295,7 @@ internal static class TaskAdderPatches
if (_scroller) if (_scroller)
{ {
_scroller.CalculateAndSetYBounds(__instance.ActiveItems.Count, 6, 4.5f, 1.65f); _scroller.CalculateAndSetYBounds(__instance.ActiveItems.Count, 6, 5, 1.4f);
_scroller.SetYBoundsMin(0.0f); _scroller.SetYBoundsMin(0.0f);
_scroller.ScrollToTop(); _scroller.ScrollToTop();
} }
@@ -323,9 +317,9 @@ internal static class TaskAdderPatches
ref float num2, ref float num2,
ref float num3) ref float num3)
{ {
for (var m = 0; m < plugin.InternalModifiers.Count; m++) for (var m = 0; m < plugin.Modifiers.Count; m++)
{ {
var modifier = plugin.InternalModifiers[m]; var modifier = plugin.Modifiers[m];
if (!modifier.ShowInFreeplay) if (!modifier.ShowInFreeplay)
{ {
continue; continue;
@@ -337,10 +331,8 @@ internal static class TaskAdderPatches
taskAddButton.MyTask = null; taskAddButton.MyTask = null;
taskAddButton.SafePositionWorld = instance.SafePositionWorld; taskAddButton.SafePositionWorld = instance.SafePositionWorld;
taskAddButton.Text.text = modifier.ModifierName; taskAddButton.Text.text = modifier.ModifierName;
taskAddButton.Text.fontSizeMin = 1;
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)(() =>
{ {
@@ -357,12 +349,6 @@ internal static class TaskAdderPatches
} }
})); }));
if (modifier is TimedModifier timed)
{
taskAddButton.FileImage.sprite = MiraAssets.TimedModifierFile.LoadAsset();
taskAddButton.Text.text += $"({timed.Duration}s)";
}
instance.AddFileAsChildCustom(taskAddButton, ref num, ref num2, ref num3); instance.AddFileAsChildCustom(taskAddButton, ref num, ref num2, ref num3);
ControllerManager.Instance.AddSelectableUiElement(taskAddButton.Button); ControllerManager.Instance.AddSelectableUiElement(taskAddButton.Button);
@@ -382,9 +368,9 @@ internal static class TaskAdderPatches
private static void PluginRoles(TaskAdderGame instance, MiraPluginInfo plugin, ref float num, ref float num2, ref float num3) private static void PluginRoles(TaskAdderGame instance, MiraPluginInfo plugin, ref float num, ref float num2, ref float num3)
{ {
for (var m = 0; m < plugin.InternalRoles.Count; m++) for (var m = 0; m < plugin.CustomRoles.Count; m++)
{ {
var roleBehaviour = plugin.InternalRoles.ElementAt(m).Value; var roleBehaviour = plugin.CustomRoles.ElementAt(m).Value;
if (roleBehaviour.Role == RoleTypes.ImpostorGhost || roleBehaviour.Role == RoleTypes.CrewmateGhost || if (roleBehaviour.Role == RoleTypes.ImpostorGhost || roleBehaviour.Role == RoleTypes.CrewmateGhost ||
roleBehaviour is ICustomRole { Configuration.ShowInFreeplay: false }) roleBehaviour is ICustomRole { Configuration.ShowInFreeplay: false })
{ {
@@ -398,20 +384,6 @@ 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;
-2
View File
@@ -1,2 +0,0 @@
[*.cs]
dotnet_diagnostic.S125.severity = none
+23 -16
View File
@@ -1,30 +1,37 @@
using HarmonyLib; using HarmonyLib;
using MiraAPI.Hud; using MiraAPI.Hud;
using Reactor.Utilities;
namespace MiraAPI.Patches.Hud; namespace MiraAPI.Patches.Hud;
[HarmonyPatch(typeof(MeetingHud), nameof(MeetingHud.Start))] /// <summary>
[HarmonyPatch(typeof(ExileController), nameof(ExileController.ReEnableGameplay))] /// Reset button patches.
/// </summary>
[HarmonyPatch]
public static class ButtonResetPatches public static class ButtonResetPatches
{ {
public static void Postfix() /// <summary>
/// Resets the cooldown and effect of all custom buttons when the meeting starts.
/// </summary>
[HarmonyPostfix]
[HarmonyPatch(typeof(MeetingHud), nameof(MeetingHud.Start))]
public static void MeetingHudStartPostfix()
{ {
foreach (var customActionButton in CustomButtonManager.CustomButtons) foreach (var customActionButton in CustomButtonManager.CustomButtons)
{ {
try customActionButton.ResetCooldownAndOrEffect();
{ }
customActionButton.ResetCooldownAndOrEffect(); }
}
catch (System.Exception ex)
{
Logger<MiraApiPlugin>.Error($"Error resetting cooldown and effect for button {customActionButton.GetType().Name}: {ex}");
}
if (customActionButton.UsesMode == ButtonUsesMode.PerRound) /// <summary>
{ /// Resets the cooldown and effect of all custom buttons after the exile screen is closed.
customActionButton.SetUses(customActionButton.MaxUses); /// </summary>
} [HarmonyPostfix]
[HarmonyPatch(typeof(ExileController), nameof(ExileController.ReEnableGameplay))]
public static void ExileControllerReEnableGameplayPostfix()
{
foreach (var customActionButton in CustomButtonManager.CustomButtons)
{
customActionButton.ResetCooldownAndOrEffect();
} }
} }
} }
+4 -86
View File
@@ -1,14 +1,6 @@
using System.Collections.Generic; using HarmonyLib;
using System.Linq;
using HarmonyLib;
using MiraAPI.Hud; using MiraAPI.Hud;
using MiraAPI.Keybinds;
using MiraAPI.LocalSettings;
using MiraAPI.Utilities;
using Reactor.Utilities;
using Reactor.Utilities.Extensions; using Reactor.Utilities.Extensions;
using Rewired;
using TMPro;
using UnityEngine; using UnityEngine;
using Object = UnityEngine.Object; using Object = UnityEngine.Object;
@@ -25,8 +17,6 @@ public static class HudManagerPatches
public static Transform? BottomRight { get; private set; } public static Transform? BottomRight { get; private set; }
public static Transform? Buttons { get; private set; } public static Transform? Buttons { get; private set; }
private static Dictionary<TextMeshPro, int> vanillaKeybindIcons = new();
/* /*
/// <summary> /// <summary>
/// Trigger hudstart on current custom gamemode /// Trigger hudstart on current custom gamemode
@@ -87,47 +77,13 @@ public static class HudManagerPatches
continue; continue;
} }
try button.CreateButton(location);
{
button.CreateButton(location);
}
catch (System.Exception e)
{
Logger<MiraApiPlugin>.Error($"Failed to create custom button {button.GetType().Name}: {e}");
}
} }
gridArrange.Start(); gridArrange.Start();
gridArrange.ArrangeChilds(); gridArrange.ArrangeChilds();
aspectPosition.AdjustPosition(); aspectPosition.AdjustPosition();
vanillaKeybindIcons = [];
var keybindIconPos = new Vector3(-0.4f, 0.4f, -9.5f);
var vanillaButtons = new Dictionary<GameObject, int>
{
{ __instance.KillButton.gameObject, 8 },
{ __instance.UseButton.gameObject, 6 },
{ __instance.ReportButton.gameObject, 7 },
{ __instance.ImpostorVentButton.gameObject, 50 },
{ __instance.SabotageButton.gameObject, 4 },
{ __instance.AbilityButton.gameObject, 49 },
};
foreach (var kvp in vanillaButtons)
{
var buttonObj = kvp.Key;
var actionId = kvp.Value;
var key = KeybindUtils.GetKeycodeByActionId(actionId);
if (key == KeyboardKeyCode.None)
{
continue;
}
var icon = Helpers.CreateKeybindIcon(buttonObj, key, keybindIconPos);
vanillaKeybindIcons.Add(icon.transform.GetChild(0).GetComponent<TextMeshPro>(), actionId);
var comp = buttonObj.GetComponent<ActionButton>();
KeybindManager.VanillaKeybinds[comp.GetType()].Button = comp;
}
} }
/// <summary> /// <summary>
@@ -149,45 +105,7 @@ public static class HudManagerPatches
foreach (var button in CustomButtonManager.CustomButtons) foreach (var button in CustomButtonManager.CustomButtons)
{ {
try button.SetActive(isActive, role);
{
button.SetActive(isActive, role);
}
catch (System.Exception e)
{
Logger<MiraApiPlugin>.Error($"Failed to set custom button {button.GetType().Name} active: {e}");
}
}
}
[HarmonyPatch(nameof(HudManager.Update))]
[HarmonyPostfix]
public static void UpdatePostfix()
{
foreach (var btnIcon in vanillaKeybindIcons)
{
btnIcon.Key.text = KeybindUtils.GetKeycodeByActionId(btnIcon.Value).ToString();
btnIcon.Key.transform.parent.gameObject.SetActive(ActiveInputManager.currentControlType == ActiveInputManager.InputType.Keyboard &&
LocalSettingsTabSingleton<MiraApiSettings>.Instance.ShowKeybinds.Value);
}
var player = ReInput.players.GetPlayer(0);
foreach (var entry in KeybindManager.Keybinds)
{
var keyboard = player.controllers.Keyboard;
bool modifierKeysPressed = entry.ModifierKeys.All(k => keyboard.GetModifierKey(k)) ||
entry.ModifierKeys.Length <= 0;
if (player.GetButtonDown(entry.Id) && modifierKeysPressed)
{
entry.Invoke();
}
}
foreach (var entry in KeybindManager.VanillaKeybinds.Values)
{
if (player.GetButtonDown(entry.Id))
{
entry.Invoke();
}
} }
} }
} }
-15
View File
@@ -29,16 +29,6 @@ public static class IntroCutscenePatches
MiraEventManager.InvokeEvent(@event); MiraEventManager.InvokeEvent(@event);
} }
[HarmonyPostfix]
[HarmonyPatch(typeof(IntroCutscene._ShowRole_d__41), nameof(IntroCutscene._ShowRole_d__41.MoveNext))]
[HarmonyPriority(Priority.Last)]
public static void ShowRolePatch(IntroCutscene._ShowRole_d__41 __instance)
{
var realInstance = __instance.__4__this;
var @event = new IntroRoleRevealEvent(realInstance);
MiraEventManager.InvokeEvent(@event);
}
[HarmonyPrefix] [HarmonyPrefix]
[HarmonyPatch(nameof(IntroCutscene.BeginImpostor))] [HarmonyPatch(nameof(IntroCutscene.BeginImpostor))]
[HarmonyPatch(nameof(IntroCutscene.BeginCrewmate))] [HarmonyPatch(nameof(IntroCutscene.BeginCrewmate))]
@@ -71,11 +61,6 @@ public static class IntroCutscenePatches
public static void GameBeginPatch(IntroCutscene __instance) public static void GameBeginPatch(IntroCutscene __instance)
{ {
MiraEventManager.InvokeEvent(new IntroEndEvent(__instance)); 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));
} }
} }
@@ -1,67 +0,0 @@
using System.Linq;
using HarmonyLib;
using MiraAPI.Keybinds;
using MiraAPI.LocalSettings;
using Reactor.Utilities;
using Reactor.Utilities.Extensions;
using Rewired.UI.ControlMapper;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
namespace MiraAPI.Patches.Keybinds;
[HarmonyPatch(typeof(ControlMapper))]
public static class ControlMapperPatches
{
[HarmonyPrefix]
[HarmonyPatch(nameof(ControlMapper.Start))]
private static void StartPrefix(ControlMapper __instance)
{
var doneButton = GameObject.Find("DoneButton")?.GetComponent<CustomButton>();
if (doneButton == null)
{
return;
}
var toggle = Object.Instantiate(doneButton, doneButton.transform.parent);
var text = toggle.GetComponentInChildren<TextMeshProUGUI>();
var entry = LocalSettingsTabSingleton<MiraApiSettings>.Instance.ShowKeybinds;
toggle.gameObject.name = "KeybindsVisibleToggle";
text.text = $"Show Keybinds: {(entry.Value ? "On" : "Off")}";
toggle.onClick.RemoveAllListeners();
toggle.GetComponent<ButtonInfo>().identifier = "KeybindsVisibleToggle";
toggle.onClick.AddListener((UnityAction)(() =>
{
entry.Value = !entry.Value;
text.text = $"Show Keybinds: {(entry.Value ? "On" : "Off")}";
}));
}
[HarmonyPrefix]
[HarmonyPatch(nameof(ControlMapper.Update))]
private static void UpdatePrefix(ControlMapper __instance)
{
foreach (var element in __instance.themedElements)
{
var info = element.GetComponent<InputFieldInfo>();
if (info == null)
{
continue;
}
var key = info.glyphOrText?.actionElementMap?.keyboardKeyCode;
if (key == null)
{
continue;
}
var conflicts = KeybindManager.GetConflicts();
if (conflicts.Any(x => x.Key.ToString() == key.ToString()))
{
element.GetComponent<Image>().color = Color.red;
}
}
}
}
-36
View File
@@ -1,36 +0,0 @@
using HarmonyLib;
using MiraAPI.Keybinds;
using MonoMod.Utils;
using Rewired;
namespace MiraAPI.Patches.Keybinds;
[HarmonyPatch(typeof(InputManager_Base), nameof(InputManager_Base.Awake))]
public static class KeybindMenuPatch
{
private static bool _registered;
[HarmonyPrefix]
private static void AwakePrefix(InputManager_Base __instance)
{
if (_registered)
{
return;
}
KeybindUtils.RewiredInputManager = __instance;
KeybindManager.RewiredInit();
_registered = true;
// Set these here right after input manager initializes
KeybindManager.VanillaKeybinds = new()
{
{ typeof(KillButton), new VanillaKeybind(8) },
{ typeof(UseButton), new VanillaKeybind(6) },
{ typeof(ReportButton), new VanillaKeybind(7) },
{ typeof(VentButton), new VanillaKeybind(50) },
{ typeof(SabotageButton), new VanillaKeybind(4) },
{ typeof(AbilityButton), new VanillaKeybind(49) },
};
}
}
@@ -1,40 +0,0 @@
using HarmonyLib;
using MiraAPI.Keybinds;
using UnityEngine;
namespace MiraAPI.Patches.Keybinds;
[HarmonyPatch(typeof(OptionsMenuBehaviour))]
public static class OptionsMenuBehaviourPatch
{
private static ButtonRolloverHandler? _remap_rollover;
private static SpriteRenderer? _remap_background;
private static bool _conflicts => KeybindManager.GetConflicts().Count > 0;
[HarmonyPostfix]
[HarmonyPatch(nameof(OptionsMenuBehaviour.Open))]
private static void OpenPostfix(OptionsMenuBehaviour __instance)
{
_remap_rollover = GameObject.Find("Remap Controls")?.GetComponent<ButtonRolloverHandler>()!;
_remap_background = _remap_rollover.transform.FindChild("Background").GetComponent<SpriteRenderer>();
_remap_background.color = _conflicts ? Color.red : Color.white;
}
[HarmonyPostfix]
[HarmonyPatch(nameof(OptionsMenuBehaviour.Update))]
private static void UpdatePostfix(OptionsMenuBehaviour __instance)
{
if (_remap_rollover == null)
{
return;
}
if (_remap_background == null)
{
return;
}
_remap_rollover.OutColor = _conflicts ? Color.red : Color.white;
_remap_rollover.UnselectedColor = _conflicts ? Color.red : Color.white;
_remap_rollover.OverColor = _conflicts ? new Color32(255, 55, 55, 255) : Palette.AcceptedGreen;
}
}
@@ -1,232 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using HarmonyLib;
using MiraAPI.LocalSettings;
using MiraAPI.Utilities.Assets;
using Reactor.Utilities;
using Reactor.Utilities.Extensions;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
namespace MiraAPI.Patches.LocalSettings;
[HarmonyPatch(typeof(OptionsMenuBehaviour))]
public static class OptionsMenuPatches
{
internal static OptionsMenuBehaviour? Instance { get; private set; }
internal static BoxCollider2D MaskCollider;
private static SpriteRenderer? background;
private static int currentPage = 1;
private static Dictionary<int, List<GameObject>> tabButtons = new();
/// <summary>
/// Creates the tabs and their content
/// </summary>
[HarmonyPostfix]
[HarmonyPatch(nameof(OptionsMenuBehaviour.Start))]
public static void StartPostfix(OptionsMenuBehaviour __instance)
{
Instance = __instance;
// Fix for tabs not being clickable in the main menu
if (!AmongUsClient.Instance.IsInGame)
{
__instance.Background.GetComponent<BoxCollider2D>().enabled = false;
__instance.transform.FindChild("Tint").SetLocalZ(5.6f);
}
background = __instance.Background;
var maskObj = new GameObject
{
layer = 5,
name = "SpriteMask",
};
maskObj.transform.SetParent(__instance.transform);
maskObj.transform.localPosition = new Vector3(0, -0.3f, 0);
maskObj.transform.localScale = new Vector3(500, 120, 1);
var blank = Sprite.Create(
Texture2D.whiteTexture,
new Rect(0, 0, Texture2D.whiteTexture.width, Texture2D.whiteTexture.height),
new Vector2(0.5f, 0.5f));
var mask = maskObj.AddComponent<SpriteMask>();
mask.sprite = blank;
mask.isCustomRangeActive = true;
mask.sortingLayerName = "Default";
mask.backSortingOrder = 100;
mask.frontSortingOrder = 500;
MaskCollider = maskObj.AddComponent<BoxCollider2D>();
MaskCollider.size = new Vector2(0.01f, 0.1f);
MaskCollider.isTrigger = true;
MaskCollider.enabled = true;
currentPage = 1;
tabButtons.Clear();
float yOffset = 0;
int i = 0;
int tabIdx = 0;
int page = 1;
foreach (var settings in LocalSettingsManager.Tabs)
{
var tab = settings.CreateTab(__instance);
if (settings.ShouldCreateButton)
{
var button = settings.CreateTabButton(__instance, ref tabIdx, ref yOffset);
button.GetComponent<TabGroup>().Content = tab;
button.SetActive(page == currentPage);
if (!tabButtons.TryGetValue(page, out var list))
{
tabButtons.Add(page, new());
list = tabButtons[page];
}
list.Add(button);
yOffset += 0.6f;
tabIdx++;
i++;
}
if (i > 7)
{
i = 0;
yOffset = 0;
page++;
}
}
void ChangePage(int increase)
{
currentPage += increase;
if (currentPage > tabButtons.Count)
{
currentPage = 1;
}
if (currentPage < 1)
{
currentPage = tabButtons.Count;
}
}
// Create page buttons
// TODO: In-game menu has no close button. I tried making one from scratch but failed. Add one later plz
if (tabButtons.Count <= 1)
{
return;
}
var nextButton = Object.Instantiate(__instance.BackButton, __instance.transform).GetComponent<PassiveButton>();
nextButton.transform.localPosition = new Vector3(3.2f, 2.6f, -10);
nextButton.transform.localScale = new Vector2(0.25f, 0.25f);
var nextRend = nextButton.GetComponent<SpriteRenderer>();
nextRend.sprite = MiraAssets.NextButton.LoadAsset();
nextButton.gameObject.GetComponent<CloseButtonConsoleBehaviour>().DestroyImmediate();
var nextRollover = nextButton.gameObject.AddComponent<ButtonRolloverHandler>();
nextRollover.Target = nextRend;
nextRollover.OverColor = Palette.AcceptedGreen;
nextButton.OnClick = new Button.ButtonClickedEvent();
nextButton.OnClick.AddListener(
(UnityAction)(() =>
{
ChangePage(1);
UpdatePages();
}));
var backButton = Object.Instantiate(__instance.BackButton, __instance.transform).GetComponent<PassiveButton>();
backButton.transform.localPosition = new Vector3(2.9f, 2.6f, -10);
backButton.transform.localScale = new Vector2(0.25f, 0.25f);
var backRend = backButton.GetComponent<SpriteRenderer>();
backRend.sprite = MiraAssets.NextButton.LoadAsset();
backRend.flipX = true;
backButton.gameObject.GetComponent<CloseButtonConsoleBehaviour>().DestroyImmediate();
var backRollover = backButton.gameObject.AddComponent<ButtonRolloverHandler>();
backRollover.Target = backRend;
backRollover.OverColor = Palette.AcceptedGreen;
backButton.OnClick = new Button.ButtonClickedEvent();
backButton.OnClick.AddListener(
(UnityAction)(() =>
{
ChangePage(-1);
UpdatePages();
}));
}
/// <summary>
/// Modifies how opening tabs is handled for the custom ones to work.
/// </summary>
[HarmonyPrefix]
[HarmonyPatch(nameof(OptionsMenuBehaviour.OpenTabGroup))]
public static bool OpenTabGroupPrefix(OptionsMenuBehaviour __instance, ref int index)
{
__instance.Tabs.ToList().ForEach(x => x.Close()); // Close all vanilla tabs
LocalSettingsManager.Tabs.ForEach(CustomClose); // Close all mod tabs
// Tabs with index 10 and above are the mod settings tabs
if (index >= 10)
{
CustomOpen(LocalSettingsManager.Tabs[index - 10]);
return false;
}
return true;
}
/// <summary>
/// Makes the custom tabs close when opening the options menu and resets pages.
/// </summary>
[HarmonyPostfix]
[HarmonyPatch(nameof(OptionsMenuBehaviour.Open))]
public static void OpenPostfix(OptionsMenuBehaviour __instance)
{
LocalSettingsManager.Tabs.ForEach(CustomClose);
currentPage = 1;
UpdatePages();
}
private static void UpdatePages()
{
foreach (var pages in tabButtons)
{
pages.Value?
.Where(x => x != null)
.Do(
x => x?.SetActive(
pages.Key == currentPage));
}
}
private static void CustomOpen(LocalSettingsTab tab)
{
if (tab.TabButton?.Button)
tab.TabButton!.Button.color = tab.TabAppearance.TabButtonHoverColor;
if (tab.TabButton?.Rollover)
tab.TabButton!.Rollover.OutColor = tab.TabAppearance.TabButtonHoverColor;
if (tab.TabButton?.Content)
tab.TabButton!.Content.SetActive(true);
if (background != null)
background.color = tab.TabAppearance.TabColor;
}
private static void CustomClose(LocalSettingsTab tab)
{
if (tab.TabButton?.Button)
tab.TabButton!.Button.color = tab.TabAppearance.TabButtonColor;
if (tab.TabButton?.Rollover)
tab.TabButton!.Rollover.OutColor = tab.TabAppearance.TabButtonColor;
if (tab.TabButton?.Content)
tab.TabButton!.Content.SetActive(false);
if (background != null)
background.color = Color.white;
}
}
@@ -1,37 +0,0 @@
using HarmonyLib;
using UnityEngine;
namespace MiraAPI.Patches.LocalSettings;
[HarmonyPatch(typeof(ToggleButtonBehaviour))]
public static class ToggleBehaviourPatch
{
/// <summary>
/// ResetText was inlined ofc.
/// Skill issue
/// </summary>
[HarmonyPrefix]
[HarmonyPatch(nameof(ToggleButtonBehaviour.UpdateText))]
public static bool UpdateTextPrefix(ToggleButtonBehaviour __instance, bool on)
{
var color = on ? new Color(0f, 1f, 0.16470589f, 1f) : Color.white;
__instance.onState = on;
__instance.Background.color = color;
// Used to mask the text
__instance.Text.richText = true;
__instance.Text.text = $"<font=\"LiberationSans SDF\" material=\"LiberationSans SDF - Chat Message Masked\">" +
$"{DestroyableSingleton<TranslationController>.Instance.GetString(__instance.BaseText)}: <b>" +
$"{DestroyableSingleton<TranslationController>.Instance.GetString(__instance.onState ? StringNames.SettingsOn : StringNames.SettingsOff)}</b></font>";
__instance.Text.sortingOrder = 151;
__instance.Text.renderer.sortingLayerName = "Default";
__instance.Text.renderer.sortingOrder = 151;
if (__instance.Rollover)
{
__instance.Rollover.ChangeOutColor(color);
}
return false;
}
}
+2 -2
View File
@@ -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} ({currentPage + 1}/{sortedHats.Count})\nPress Ctrl or Tab to cycle pages"; text.text = $"{groupName}\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;
@@ -114,7 +114,7 @@ public static class HatsTabPatches
yield return null; yield return null;
} }
__instance.SetScrollerBounds(); __instance.scroller.ContentYBounds.max = -(__instance.YStart - (hatIndex + 1) / __instance.NumPerRow * __instance.YOffset) - 3f;
yield return new WaitForSeconds(0.01f); yield return new WaitForSeconds(0.01f);
} }
__instance.currentHatIsEquipped = true; __instance.currentHatIsEquipped = true;
+2 -2
View File
@@ -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} ({currentPage + 1}/{SortedNameplates.Count})\nPress Ctrl or Tab to cycle pages"; text.text = $"{groupName}\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;
@@ -91,7 +91,7 @@ public static class NameplatesTabPatches
hatIndex += 1; hatIndex += 1;
} }
__instance.SetScrollerBounds(); __instance.scroller.ContentYBounds.max = -(__instance.YStart - (hatIndex + 1) / __instance.NumPerRow * __instance.YOffset) - 3f;
__instance.currentNameplateIsEquipped = true; __instance.currentNameplateIsEquipped = true;
} }
+2 -2
View File
@@ -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} ({currentPage + 1}/{sortedSkins.Count})\nPress Ctrl or Tab to cycle pages"; text.text = $"{groupName}\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;
@@ -94,7 +94,7 @@ public static class SkinsTabPatches
hatIndex += 1; hatIndex += 1;
} }
__instance.SetScrollerBounds(); __instance.scroller.ContentYBounds.max = -(__instance.YStart - (hatIndex + 1) / __instance.NumPerRow * __instance.YOffset) - 3f;
__instance.currentSkinIsEquipped = true; __instance.currentSkinIsEquipped = true;
} }
+2 -2
View File
@@ -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} ({currentPage + 1}/{SortedVisors.Count})\nPress Ctrl or Tab to cycle pages"; text.text = $"{groupName}\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;
@@ -90,7 +90,7 @@ public static class VisorsTabPatches
hatIndex += 1; hatIndex += 1;
} }
__instance.SetScrollerBounds(); __instance.scroller.ContentYBounds.max = -(__instance.YStart - (hatIndex + 1) / __instance.NumPerRow * __instance.YOffset) - 3f;
__instance.currentVisorIsEquipped = true; __instance.currentVisorIsEquipped = true;
} }
@@ -14,12 +14,18 @@ using Object = UnityEngine.Object;
namespace MiraAPI.Patches.Options; namespace MiraAPI.Patches.Options;
/// <summary>
/// Patches the GameOptionsMenu to add custom options.
/// </summary>
[HarmonyPatch(typeof(GameOptionsMenu))] [HarmonyPatch(typeof(GameOptionsMenu))]
internal static class GameOptionsMenuPatch public static class GameOptionsMenuPatch
{ {
/// <summary>
/// Update patch for the GameOptionsMenu.
/// </summary>
/// <param name="__instance">The GameOptionsMenu instance.</param>
[HarmonyPostfix] [HarmonyPostfix]
[HarmonyPatch(nameof(GameOptionsMenu.Update))] [HarmonyPatch(nameof(GameOptionsMenu.Update))]
// ReSharper disable once InconsistentNaming
public static void UpdatePatch(GameOptionsMenu __instance) public static void UpdatePatch(GameOptionsMenu __instance)
{ {
if (GameSettingMenuPatches.SelectedModIdx == 0) if (GameSettingMenuPatches.SelectedModIdx == 0)
@@ -35,7 +41,7 @@ internal static class GameOptionsMenuPatch
else else
{ {
var filteredGroups = var filteredGroups =
GameSettingMenuPatches.SelectedMod?.InternalOptionGroups GameSettingMenuPatches.SelectedMod?.OptionGroups
.Where(x => x.OptionableType == null && !x.ShowInModifiersMenu) ?? []; .Where(x => x.OptionableType == null && !x.ShowInModifiersMenu) ?? [];
foreach (var group in filteredGroups) foreach (var group in filteredGroups)
@@ -101,7 +107,7 @@ internal static class GameOptionsMenuPatch
private static void ModifiersUpdate(ref float num) private static void ModifiersUpdate(ref float num)
{ {
var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups var groups = GameSettingMenuPatches.SelectedMod?.OptionGroups
.Where(x => x.ShowInModifiersMenu || x.OptionableType?.IsAssignableTo(typeof(BaseModifier))==true) ?? []; .Where(x => x.ShowInModifiersMenu || x.OptionableType?.IsAssignableTo(typeof(BaseModifier))==true) ?? [];
foreach (var modGroup in groups) foreach (var modGroup in groups)
@@ -112,7 +118,7 @@ internal static class GameOptionsMenuPatch
private static void ModifiersCreate(GameOptionsMenu menu) private static void ModifiersCreate(GameOptionsMenu menu)
{ {
var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups var groups = GameSettingMenuPatches.SelectedMod?.OptionGroups
.Where(x => x.ShowInModifiersMenu || x.OptionableType?.IsAssignableTo(typeof(BaseModifier))==true) ?? []; .Where(x => x.ShowInModifiersMenu || x.OptionableType?.IsAssignableTo(typeof(BaseModifier))==true) ?? [];
foreach (var group in groups) foreach (var group in groups)
{ {
@@ -122,7 +128,6 @@ internal static class GameOptionsMenuPatch
[HarmonyPrefix] [HarmonyPrefix]
[HarmonyPatch(nameof(GameOptionsMenu.CreateSettings))] [HarmonyPatch(nameof(GameOptionsMenu.CreateSettings))]
// ReSharper disable once InconsistentNaming
public static bool SettingsPatch(GameOptionsMenu __instance) public static bool SettingsPatch(GameOptionsMenu __instance)
{ {
if (GameSettingMenuPatches.SelectedModIdx == 0) if (GameSettingMenuPatches.SelectedModIdx == 0)
@@ -138,7 +143,7 @@ internal static class GameOptionsMenuPatch
return false; return false;
} }
var filteredGroups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups var filteredGroups = GameSettingMenuPatches.SelectedMod?.OptionGroups
.Where(x => x.OptionableType == null && !x.ShowInModifiersMenu) ?? []; .Where(x => x.OptionableType == null && !x.ShowInModifiersMenu) ?? [];
foreach (var group in filteredGroups) foreach (var group in filteredGroups)
@@ -151,7 +156,6 @@ internal static class GameOptionsMenuPatch
[HarmonyPrefix] [HarmonyPrefix]
[HarmonyPatch(nameof(GameOptionsMenu.Initialize))] [HarmonyPatch(nameof(GameOptionsMenu.Initialize))]
// ReSharper disable once InconsistentNaming
public static bool InitPatch(GameOptionsMenu __instance) public static bool InitPatch(GameOptionsMenu __instance)
{ {
if (__instance.Children != null && __instance.Children.Count != 0) if (__instance.Children != null && __instance.Children.Count != 0)
@@ -220,7 +224,6 @@ internal static class GameOptionsMenuPatch
menu.checkboxOrigin, menu.checkboxOrigin,
menu.numberOptionOrigin, menu.numberOptionOrigin,
menu.stringOptionOrigin, menu.stringOptionOrigin,
menu.playerOptionOrigin,
menu.settingsContainer)); menu.settingsContainer));
foreach (var newOpt in options) foreach (var newOpt in options)
Loaded 100 of 140 files, more files were not shown because too many files have changed in this diff. Show more