mirror of
https://github.com/All-Of-Us-Mods/MiraAPI.git
synced 2026-10-20 01:46:15 +00:00
Compare commits
35
Commits
0.3.4
...
runtime-options
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cff46ebd19 | ||
|
|
9270de408d | ||
|
|
8940de76bb | ||
|
|
1813fe5a84 | ||
|
|
272b14f0e0 | ||
|
|
c29e882d7e | ||
|
|
8774c9da70 | ||
|
|
51c7e248f9 | ||
|
|
02f9c20804 | ||
|
|
7f506000a4 | ||
|
|
795b6a81d7 | ||
|
|
bd8d7a3208 | ||
|
|
090db766b2 | ||
|
|
1f0027108e | ||
|
|
665724010d | ||
|
|
2d58c91289 | ||
|
|
b3ed6d67f9 | ||
|
|
64ef78e3a2 | ||
|
|
5e9e4012ef | ||
|
|
fe04a7033a | ||
|
|
ba0ee49255 | ||
|
|
97528b4380 | ||
|
|
20e51cc993 | ||
|
|
64b5e01ba1 | ||
|
|
d4469153ab | ||
|
|
ab67c233d8 | ||
|
|
c72eacc7a2 | ||
|
|
13072d6834 | ||
|
|
c6342750ef | ||
|
|
5c2e95376d | ||
|
|
067859b9fd | ||
|
|
5d01462a32 | ||
|
|
a9b6f40ed0 | ||
|
|
eae0024ec3 | ||
|
|
2907564d6a |
No files matched your search
@@ -8,7 +8,7 @@
|
||||
|
||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||
|
||||
<VersionPrefix>0.3.4</VersionPrefix>
|
||||
<VersionPrefix>0.3.8</VersionPrefix>
|
||||
<VersionSuffix>dev</VersionSuffix>
|
||||
|
||||
<Authors>All Of Us, XtraCube, Angxl</Authors>
|
||||
@@ -42,4 +42,4 @@
|
||||
<PropertyGroup>
|
||||
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)\stylecop.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using MiraAPI.Events;
|
||||
using MiraAPI.Events.Mira;
|
||||
using MiraAPI.Events.Vanilla.Gameplay;
|
||||
@@ -17,7 +18,7 @@ public static class ExampleEventHandlers
|
||||
{
|
||||
public static void Initialize()
|
||||
{
|
||||
// You can register event handlers with the MiraEventManager class.
|
||||
// You can register event handlers with the MiraEventManager class
|
||||
var handle = MiraEventManager.RegisterEventHandler<BeforeMurderEvent>(@event =>
|
||||
{
|
||||
Logger<ExamplePlugin>.Info($"{@event.Source.Data.PlayerName} is about to kill {@event.Target.Data.PlayerName}");
|
||||
@@ -37,6 +38,21 @@ public static class ExampleEventHandlers
|
||||
MiraEventManager.UnregisterEventHandler(handle);
|
||||
}
|
||||
|
||||
// If you want to check vanilla buttons, use something like this!
|
||||
[RegisterEvent]
|
||||
public static void VanillaButtonClickHandler(VanillaButtonClickEvent @event)
|
||||
{
|
||||
Logger<ExamplePlugin>.Info($"{@event.Button.buttonLabelText.text} is being used!");
|
||||
if (@event.PlayerTarget != null)
|
||||
{
|
||||
Logger<ExamplePlugin>.Info($"{@event.PlayerTarget.Data.PlayerName} is being targeted!");
|
||||
}
|
||||
else if (@event.VentTarget != null)
|
||||
{
|
||||
Logger<ExamplePlugin>.Info($"Vent with id {@event.VentTarget.Id.ToString(CultureInfo.InvariantCulture)} is being targeted!");
|
||||
}
|
||||
}
|
||||
|
||||
// If you want to add extra votes to a player, do something like this.
|
||||
[RegisterEvent]
|
||||
public static void StartMeetingEvent(StartMeetingEvent _)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace MiraAPI.Events.Vanilla.Gameplay;
|
||||
using MiraAPI.Networking;
|
||||
|
||||
namespace MiraAPI.Events.Vanilla.Gameplay;
|
||||
|
||||
/// <summary>
|
||||
/// Event that is invoked before a player is murdered. This event is cancelable.
|
||||
@@ -15,6 +17,24 @@ public sealed class BeforeMurderEvent : MiraCancelableEvent
|
||||
/// </summary>
|
||||
public PlayerControl Target { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the murder was meant to be done in a meeting via an enum.
|
||||
/// </summary>
|
||||
public MeetingCheck InMeeting { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BeforeMurderEvent"/> class.
|
||||
/// </summary>
|
||||
/// <param name="source">The player that is killing the target.</param>
|
||||
/// <param name="target">The player that is being killed.</param>
|
||||
/// <param name="inMeeting">Whether the murder is intended to be triggered in a meeting.</param>
|
||||
public BeforeMurderEvent(PlayerControl source, PlayerControl target, MeetingCheck inMeeting)
|
||||
{
|
||||
Source = source;
|
||||
Target = target;
|
||||
InMeeting = inMeeting;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BeforeMurderEvent"/> class.
|
||||
/// </summary>
|
||||
@@ -24,5 +44,6 @@ public sealed class BeforeMurderEvent : MiraCancelableEvent
|
||||
{
|
||||
Source = source;
|
||||
Target = target;
|
||||
InMeeting = MeetingCheck.Ignore;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using MiraAPI.Hud;
|
||||
|
||||
namespace MiraAPI.Events.Vanilla.Gameplay;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a Vanilla Button click is cancelled. Do not use for custom buttons.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The Vanilla Button type.</typeparam>
|
||||
public sealed class VanillaButtonCancelledEvent<T> : MiraEvent where T : AbilityButton
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets Vanilla Button whose click was cancelled.
|
||||
/// </summary>
|
||||
public T Button { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="VanillaButtonCancelledEvent{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="button">The Mira Button whose click was cancelled.</param>
|
||||
public VanillaButtonCancelledEvent(T button)
|
||||
{
|
||||
Button = button;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a Vanilla Button click is cancelled. Do not use for custom buttons.
|
||||
/// </summary>
|
||||
public sealed class VanillaButtonCancelledEvent : MiraEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets Vanilla Button whose click was cancelled.
|
||||
/// </summary>
|
||||
public AbilityButton Button { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="VanillaButtonCancelledEvent"/> class.
|
||||
/// </summary>
|
||||
/// <param name="button">The Vanilla Button whose click was cancelled.</param>
|
||||
public VanillaButtonCancelledEvent(AbilityButton button)
|
||||
{
|
||||
Button = button;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using MiraAPI.Hud;
|
||||
|
||||
namespace MiraAPI.Events.Vanilla.Gameplay;
|
||||
|
||||
/// <summary>
|
||||
/// Button click event for Vanilla Buttons only. Do not use for custom buttons.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The Vanilla Button type.</typeparam>
|
||||
public sealed class VanillaButtonClickEvent<T> : MiraCancelableEvent where T : AbilityButton
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets Vanilla Button that was clicked.
|
||||
/// </summary>
|
||||
public T Button { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the generic click event that is invoked before this button-specific event.
|
||||
/// </summary>
|
||||
public VanillaButtonClickEvent GenericClickEvent { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="VanillaButtonClickEvent{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="button">The Mira Button that was clicked.</param>
|
||||
/// <param name="genericClickEvent">The generic click event invoked before button-specific events.</param>
|
||||
public VanillaButtonClickEvent(T button, VanillaButtonClickEvent genericClickEvent)
|
||||
{
|
||||
GenericClickEvent = genericClickEvent;
|
||||
Button = button;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Button click event for Vanilla Buttons only. Do not use for custom buttons.
|
||||
/// </summary>
|
||||
public sealed class VanillaButtonClickEvent : MiraCancelableEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets Vanilla Button that was clicked.
|
||||
/// </summary>
|
||||
public AbilityButton Button { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target player, if any, of the Vanilla Button that was clicked.
|
||||
/// </summary>
|
||||
public PlayerControl? PlayerTarget { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target vent, if any, of the Vanilla Button that was clicked.
|
||||
/// </summary>
|
||||
public Vent? VentTarget { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="VanillaButtonClickEvent"/> class.
|
||||
/// </summary>
|
||||
/// <param name="button">The Vanilla Button that was clicked.</param>
|
||||
/// <param name="playerTarget">The Player target, if any.</param>
|
||||
/// <param name="ventTarget">The Vent target, if any.</param>
|
||||
public VanillaButtonClickEvent(AbilityButton button, PlayerControl? playerTarget = null, Vent? ventTarget = null)
|
||||
{
|
||||
Button = button;
|
||||
PlayerTarget = playerTarget;
|
||||
VentTarget = ventTarget;
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ public static class ModdedOptionsManager
|
||||
private static readonly Dictionary<Type, AbstractOptionGroup> TypeToGroup = [];
|
||||
|
||||
internal static readonly Dictionary<OptionBehaviour, ModdedPlayerOption> CreatedPlayerOptions = [];
|
||||
internal static readonly Dictionary<OptionBehaviour, ModdedStringOption> CreatedStringOptions = [];
|
||||
internal static readonly Dictionary<uint, IModdedOption> ModdedOptions = [];
|
||||
internal static readonly List<AbstractOptionGroup> Groups = [];
|
||||
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Il2CppInterop.Runtime.InteropTypes.Arrays;
|
||||
using MiraAPI.Networking;
|
||||
using Reactor.Localization.Utilities;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace MiraAPI.GameOptions.OptionTypes;
|
||||
|
||||
/// <summary>
|
||||
/// An option for selecting an enum value.
|
||||
/// </summary>
|
||||
public class ModdedStringOption : ModdedOption<string>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the string values of the option.
|
||||
/// </summary>
|
||||
public string[] Values { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the String Name values of the option.
|
||||
/// </summary>
|
||||
public StringNames[] StringNameValues { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the string's index
|
||||
/// </summary>
|
||||
/// <param name="value">The option's string value.</param>
|
||||
/// <returns>The option's value in the index.</returns>
|
||||
public int GetIndex(string value, int location)
|
||||
{
|
||||
if (Values.Contains(value))
|
||||
{
|
||||
return Values.ToList().IndexOf(value);
|
||||
}
|
||||
Info($"Cannot find a valid index for {value} in {location}. Returning 0 instead. Values: {string.Join(", ", Values)}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a value to the option.
|
||||
/// </summary>
|
||||
/// <param name="value">The option's string value.</param>
|
||||
/// <param name="index">The additional value's index on the list.</param>
|
||||
public void RegisterExtraValue(string value, int index = -1)
|
||||
{
|
||||
if (!Values.Contains(value))
|
||||
{
|
||||
if (index <= -1)
|
||||
index = Values.Length;
|
||||
|
||||
var newVals = Values.ToList();
|
||||
newVals.Insert(index, value);
|
||||
Values = newVals.ToArray();
|
||||
var newStrVals = StringNameValues.ToList();
|
||||
newStrVals.Insert(index, CustomStringName.CreateAndRegister(value));
|
||||
StringNameValues = newStrVals.ToArray();
|
||||
}
|
||||
|
||||
if (Data != null)
|
||||
{
|
||||
var data = (StringGameSetting)Data;
|
||||
data.Values = StringNameValues;
|
||||
|
||||
data.Index = GetIndex(Value, 1);
|
||||
if (OptionBehaviour != null)
|
||||
{
|
||||
var strOpt = OptionBehaviour.TryCast<StringOption>();
|
||||
if (strOpt != null)
|
||||
{
|
||||
strOpt.Values = StringNameValues;
|
||||
strOpt.Value = GetIndex(Value, 2);
|
||||
Warning("StringOption is valid");
|
||||
}
|
||||
else
|
||||
{
|
||||
Error("StringOption is null.");
|
||||
}
|
||||
Warning("OptionBehaviour is valid");
|
||||
}
|
||||
else
|
||||
{
|
||||
Error("OptionBehaviour is null.");
|
||||
}
|
||||
Warning("Data is valid");
|
||||
}
|
||||
else
|
||||
{
|
||||
Error("Data is null.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ModdedStringOption"/> class.
|
||||
/// </summary>
|
||||
/// <param name="title">The title of the option.</param>
|
||||
/// <param name="defaultValue">The default value as a string.</param>
|
||||
/// <param name="values">An option list of string values to use.</param>
|
||||
/// <param name="includeInPreset">Whether to include this option in the preset or not.</param>
|
||||
public ModdedStringOption(string title, string defaultValue, string[] values, bool includeInPreset=true) : base(title, defaultValue, includeInPreset)
|
||||
{
|
||||
Values = values;
|
||||
Data = ScriptableObject.CreateInstance<StringGameSetting>();
|
||||
var data = (StringGameSetting)Data;
|
||||
|
||||
data.Title = StringName;
|
||||
data.Type = global::OptionTypes.String;
|
||||
StringNameValues = values.Select(CustomStringName.CreateAndRegister).ToArray();
|
||||
data.Values = StringNameValues;
|
||||
|
||||
data.Index = GetIndex(Value, 3);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, PlayerOption playerOpt, Transform container)
|
||||
{
|
||||
var stringOption = Object.Instantiate(stringOpt, container);
|
||||
stringOption.name =
|
||||
$"{ParentMod!.OptionsTitleText}.StringOption.{TranslationController.Instance.GetString(StringName)}";
|
||||
|
||||
stringOption.SetUpFromData(Data, 20);
|
||||
stringOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged;
|
||||
|
||||
// SetUpFromData method doesnt work correctly so we must set the values manually
|
||||
stringOption.Title = StringName;
|
||||
stringOption.Values = (Data as StringGameSetting)?.Values ?? new Il2CppStructArray<StringNames>(0);
|
||||
stringOption.Value = GetIndex(Value, 4);
|
||||
|
||||
OptionBehaviour = stringOption;
|
||||
ModdedOptionsManager.CreatedStringOptions.TryAdd(stringOption, this);
|
||||
|
||||
return stringOption;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override float GetFloatData()
|
||||
{
|
||||
return Values.IndexOf(Value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override NetData GetNetData()
|
||||
{
|
||||
return new NetData(Id, Encoding.Unicode.GetBytes(Convert.ToString(Value, NumberFormatInfo.InvariantInfo)!));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void HandleNetData(byte[] data)
|
||||
{
|
||||
SetValue(Encoding.Unicode.GetString(data));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour)
|
||||
{
|
||||
return Values[optionBehaviour.GetInt()];
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnValueChanged(string newValue)
|
||||
{
|
||||
HudManager.Instance.Notifier.AddSettingsChangeMessage(StringName, Data.GetValueString(GetIndex(newValue, 5)), false);
|
||||
if (!OptionBehaviour)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (OptionBehaviour is StringOption opt)
|
||||
{
|
||||
opt.Value = GetIndex(newValue, 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,4 +69,4 @@ public class BaseKeybind
|
||||
{
|
||||
Handler -= action;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ namespace MiraAPI.Keybinds;
|
||||
public class MiraKeybind : BaseKeybind
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets name of the keybind
|
||||
/// Gets name of the keybind.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
global using static Reactor.Utilities.Logger<MiraAPI.MiraApiPlugin>;
|
||||
using System;
|
||||
using BepInEx;
|
||||
using BepInEx.Unity.IL2CPP;
|
||||
using HarmonyLib;
|
||||
@@ -21,6 +22,11 @@ namespace MiraAPI;
|
||||
[ReactorModFlags(ModFlags.RequireOnAllClients)]
|
||||
public partial class MiraApiPlugin : BasePlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the current device is running Starlight (on mobile).
|
||||
/// </summary>
|
||||
public static bool IsMobile => Constants.GetPlatformType() is Platforms.Android or Platforms.IPhone;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the branding Mira API color.
|
||||
/// </summary>
|
||||
@@ -31,6 +37,11 @@ public partial class MiraApiPlugin : BasePlugin
|
||||
/// </summary>
|
||||
public static Color DefaultHeaderColor { get; } = new Color32(77, 77, 77, 255);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the current build is a development build or not. This mostly avoids confusion for users asking why the mod appears red.
|
||||
/// </summary>
|
||||
public static bool IsDevBuild => Version.Contains("ci", StringComparison.OrdinalIgnoreCase) || Version.Contains("dev", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
private static MiraPluginManager? PluginManager { get; set; }
|
||||
internal Harmony Harmony { get; } = new(Id);
|
||||
|
||||
@@ -39,7 +50,7 @@ public partial class MiraApiPlugin : BasePlugin
|
||||
{
|
||||
Harmony.PatchAll();
|
||||
|
||||
ReactorCredits.Register("Mira API", Version, true, ReactorCredits.AlwaysShow);
|
||||
ReactorCredits.Register("Mira API", Version, IsDevBuild, ReactorCredits.AlwaysShow);
|
||||
|
||||
PluginManager = new MiraPluginManager();
|
||||
PluginManager.Initialize();
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
using BepInEx.Configuration;
|
||||
using MiraAPI.LocalSettings;
|
||||
using MiraAPI.LocalSettings.Attributes;
|
||||
using MiraAPI.Patches;
|
||||
using MiraAPI.Utilities;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI;
|
||||
|
||||
/// <summary>
|
||||
/// Mira API Config File Handler
|
||||
/// Mira API Config File Handler.
|
||||
/// </summary>
|
||||
public class MiraApiSettings(ConfigFile config) : LocalSettingsTab(config)
|
||||
{
|
||||
@@ -21,22 +24,78 @@ public class MiraApiSettings(ConfigFile config) : LocalSettingsTab(config)
|
||||
HideIconOnHover = false,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value stored for scaling buttons properly.
|
||||
/// </summary>
|
||||
public static float OldButtonScaleFactor { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Open()
|
||||
{
|
||||
base.Open();
|
||||
OldButtonScaleFactor = ButtonUIFactorSlider.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnOptionChanged(ConfigEntryBase configEntry)
|
||||
{
|
||||
base.OnOptionChanged(configEntry);
|
||||
if (configEntry == ButtonUIFactorSlider)
|
||||
{
|
||||
if (HudManager.InstanceExists)
|
||||
{
|
||||
HudManagerPatches.ResizeUI(1f / OldButtonScaleFactor);
|
||||
HudManagerPatches.ResizeUI(ButtonUIFactorSlider.Value);
|
||||
}
|
||||
OldButtonScaleFactor = ButtonUIFactorSlider.Value;
|
||||
}
|
||||
else if (configEntry == SetFpsSlider)
|
||||
{
|
||||
Application.targetFrameRate = (int)SetFpsSlider.Value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the modifiers hud should be on the left side of the screen (under roles/task tab). Recommended for streamers.
|
||||
/// </summary>
|
||||
[LocalToggleSetting]
|
||||
public ConfigEntry<bool> ModifiersHudLeftSide { get; private set; } = config.Bind("Displays", "Show Modifiers HUD on Left Side", false);
|
||||
public ConfigEntry<bool> ModifiersHudLeftSide { get; private set; } = config.Bind("Visuals/UI", "Show Modifiers HUD on Left Side", false);
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether to show keybinds on buttons.
|
||||
/// </summary>
|
||||
[LocalToggleSetting]
|
||||
public ConfigEntry<bool> ShowKeybinds { get; private set; } = config.Bind("Keybinds", "Show Keybinds on Buttons", true);
|
||||
// This would be placed in the keybinds menu, but it crashes for Epic Games users. - Atony
|
||||
public ConfigEntry<bool> ShowKeybinds { get; private set; } = config.Bind("Visuals/UI", "Show Keybinds on Buttons", true);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scale of the buttons.
|
||||
/// </summary>
|
||||
[LocalSliderSetting(min: 0.5f, max: 1.5f, suffixType: MiraNumberSuffixes.Multiplier, formatString: "0.00", displayValue: true)]
|
||||
public ConfigEntry<float> ButtonUIFactorSlider { get; private set; } =
|
||||
config.Bind("Visuals/UI", "Button Scale Factor", 0.75f);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the fps specified by the player.
|
||||
/// </summary>
|
||||
[LocalSliderSetting(min: 60f, max: 240f, suffixType: MiraNumberSuffixes.None, formatString: "0", displayValue: true, roundValue: true)]
|
||||
public ConfigEntry<float> SetFpsSlider { get; private set; } =
|
||||
config.Bind("Visuals/UI", "Max FPS", 120f);
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether to apply cosmetic changes to the TaskAdder.
|
||||
/// </summary>
|
||||
[LocalToggleSetting]
|
||||
public ConfigEntry<bool> PrettyTaskAdder { get; private set; } = config.Bind("Freeplay", "Pretty Task Laptop", true);
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether to show the red flash from sabotages.
|
||||
/// </summary>
|
||||
[LocalToggleSetting]
|
||||
public ConfigEntry<bool> EnableSabotageFlashes { get; private set; } = config.Bind("Accessibility", "Enable Sabotage Flashes", true);
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether to enable the sabotage sound effects or not.
|
||||
/// </summary>
|
||||
[LocalToggleSetting]
|
||||
public ConfigEntry<bool> EnableSabotageBlares { get; private set; } = config.Bind("Accessibility", "Enable Sabotage Blare", true);
|
||||
}
|
||||
@@ -7,10 +7,12 @@ using Assets.CoreScripts;
|
||||
using BepInEx.Unity.IL2CPP.Utils;
|
||||
using MiraAPI.Events;
|
||||
using MiraAPI.Events.Vanilla.Gameplay;
|
||||
using MiraAPI.Utilities;
|
||||
using Reactor.Networking.Attributes;
|
||||
using Reactor.Networking.Rpc;
|
||||
using Reactor.Utilities;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using Rewired;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
@@ -22,7 +24,7 @@ namespace MiraAPI.Networking;
|
||||
public static class CustomMurderRpc
|
||||
{
|
||||
/// <summary>
|
||||
/// Networked Custom Murder method.
|
||||
/// Networked Custom Murder method, ran by clients for the host to confirm kills.
|
||||
/// </summary>
|
||||
/// <param name="source">The killer.</param>
|
||||
/// <param name="target">The player to murder.</param>
|
||||
@@ -32,7 +34,6 @@ public static class CustomMurderRpc
|
||||
/// <param name="teleportMurderer">Should the killer be snapped to the dead player.</param>
|
||||
/// <param name="showKillAnim">Should the kill animation be shown.</param>
|
||||
/// <param name="playKillSound">Should the kill sound be played.</param>
|
||||
[MethodRpc((uint)MiraRpc.CustomMurder, LocalHandling = RpcLocalHandling.Before)]
|
||||
public static void RpcCustomMurder(
|
||||
this PlayerControl source,
|
||||
PlayerControl target,
|
||||
@@ -43,16 +44,106 @@ public static class CustomMurderRpc
|
||||
bool showKillAnim = true,
|
||||
bool playKillSound = true)
|
||||
{
|
||||
source.RpcCustomMurder(
|
||||
target,
|
||||
MeetingCheck.Ignore,
|
||||
didSucceed,
|
||||
resetKillTimer,
|
||||
createDeadBody,
|
||||
teleportMurderer,
|
||||
showKillAnim,
|
||||
playKillSound);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Networked Custom Murder method, which checks for meetings as well, ran by clients for the host to confirm kills.
|
||||
/// </summary>
|
||||
/// <param name="source">The killer.</param>
|
||||
/// <param name="target">The player to murder.</param>
|
||||
/// <param name="inMeeting">Whether the murder is intended to be triggered in a meeting.</param>
|
||||
/// <param name="didSucceed">Whether the murder was successful or not.</param>
|
||||
/// <param name="resetKillTimer">Should the kill timer be reset.</param>
|
||||
/// <param name="createDeadBody">Should a dead body be created.</param>
|
||||
/// <param name="teleportMurderer">Should the killer be snapped to the dead player.</param>
|
||||
/// <param name="showKillAnim">Should the kill animation be shown.</param>
|
||||
/// <param name="playKillSound">Should the kill sound be played.</param>
|
||||
[MethodRpc((uint)MiraRpc.CustomMurder, LocalHandling = RpcLocalHandling.Before)]
|
||||
public static void RpcCustomMurder(
|
||||
this PlayerControl source,
|
||||
PlayerControl target,
|
||||
MeetingCheck inMeeting,
|
||||
bool didSucceed = true,
|
||||
bool resetKillTimer = true,
|
||||
bool createDeadBody = true,
|
||||
bool teleportMurderer = true,
|
||||
bool showKillAnim = true,
|
||||
bool playKillSound = true)
|
||||
{
|
||||
if (LobbyBehaviour.Instance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var murderResultFlags = didSucceed ? MurderResultFlags.Succeeded : MurderResultFlags.FailedError;
|
||||
|
||||
var beforeMurderEvent = new BeforeMurderEvent(source, target);
|
||||
var beforeMurderEvent = new BeforeMurderEvent(source, target, inMeeting);
|
||||
MiraEventManager.InvokeEvent(beforeMurderEvent);
|
||||
var isMeetingActive = MeetingHud.Instance != null || ExileController.Instance != null;
|
||||
if ((inMeeting is MeetingCheck.ForMeeting && !isMeetingActive) || (inMeeting is MeetingCheck.OutsideMeeting && isMeetingActive))
|
||||
{
|
||||
beforeMurderEvent.Cancel();
|
||||
}
|
||||
|
||||
if (beforeMurderEvent.IsCancelled)
|
||||
{
|
||||
murderResultFlags = MurderResultFlags.FailedError;
|
||||
}
|
||||
|
||||
if (!PlayerControl.LocalPlayer.IsHost())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RpcConfirmCustomMurder(
|
||||
PlayerControl.LocalPlayer,
|
||||
source,
|
||||
target,
|
||||
murderResultFlags,
|
||||
resetKillTimer,
|
||||
createDeadBody,
|
||||
teleportMurderer,
|
||||
showKillAnim,
|
||||
playKillSound);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Networked Custom Murder method, which checks for meetings as well, ran by clients for the host to confirm kills.
|
||||
/// </summary>
|
||||
/// <param name="host">The game host.</param>
|
||||
/// <param name="source">The killer.</param>
|
||||
/// <param name="target">The player to murder.</param>
|
||||
/// <param name="murderResultFlags">End result for the murder.</param>
|
||||
/// <param name="resetKillTimer">Should the kill timer be reset.</param>
|
||||
/// <param name="createDeadBody">Should a dead body be created.</param>
|
||||
/// <param name="teleportMurderer">Should the killer be snapped to the dead player.</param>
|
||||
/// <param name="showKillAnim">Should the kill animation be shown.</param>
|
||||
/// <param name="playKillSound">Should the kill sound be played.</param>
|
||||
[MethodRpc((uint)MiraRpc.ConfirmCustomMurder, LocalHandling = RpcLocalHandling.Before)]
|
||||
public static void RpcConfirmCustomMurder(
|
||||
this PlayerControl host,
|
||||
PlayerControl source,
|
||||
PlayerControl target,
|
||||
MurderResultFlags murderResultFlags,
|
||||
bool resetKillTimer = true,
|
||||
bool createDeadBody = true,
|
||||
bool teleportMurderer = true,
|
||||
bool showKillAnim = true,
|
||||
bool playKillSound = true)
|
||||
{
|
||||
if (LobbyBehaviour.Instance || !host.IsHost() || target.Data.IsDead || target.Data.Disconnected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var murderResultFlags2 = MurderResultFlags.DecisionByHost | murderResultFlags;
|
||||
|
||||
source.CustomMurder(
|
||||
@@ -87,11 +178,10 @@ public static class CustomMurderRpc
|
||||
bool playKillSound = true)
|
||||
{
|
||||
source.isKilling = false;
|
||||
if (resultFlags.HasFlag(MurderResultFlags.FailedError))
|
||||
if (resultFlags.HasFlag(MurderResultFlags.FailedError) || LobbyBehaviour.Instance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var data = target.Data;
|
||||
|
||||
if (resultFlags.HasFlag(MurderResultFlags.FailedProtected) ||
|
||||
(resultFlags.HasFlag(MurderResultFlags.DecisionByHost) && target.protectedByGuardianId > -1))
|
||||
@@ -174,7 +264,7 @@ public static class CustomMurderRpc
|
||||
{
|
||||
try
|
||||
{
|
||||
HudManager.Instance.KillOverlay.ShowKillAnimation(source.Data, data);
|
||||
HudManager.Instance.KillOverlay.ShowKillAnimation(source.Data, target.Data);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -211,6 +301,10 @@ public static class CustomMurderRpc
|
||||
bool createDeadBody = true,
|
||||
bool teleportMurderer = true)
|
||||
{
|
||||
if (LobbyBehaviour.Instance)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
var cam = Camera.main?.GetComponent<FollowerCamera>();
|
||||
var isParticipant = source.AmOwner || target.AmOwner;
|
||||
var sourcePhys = source.MyPhysics;
|
||||
@@ -298,3 +392,24 @@ public static class CustomMurderRpc
|
||||
source.isKilling = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks for custom murders.
|
||||
/// </summary>
|
||||
public enum MeetingCheck
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks for meetings.
|
||||
/// </summary>
|
||||
ForMeeting,
|
||||
|
||||
/// <summary>
|
||||
/// Checks for a meeting to not be active.
|
||||
/// </summary>
|
||||
OutsideMeeting,
|
||||
|
||||
/// <summary>
|
||||
/// Doesn't check for a meeting at all.
|
||||
/// </summary>
|
||||
Ignore,
|
||||
}
|
||||
@@ -35,6 +35,11 @@ public enum MiraRpc : uint
|
||||
/// </summary>
|
||||
CustomMurder,
|
||||
|
||||
/// <summary>
|
||||
/// Custom Murder RPC Confirmation.
|
||||
/// </summary>
|
||||
ConfirmCustomMurder,
|
||||
|
||||
/// <summary>
|
||||
/// Custom Game Over RPC.
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
using System.Collections;
|
||||
using BepInEx.Unity.IL2CPP.Utils.Collections;
|
||||
using HarmonyLib;
|
||||
using MiraAPI.LocalSettings;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Patches.Accessibility;
|
||||
[HarmonyPatch]
|
||||
public static class HudManagerFlashPatches
|
||||
{
|
||||
[HarmonyPatch(typeof(HudManager), nameof(HudManager.StartReactorFlash))]
|
||||
[HarmonyPrefix]
|
||||
public static bool ReactorFlashPrefix(HudManager __instance)
|
||||
{
|
||||
if (__instance.ReactorFlash == null)
|
||||
{
|
||||
__instance.ReactorFlash = __instance.StartCoroutine(CoReactorFlash().WrapToIl2Cpp());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
[HarmonyPatch(typeof(HudManager), nameof(HudManager.StartOxyFlash))]
|
||||
[HarmonyPrefix]
|
||||
public static bool OxygenFlashPrefix(HudManager __instance)
|
||||
{
|
||||
if (__instance.OxyFlash == null)
|
||||
{
|
||||
__instance.OxyFlash = __instance.StartCoroutine(CoReactorFlash().WrapToIl2Cpp());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
public static IEnumerator CoReactorFlash()
|
||||
{
|
||||
if (!HudManager.InstanceExists)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
var hudManager = HudManager.Instance;
|
||||
WaitForSeconds wait = new WaitForSeconds(1f);
|
||||
bool light = false;
|
||||
|
||||
hudManager.FullScreen.color = new Color(1f, 0f, 0f, 0.37254903f);
|
||||
while (true)
|
||||
{
|
||||
var settins = LocalSettingsTabSingleton<MiraApiSettings>.Instance;
|
||||
hudManager.FullScreen.gameObject.SetActive(settins.EnableSabotageFlashes.Value && !hudManager.FullScreen.gameObject.activeSelf);
|
||||
if (settins.EnableSabotageBlares.Value)
|
||||
{
|
||||
SoundManager.Instance.PlaySound(ShipStatus.Instance.SabotageSound, false, 1f, null);
|
||||
}
|
||||
light = !light;
|
||||
|
||||
/*if (!MiraApiPlugin.IsMobile)
|
||||
{
|
||||
if (hudManager.lightFlashHandle == null)
|
||||
{
|
||||
hudManager.lightFlashHandle = DualshockLightManager.Instance.AllocateLight();
|
||||
hudManager.lightFlashHandle.color = new Color(1f, 0f, 0f, 1f);
|
||||
hudManager.lightFlashHandle.intensity = 1f;
|
||||
}
|
||||
hudManager.lightFlashHandle.color.SetAlpha(light ? 1f : 0f);
|
||||
}*/
|
||||
yield return wait;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using System.Linq;
|
||||
using AmongUs.GameOptions;
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Events;
|
||||
using MiraAPI.Events.Vanilla.Gameplay;
|
||||
using MiraAPI.Events.Vanilla.Usables;
|
||||
|
||||
namespace MiraAPI.Patches.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Patches to invoke vent related MiraEvents.
|
||||
/// </summary>
|
||||
[HarmonyPatch]
|
||||
public static class VanillaButtonPatches
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(AbilityButton), nameof(AbilityButton.DoClick))]
|
||||
public static bool DoClickPrefix(AbilityButton __instance)
|
||||
{
|
||||
// Invoke the generic button click event.
|
||||
PlayerControl? playerTarget = null;
|
||||
Vent? ventTarget = null;
|
||||
var role = PlayerControl.LocalPlayer.Data.Role;
|
||||
if (role.Role is RoleTypes.Tracker)
|
||||
{
|
||||
playerTarget = role.Cast<TrackerRole>().currentTarget;
|
||||
}
|
||||
else if (role.Role is RoleTypes.GuardianAngel)
|
||||
{
|
||||
playerTarget = role.Cast<GuardianAngelRole>().currentTarget;
|
||||
}
|
||||
else if (role.Role is RoleTypes.Engineer)
|
||||
{
|
||||
ventTarget = role.Cast<EngineerRole>().currentTarget;
|
||||
}
|
||||
var genericEvent = new VanillaButtonClickEvent(__instance, playerTarget, ventTarget);
|
||||
MiraEventManager.InvokeEvent(genericEvent);
|
||||
if (genericEvent.IsCancelled)
|
||||
{
|
||||
MiraEventManager.InvokeEvent(new VanillaButtonCancelledEvent(__instance));
|
||||
}
|
||||
|
||||
if (!genericEvent.IsCancelled)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(SecondaryAbilityButton), nameof(SecondaryAbilityButton.DoClick))]
|
||||
public static bool SecondaryDoClickPrefix(AbilityButton __instance)
|
||||
{
|
||||
// Invoke the generic button click event.
|
||||
PlayerControl? playerTarget = null;
|
||||
var role = PlayerControl.LocalPlayer.Data.Role;
|
||||
if (role.Role is RoleTypes.Detective)
|
||||
{
|
||||
playerTarget = role.Cast<DetectiveRole>().currentTarget;
|
||||
}
|
||||
var genericEvent = new VanillaButtonClickEvent(__instance, playerTarget);
|
||||
MiraEventManager.InvokeEvent(genericEvent);
|
||||
if (genericEvent.IsCancelled)
|
||||
{
|
||||
MiraEventManager.InvokeEvent(new VanillaButtonCancelledEvent(__instance));
|
||||
}
|
||||
|
||||
if (!genericEvent.IsCancelled)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Hud;
|
||||
using MiraAPI.Keybinds;
|
||||
@@ -25,9 +25,87 @@ public static class HudManagerPatches
|
||||
public static Transform? BottomRight { get; private set; }
|
||||
public static Transform? Buttons { get; private set; }
|
||||
|
||||
private static Dictionary<TextMeshPro, int> vanillaKeybindIcons = new();
|
||||
private static Dictionary<TextMeshPro, int> vanillaKeybindIcons = [];
|
||||
|
||||
internal static List<TextMeshPro> ModdedKeybindIcons = new();
|
||||
internal static List<TextMeshPro> ModdedKeybindIcons = [];
|
||||
|
||||
public static IEnumerator CoResizeUI()
|
||||
{
|
||||
while (!HudManager.Instance)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
yield return new WaitForSeconds(0.01f);
|
||||
ResizeUI(LocalSettingsTabSingleton<MiraApiSettings>.Instance.ButtonUIFactorSlider.Value);
|
||||
}
|
||||
|
||||
public static void ResizeUI(float scaleFactor)
|
||||
{
|
||||
var baseButtons = HudManager.Instance.transform.FindChild("Buttons");
|
||||
if (baseButtons != null)
|
||||
{
|
||||
foreach (var aspect in baseButtons.GetComponentsInChildren<AspectPosition>(true))
|
||||
{
|
||||
if (aspect.gameObject == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (aspect.gameObject.transform.parent.name == "TopRight")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (aspect.gameObject.transform.parent.transform.parent.name == "TopRight")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
aspect.gameObject.SetActive(!aspect.isActiveAndEnabled);
|
||||
aspect.DistanceFromEdge *= new Vector2(scaleFactor, scaleFactor);
|
||||
aspect.gameObject.SetActive(!aspect.isActiveAndEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var button in HudManager.Instance.GetComponentsInChildren<ActionButton>(true))
|
||||
{
|
||||
if (button.gameObject == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
button.gameObject.SetActive(!button.isActiveAndEnabled);
|
||||
button.gameObject.transform.localScale *= scaleFactor;
|
||||
button.gameObject.SetActive(!button.isActiveAndEnabled);
|
||||
}
|
||||
|
||||
if (baseButtons != null)
|
||||
{
|
||||
foreach (var arrange in baseButtons.GetComponentsInChildren<GridArrange>(true))
|
||||
{
|
||||
if (!arrange.gameObject || !arrange.transform)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
arrange.gameObject.SetActive(!arrange.isActiveAndEnabled);
|
||||
arrange.CellSize = new Vector2(scaleFactor, scaleFactor);
|
||||
arrange.gameObject.SetActive(!arrange.isActiveAndEnabled);
|
||||
if (arrange.isActiveAndEnabled && arrange.gameObject.transform.childCount != 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
arrange.ArrangeChilds();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Error($"Error arranging child objects in GridArrange: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
@@ -146,9 +224,15 @@ public static class HudManagerPatches
|
||||
}
|
||||
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;
|
||||
if (!MiraApiPlugin.IsMobile)
|
||||
{
|
||||
var comp = buttonObj.GetComponent<ActionButton>();
|
||||
KeybindManager.VanillaKeybinds[comp.GetType()].Button = comp;
|
||||
}
|
||||
}
|
||||
MiraApiSettings.OldButtonScaleFactor =
|
||||
LocalSettingsTabSingleton<MiraApiSettings>.Instance.ButtonUIFactorSlider.Value;
|
||||
Coroutines.Start(CoResizeUI());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -187,31 +271,54 @@ public static class HudManagerPatches
|
||||
{
|
||||
var canSeeBinds = ActiveInputManager.currentControlType == ActiveInputManager.InputType.Keyboard &&
|
||||
LocalSettingsTabSingleton<MiraApiSettings>.Instance.ShowKeybinds.Value;
|
||||
|
||||
foreach (var btnIcon in vanillaKeybindIcons)
|
||||
{
|
||||
btnIcon.Key.text = KeybindUtils.GetKeycodeByActionId(btnIcon.Value).ToString();
|
||||
btnIcon.Key.transform.parent.gameObject.SetActive(canSeeBinds);
|
||||
}
|
||||
|
||||
foreach (var btnIcon in ModdedKeybindIcons)
|
||||
{
|
||||
btnIcon.transform.parent.gameObject.SetActive(canSeeBinds);
|
||||
}
|
||||
|
||||
var player = ReInput.players.GetPlayer(0);
|
||||
var keyboard = player.controllers.Keyboard;
|
||||
foreach (var entry in KeybindManager.Keybinds)
|
||||
{
|
||||
var keyboard = player.controllers.Keyboard;
|
||||
bool modifierKeysPressed = entry.ModifierKeys.All(k => keyboard.GetModifierKey(k)) ||
|
||||
entry.ModifierKeys.Length <= 0;
|
||||
var modKeys = entry.ModifierKeys;
|
||||
bool modifierKeysPressed;
|
||||
if (modKeys.Length == 0)
|
||||
{
|
||||
modifierKeysPressed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
modifierKeysPressed = true;
|
||||
foreach (var key in modKeys)
|
||||
{
|
||||
if (keyboard.GetModifierKey(key)) continue;
|
||||
modifierKeysPressed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (player.GetButtonDown(entry.Id) && modifierKeysPressed)
|
||||
{
|
||||
entry.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var entry in KeybindManager.VanillaKeybinds.Values.Where(x => player.GetButtonDown(x.Id)))
|
||||
// suppressed warning because we want minimum allocations here
|
||||
#pragma warning disable S3267
|
||||
foreach (var entry in KeybindManager.VanillaKeybinds.Values)
|
||||
#pragma warning restore S3267
|
||||
{
|
||||
entry.Invoke();
|
||||
if (player.GetButtonDown(entry.Id))
|
||||
{
|
||||
entry.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
using HarmonyLib;
|
||||
using MiraAPI.LocalSettings;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Patches.Menu;
|
||||
|
||||
@@ -17,5 +19,6 @@ public static class MainMenuManagerPatches
|
||||
public static void AwakePostfix()
|
||||
{
|
||||
AddressablesLoader.LoadAll();
|
||||
Application.targetFrameRate = (int)LocalSettingsTabSingleton<MiraApiSettings>.Instance.SetFpsSlider.Value;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using AmongUs.GameOptions;
|
||||
using HarmonyLib;
|
||||
using Il2CppInterop.Runtime.Attributes;
|
||||
using MiraAPI.GameOptions;
|
||||
using MiraAPI.Modifiers;
|
||||
using MiraAPI.Presets;
|
||||
using MiraAPI.Utilities;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using Reactor.Localization.Utilities;
|
||||
@@ -10,6 +13,7 @@ using Reactor.Utilities.Extensions;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.UI;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace MiraAPI.Patches.Options;
|
||||
@@ -17,6 +21,20 @@ namespace MiraAPI.Patches.Options;
|
||||
[HarmonyPatch(typeof(GameOptionsMenu))]
|
||||
internal static class GameOptionsMenuPatch
|
||||
{
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(GameOptionsMenu.OnEnable))]
|
||||
public static void OpenPatch(GameOptionsMenu __instance)
|
||||
{
|
||||
HudManager.Instance.PlayerCam.OverrideScreenShakeEnabled = false;
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(GameOptionsMenu.OnDisable))]
|
||||
public static void ClosePatch(GameOptionsMenu __instance)
|
||||
{
|
||||
HudManager.Instance.PlayerCam.OverrideScreenShakeEnabled = true;
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(GameOptionsMenu.Update))]
|
||||
// ReSharper disable once InconsistentNaming
|
||||
@@ -153,7 +171,7 @@ internal static class GameOptionsMenuPatch
|
||||
private static void ModifiersUpdate(ref float num)
|
||||
{
|
||||
var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
|
||||
.Where(x => x.ShowInModifiersMenu || x.OptionableType?.IsAssignableTo(typeof(BaseModifier))==true) ?? [];
|
||||
.Where(x => x.ShowInModifiersMenu || x.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true) ?? [];
|
||||
|
||||
foreach (var modGroup in groups)
|
||||
{
|
||||
@@ -164,7 +182,7 @@ internal static class GameOptionsMenuPatch
|
||||
private static void ModifiersCreate(GameOptionsMenu menu)
|
||||
{
|
||||
var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
|
||||
.Where(x => x.ShowInModifiersMenu || x.OptionableType?.IsAssignableTo(typeof(BaseModifier))==true) ?? [];
|
||||
.Where(x => x.ShowInModifiersMenu || x.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true) ?? [];
|
||||
foreach (var group in groups)
|
||||
{
|
||||
CreateGroup(menu, group);
|
||||
@@ -257,7 +275,9 @@ internal static class GameOptionsMenuPatch
|
||||
categoryHeaderMasked.SetHeader(CustomStringName.CreateAndRegister(group.GroupName), 20);
|
||||
categoryHeaderMasked.Background.color = group.GroupColor;
|
||||
categoryHeaderMasked.Divider.color = group.GroupColor;
|
||||
categoryHeaderMasked.Title.color = group.GroupColor.Equals(MiraApiPlugin.DefaultHeaderColor) ? Color.white : group.GroupColor.FindAlternateColor();
|
||||
categoryHeaderMasked.Title.color = group.GroupColor.Equals(MiraApiPlugin.DefaultHeaderColor)
|
||||
? Color.white
|
||||
: group.GroupColor.FindAlternateColor();
|
||||
|
||||
categoryHeaderMasked.Background.sprite = MiraAssets.CategoryHeader.LoadAsset();
|
||||
categoryHeaderMasked.Background.sprite.texture.filterMode = FilterMode.Bilinear;
|
||||
@@ -276,13 +296,20 @@ internal static class GameOptionsMenuPatch
|
||||
newText.transform.localPosition = new Vector3(2.6249f, -0.165f, 0f);
|
||||
newText.gameObject.GetComponent<TextTranslatorTMP>().Destroy();
|
||||
|
||||
var options = group.Options.Select(
|
||||
opt => opt.CreateOption(
|
||||
menu.checkboxOrigin,
|
||||
menu.numberOptionOrigin,
|
||||
menu.stringOptionOrigin,
|
||||
menu.playerOptionOrigin,
|
||||
menu.settingsContainer));
|
||||
var options = group.Options.Select(opt => opt.CreateOption(
|
||||
menu.checkboxOrigin,
|
||||
menu.numberOptionOrigin,
|
||||
menu.stringOptionOrigin,
|
||||
menu.playerOptionOrigin,
|
||||
menu.settingsContainer));
|
||||
|
||||
OptionPreset? defaultPreset = null;
|
||||
if (GameSettingMenuPatches.SelectedMod != null && PresetManager.DefaultPresets.TryGetValue(
|
||||
GameSettingMenuPatches.SelectedMod,
|
||||
out var preset))
|
||||
{
|
||||
defaultPreset = preset;
|
||||
}
|
||||
|
||||
foreach (var newOpt in options)
|
||||
{
|
||||
@@ -318,14 +345,79 @@ internal static class GameOptionsMenuPatch
|
||||
if (newOpt is ToggleOption toggle)
|
||||
{
|
||||
toggle.CheckMark.sprite = MiraAssets.Checkmark.LoadAsset();
|
||||
toggle.CheckMark.color = group.GroupColor != MiraApiPlugin.DefaultHeaderColor ? group.GroupColor : MiraAssets.AcceptedTeal;
|
||||
toggle.CheckMark.color = group.GroupColor != MiraApiPlugin.DefaultHeaderColor
|
||||
? group.GroupColor
|
||||
: MiraAssets.AcceptedTeal;
|
||||
var rend = toggle.CheckMark.transform.parent.FindChild("ActiveSprite")
|
||||
.GetComponent<SpriteRenderer>();
|
||||
rend.sprite = MiraAssets.CheckmarkBox.LoadAsset();
|
||||
rend.color = group.GroupColor != MiraApiPlugin.DefaultHeaderColor ? group.GroupColor : MiraAssets.AcceptedTeal;
|
||||
rend.color = group.GroupColor != MiraApiPlugin.DefaultHeaderColor
|
||||
? group.GroupColor
|
||||
: MiraAssets.AcceptedTeal;
|
||||
}
|
||||
|
||||
menu.Children.Add(newOpt);
|
||||
var resetBtn = new GameObject("ResetOption");
|
||||
resetBtn.transform.parent = newOpt.transform;
|
||||
resetBtn.transform.localScale = new(.5f, .5f, 1);
|
||||
resetBtn.layer = LayerMask.NameToLayer("UI");
|
||||
resetBtn.transform.localPosition = new Vector3(-3.1f, 0f, -2f);
|
||||
var resetRend = resetBtn.AddComponent<SpriteRenderer>();
|
||||
resetRend.sprite = MiraAssets.ResetButton.LoadAsset();
|
||||
resetRend.maskInteraction = SpriteMaskInteraction.VisibleInsideMask;
|
||||
resetRend.color = group.GroupColor.Equals(MiraApiPlugin.DefaultHeaderColor)
|
||||
? Color.white
|
||||
: group.GroupColor.FindAlternateColor();
|
||||
var resetBoxCol = resetBtn.gameObject.AddComponent<BoxCollider2D>();
|
||||
resetBoxCol.size = new Vector2(1f, 1f);
|
||||
resetBoxCol.offset = new Vector2(0, 0);
|
||||
var passiveButton = resetBtn.AddComponent<PassiveButton>();
|
||||
passiveButton.OnClick = new Button.ButtonClickedEvent();
|
||||
passiveButton.ClickSound = menu.BackButton.GetComponent<PassiveButton>().ClickSound;
|
||||
passiveButton.OnMouseOver = new UnityEvent();
|
||||
passiveButton.OnMouseOver.AddListener(
|
||||
(UnityAction)(() =>
|
||||
{
|
||||
resetRend.color = group.GroupColor != MiraApiPlugin.DefaultHeaderColor
|
||||
? group.GroupColor
|
||||
: MiraAssets.AcceptedTeal;
|
||||
}));
|
||||
passiveButton.OnMouseOut = new UnityEvent();
|
||||
passiveButton.OnMouseOut.AddListener(
|
||||
(UnityAction)(() =>
|
||||
{
|
||||
resetRend.color = group.GroupColor.Equals(MiraApiPlugin.DefaultHeaderColor)
|
||||
? Color.white
|
||||
: group.GroupColor.FindAlternateColor();
|
||||
}));
|
||||
if (newOpt is ToggleOption toggleOpt)
|
||||
{
|
||||
passiveButton.OnClick.AddListener(
|
||||
(UnityAction)(() =>
|
||||
{
|
||||
defaultPreset!.ResetOption(toggleOpt);
|
||||
}));
|
||||
}
|
||||
else if (newOpt is NumberOption numOpt)
|
||||
{
|
||||
passiveButton.OnClick.AddListener(
|
||||
(UnityAction)(() =>
|
||||
{
|
||||
defaultPreset!.ResetOption(numOpt);
|
||||
}));
|
||||
}
|
||||
else if (newOpt is StringOption strOpt)
|
||||
{
|
||||
passiveButton.OnClick.AddListener(
|
||||
(UnityAction)(() =>
|
||||
{
|
||||
defaultPreset!.ResetOption(strOpt);
|
||||
}));
|
||||
}
|
||||
if (!defaultPreset!.IsOptionInPreset(newOpt))
|
||||
{
|
||||
resetBtn.Destroy();
|
||||
}
|
||||
|
||||
newOpt.Initialize();
|
||||
newOpt.gameObject.SetActive(false);
|
||||
|
||||
@@ -35,6 +35,19 @@ public static class RoleSettingMenuPatches
|
||||
private static RoleBehaviour? CurrentRole { get; set; }
|
||||
private static List<IModdedOption>? CurrentRoleOptions { get; set; }
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(RolesSettingsMenu.OnEnable))]
|
||||
public static void OpenPatch(RolesSettingsMenu __instance)
|
||||
{
|
||||
HudManager.Instance.PlayerCam.OverrideScreenShakeEnabled = false;
|
||||
}
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(RolesSettingsMenu.OnDisable))]
|
||||
public static void ClosePatch(RolesSettingsMenu __instance)
|
||||
{
|
||||
HudManager.Instance.PlayerCam.OverrideScreenShakeEnabled = true;
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(RolesSettingsMenu.SetQuotaTab))]
|
||||
public static void SetQuotaTabPostfix(RolesSettingsMenu __instance)
|
||||
|
||||
@@ -117,6 +117,20 @@ internal static class PlayerControlPatches
|
||||
__instance.RpcMurderPlayer(target, true);
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(PlayerControl.RpcMurderPlayer))]
|
||||
[HarmonyPatch(nameof(PlayerControl.MurderPlayer))]
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public static bool MurderPlayerPrefix(PlayerControl __instance)
|
||||
{
|
||||
if (LobbyBehaviour.Instance)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(PlayerControl.FixedUpdate))]
|
||||
// ReSharper disable once InconsistentNaming
|
||||
|
||||
@@ -13,6 +13,10 @@ internal static class HowToPlayPatch
|
||||
// yes i patched the entire method
|
||||
private static void Prefix(HowToPlayScene __instance)
|
||||
{
|
||||
if (RoleManager.Instance.AllRoles.ToArray().All(x => !x.IsCustomRole()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
__instance.sceneIndex = 0;
|
||||
__instance.category = HowToPlayScene.HowToPlayCategory.RolesSelection;
|
||||
__instance.startPage.SetActive(false);
|
||||
|
||||
@@ -58,7 +58,7 @@ public static class KillButtonPatches
|
||||
return false;
|
||||
}
|
||||
|
||||
PlayerControl.LocalPlayer.RpcCustomMurder(__instance.currentTarget);
|
||||
PlayerControl.LocalPlayer.RpcCustomMurder(__instance.currentTarget, MeetingCheck.OutsideMeeting);
|
||||
__instance.SetTarget(null);
|
||||
|
||||
return false;
|
||||
|
||||
@@ -39,7 +39,7 @@ public static class LogicRoleSelectionHnsPatch
|
||||
foreach (RoleManager.RoleAssignmentData roleAssignmentData in guaranteedRoles.Select((x) =>
|
||||
new RoleManager.RoleAssignmentData(x, roleOptions.GetNumPerGame(x.Role), 100)))
|
||||
{
|
||||
for (;;)
|
||||
while (true)
|
||||
{
|
||||
RoleManager.RoleAssignmentData roleAssignmentData2 = roleAssignmentData;
|
||||
int count = roleAssignmentData2.Count;
|
||||
@@ -122,7 +122,7 @@ public static class LogicRoleSelectionHnsPatch
|
||||
foreach (RoleManager.RoleAssignmentData roleAssignmentData in guaranteedRoles.Select((x) =>
|
||||
new RoleManager.RoleAssignmentData(x, roleOptions.GetNumPerGame(x.Role), 100)))
|
||||
{
|
||||
for (;;)
|
||||
while (true)
|
||||
{
|
||||
RoleManager.RoleAssignmentData roleAssignmentData2 = roleAssignmentData;
|
||||
int count = roleAssignmentData2.Count;
|
||||
|
||||
@@ -36,7 +36,7 @@ public static class LogicRoleSelectionNormalPatch
|
||||
foreach (RoleManager.RoleAssignmentData roleAssignmentData in guaranteedRoles.Select((x) =>
|
||||
new RoleManager.RoleAssignmentData(x, roleOptions.GetNumPerGame(x.Role), 100)))
|
||||
{
|
||||
for (;;)
|
||||
while (true)
|
||||
{
|
||||
RoleManager.RoleAssignmentData roleAssignmentData2 = roleAssignmentData;
|
||||
int count = roleAssignmentData2.Count;
|
||||
|
||||
@@ -71,4 +71,26 @@ public class OptionPreset
|
||||
|
||||
CustomRoleManager.SyncAllRoleSettings();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the specified option with the preset provided.
|
||||
/// </summary>
|
||||
public void ResetOption(OptionBehaviour baseOption)
|
||||
{
|
||||
var selectedOpt = Plugin.InternalOptions.First(x => x.OptionBehaviour == baseOption);
|
||||
selectedOpt.LoadFromPreset(PresetConfig);
|
||||
/*ModdedOptionsManager.SyncAllOptions();
|
||||
|
||||
CustomRoleManager.SyncAllRoleSettings();*/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the specified option is included in the preset.
|
||||
/// </summary>
|
||||
/// <returns>>The value of whether the option is in the preset.</returns>
|
||||
public bool IsOptionInPreset(OptionBehaviour baseOption)
|
||||
{
|
||||
var selectedOpt = Plugin.InternalOptions.First(x => x.OptionBehaviour == baseOption);
|
||||
return selectedOpt.ConfigDefinition != null && PresetConfig.ContainsKey(selectedOpt.ConfigDefinition);
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ public static class PresetManager
|
||||
/// </summary>
|
||||
public static string PresetDirectory { get; } = Path.GetFullPath("mira_presets", Application.persistentDataPath);
|
||||
|
||||
internal static Dictionary<MiraPluginInfo, OptionPreset> DefaultPresets { get; } = [];
|
||||
internal static List<OptionPreset> InternalMasterPresets { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
@@ -47,6 +48,7 @@ public static class PresetManager
|
||||
}
|
||||
|
||||
presetConfig.Save();
|
||||
DefaultPresets.Add(plugin, new OptionPreset("Default", plugin, presetConfig));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
@@ -1,5 +1,6 @@
|
||||
using AmongUs.GameOptions;
|
||||
using System;
|
||||
using Il2CppInterop.Runtime.Attributes;
|
||||
using MiraAPI.Patches.Freeplay;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using UnityEngine;
|
||||
@@ -77,16 +78,19 @@ public record struct CustomRoleConfiguration
|
||||
/// <summary>
|
||||
/// Gets or sets the Sprite used for the Role Options menu screenshot.
|
||||
/// </summary>
|
||||
[HideFromIl2Cpp]
|
||||
public LoadableAsset<Sprite>? OptionsScreenshot { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Sprite used for the Role Icon.
|
||||
/// </summary>
|
||||
[HideFromIl2Cpp]
|
||||
public LoadableAsset<Sprite>? Icon { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Intro sound for the Role.
|
||||
/// </summary>
|
||||
[HideFromIl2Cpp]
|
||||
public LoadableAsset<AudioClip>? IntroSound { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -89,15 +89,22 @@ public static class CustomRoleManager
|
||||
|
||||
foreach (var roleType in roles)
|
||||
{
|
||||
ClassInjector.RegisterTypeInIl2Cpp(roleType);
|
||||
|
||||
var role = RegisterRole(roleType, pluginInfo);
|
||||
if (role is null)
|
||||
try
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ClassInjector.RegisterTypeInIl2Cpp(roleType);
|
||||
|
||||
pluginInfo.InternalRoles.Add((ushort)role.Role, role);
|
||||
var role = RegisterRole(roleType, pluginInfo);
|
||||
if (role is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
pluginInfo.InternalRoles.Add((ushort)role.Role, role);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Error($"Failed to register role type {roleType.Name}: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
pluginInfo.PluginConfig.Save();
|
||||
|
||||
@@ -90,6 +90,11 @@ public static class MiraAssets
|
||||
/// </summary>
|
||||
public static LoadableResourceAsset CheckmarkBox { get; } = new("MiraAPI.Resources.CheckMarkBox.png");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Reset Box used in the Settings Menu.
|
||||
/// </summary>
|
||||
public static LoadableResourceAsset ResetButton { get; } = new("MiraAPI.Resources.ResetButton.png");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Checkmark used in the Settings Menu.
|
||||
/// </summary>
|
||||
|
||||
@@ -546,10 +546,6 @@ public static class Helpers
|
||||
public static bool IsRoleBlacklisted(this RoleBehaviour role)
|
||||
{
|
||||
// This should be patchable by mods when a vanilla role is meant to be replaced by a custom role.
|
||||
if (role.Role is RoleTypes.Engineer)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user