mirror of
https://github.com/All-Of-Us-Mods/MiraAPI.git
synced 2026-10-20 01:46:15 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a72fefe235 |
No files matched your search
+2
-1
@@ -1 +1,2 @@
|
||||
[*.{cs,vb}]
|
||||
[*.{cs,vb}]
|
||||
dotnet_diagnostic.CA1707.severity = none
|
||||
@@ -3,7 +3,6 @@
|
||||
.vs/
|
||||
obj/
|
||||
bin/
|
||||
.qodo/
|
||||
|
||||
*.user
|
||||
*.lock.json
|
||||
+3
-3
@@ -1,11 +1,11 @@
|
||||
<Project>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Reactor" Version="2.3.1" />
|
||||
<PackageReference Include="BepInEx.Unity.IL2CPP" Version="6.0.0-be.735" Private="false" ExcludeAssets="runtime;native" />
|
||||
<PackageReference Include="AmongUs.GameLibs.Steam" Version="2025.4.15" PrivateAssets="all" />
|
||||
<PackageReference Include="BepInEx.Unity.IL2CPP" Version="6.0.0-be.697" Private="false" ExcludeAssets="runtime;native" />
|
||||
<PackageReference Include="AmongUs.GameLibs.Steam" Version="2024.10.29" 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.0.1" PrivateAssets="all" ExcludeAssets="runtime" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="Copy" AfterTargets="Build" Condition="'$(AmongUs)' != ''">
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556" PrivateAssets="all" />
|
||||
<AdditionalFiles Include="$(MSBuildThisFileDirectory)\stylecop.json" Link="stylecop.json" Visible="false" />
|
||||
|
||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="10.7.0.110445">
|
||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.32.0.97167">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -5,7 +5,6 @@ namespace MiraAPI.Example;
|
||||
public static class ExampleAssets
|
||||
{
|
||||
public static LoadableResourceAsset ExampleButton { get; } = new("MiraAPI.Example.Resources.ExampleButton.png");
|
||||
public static LoadableResourceAsset CallMeetingButton { get; } = new("MiraAPI.Example.Resources.CallMeeting.png");
|
||||
|
||||
// Credit to EpicHorrors for the teleport button asset.
|
||||
public static LoadableResourceAsset TeleportButton { get; } = new("MiraAPI.Example.Resources.TeleportButton.png");
|
||||
|
||||
@@ -18,7 +18,7 @@ public static class ExampleEventHandlers
|
||||
public static void Initialize()
|
||||
{
|
||||
// You can register event handlers with the MiraEventManager class.
|
||||
var handle = MiraEventManager.RegisterEventHandler<BeforeMurderEvent>(@event =>
|
||||
MiraEventManager.RegisterEventHandler<BeforeMurderEvent>(@event =>
|
||||
{
|
||||
Logger<ExamplePlugin>.Info($"{@event.Source.Data.PlayerName} is about to kill {@event.Target.Data.PlayerName}");
|
||||
});
|
||||
@@ -32,9 +32,6 @@ public static class ExampleEventHandlers
|
||||
{
|
||||
Logger<ExamplePlugin>.Info($"{@event.Player.Data.PlayerName} completed {@event.Task.TaskType.ToString()}");
|
||||
});
|
||||
|
||||
// You can also unregister event handlers using the handle returned from RegisterEventHandler.
|
||||
MiraEventManager.UnregisterEventHandler(handle);
|
||||
}
|
||||
|
||||
// If you want to add extra votes to a player, do something like this.
|
||||
|
||||
@@ -9,9 +9,11 @@
|
||||
<ProjectReference Include="..\MiraAPI\MiraAPI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources\**\*.*" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources\ExampleButton.png" />
|
||||
<EmbeddedResource Include="Resources\FortniteBanner.jpeg" />
|
||||
<EmbeddedResource Include="Resources\TeleportButton.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\AmongUs.props" />
|
||||
</Project>
|
||||
@@ -1,26 +1,12 @@
|
||||
using MiraAPI.Example.Options.Modifiers;
|
||||
using MiraAPI.GameOptions;
|
||||
using MiraAPI.Modifiers;
|
||||
using MiraAPI.Modifiers.Types;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Example.Modifiers;
|
||||
|
||||
public class CaptainModifier : GameModifier
|
||||
{
|
||||
public override string ModifierName => "Captain";
|
||||
public override LoadableAsset<Sprite>? ModifierIcon => ExampleAssets.CallMeetingButton;
|
||||
|
||||
public override void OnDeath(DeathReason reason)
|
||||
{
|
||||
Player.RemoveModifier(this);
|
||||
}
|
||||
|
||||
public override string GetDescription()
|
||||
{
|
||||
return $"You can call a meeting from anywhere on the map.";
|
||||
}
|
||||
|
||||
public override int GetAssignmentChance()
|
||||
{
|
||||
|
||||
@@ -6,11 +6,6 @@ public class HighPriorityModifier : GameModifier
|
||||
{
|
||||
public override string ModifierName => "High Priority";
|
||||
|
||||
public override string GetDescription()
|
||||
{
|
||||
return "You are high priority! Idrk what this means either tbh...";
|
||||
}
|
||||
|
||||
public override int GetAssignmentChance()
|
||||
{
|
||||
return 100;
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 36 KiB |
@@ -1,7 +1,6 @@
|
||||
using AmongUs.GameOptions;
|
||||
using MiraAPI.Patches.Stubs;
|
||||
using MiraAPI.Roles;
|
||||
using Reactor.Utilities;
|
||||
using MiraAPI.Utilities;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -15,64 +14,37 @@ public class ChameloenRole : CrewmateRole, ICustomRole
|
||||
public Color RoleColor => Palette.AcceptedGreen;
|
||||
public ModdedRoleTeams Team => ModdedRoleTeams.Crewmate;
|
||||
|
||||
private bool _shouldHide;
|
||||
|
||||
public CustomRoleConfiguration Configuration => new(this)
|
||||
{
|
||||
OptionsScreenshot = ExampleAssets.Banner,
|
||||
IntroSound = CustomRoleUtils.GetIntroSound(RoleTypes.Shapeshifter),
|
||||
};
|
||||
|
||||
public override void Initialize(PlayerControl player)
|
||||
public void PlayerControlFixedUpdate(PlayerControl playerControl)
|
||||
{
|
||||
Logger<ExamplePlugin>.Info("Initializing ChamelonRole for player: " + player.PlayerId);
|
||||
RoleBehaviourStubs.Initialize(this, player);
|
||||
_shouldHide = true;
|
||||
}
|
||||
|
||||
public void FixedUpdate()
|
||||
{
|
||||
if (!Player || !_shouldHide)
|
||||
if (playerControl.MyPhysics.Velocity.magnitude > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Player.MyPhysics.Velocity.magnitude > 0)
|
||||
{
|
||||
var rend = Player.cosmetics.currentBodySprite.BodySprite;
|
||||
var tmp = Player.cosmetics.nameText;
|
||||
SpriteRenderer rend = playerControl.cosmetics.currentBodySprite.BodySprite;
|
||||
TextMeshPro tmp = playerControl.cosmetics.nameText;
|
||||
tmp.color = Color.Lerp(tmp.color, new Color(tmp.color.r, tmp.color.g, tmp.color.b, 1), Time.deltaTime * 4f);
|
||||
rend.color = Color.Lerp(rend.color, new Color(1, 1, 1, 1), Time.deltaTime * 4f);
|
||||
|
||||
foreach (var cosmetic in Player.cosmetics.transform.GetComponentsInChildren<SpriteRenderer>())
|
||||
foreach (var cosmetic in playerControl.cosmetics.transform.GetComponentsInChildren<SpriteRenderer>())
|
||||
{
|
||||
cosmetic.color = Color.Lerp(cosmetic.color, new Color(1, 1, 1, 1), Time.deltaTime * 4f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SpriteRenderer rend = Player.cosmetics.currentBodySprite.BodySprite;
|
||||
TextMeshPro tmp = Player.cosmetics.nameText;
|
||||
tmp.color = Color.Lerp(tmp.color, new Color(tmp.color.r, tmp.color.g, tmp.color.b, Player.AmOwner ? 0.3f : 0), Time.deltaTime * 4f);
|
||||
rend.color = Color.Lerp(rend.color, new Color(1, 1, 1, Player.AmOwner ? 0.3f : 0), Time.deltaTime * 4f);
|
||||
SpriteRenderer rend = playerControl.cosmetics.currentBodySprite.BodySprite;
|
||||
TextMeshPro tmp = playerControl.cosmetics.nameText;
|
||||
tmp.color = Color.Lerp(tmp.color, new Color(tmp.color.r, tmp.color.g, tmp.color.b, playerControl.AmOwner ? 0.3f : 0), Time.deltaTime * 4f);
|
||||
rend.color = Color.Lerp(rend.color, new Color(1, 1, 1, playerControl.AmOwner ? 0.3f : 0), Time.deltaTime * 4f);
|
||||
|
||||
foreach (var cosmetic in Player.cosmetics.transform.GetComponentsInChildren<SpriteRenderer>())
|
||||
foreach (var cosmetic in playerControl.cosmetics.transform.GetComponentsInChildren<SpriteRenderer>())
|
||||
{
|
||||
cosmetic.color = Color.Lerp(cosmetic.color, new Color(1, 1, 1, Player.AmOwner ? 0.3f : 0), Time.deltaTime * 4f);
|
||||
cosmetic.color = Color.Lerp(cosmetic.color, new Color(1, 1, 1, playerControl.AmOwner ? 0.3f : 0), Time.deltaTime * 4f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deinitialize(PlayerControl targetPlayer)
|
||||
{
|
||||
Logger<ExamplePlugin>.Info("Deinitializing ChamelonRole for player: " + targetPlayer.PlayerId);
|
||||
RoleBehaviourStubs.Deinitialize(this, targetPlayer);
|
||||
_shouldHide = false;
|
||||
foreach (var cosmetic in Player.cosmetics.transform.GetComponentsInChildren<SpriteRenderer>(true))
|
||||
{
|
||||
cosmetic.color = Color.white;
|
||||
}
|
||||
Player.cosmetics.currentBodySprite.BodySprite.color = Color.white;
|
||||
Player.cosmetics.nameText.color = Color.white;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace MiraAPI.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Event handle to use when unregistering an event.
|
||||
/// </summary>
|
||||
public class MiraEventHandle
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the type of event.
|
||||
/// </summary>
|
||||
public Type EventType { get; }
|
||||
|
||||
internal MiraEventWrapper EventWrapper { get; }
|
||||
|
||||
internal MiraEventHandle(Type type, MiraEventWrapper eventWrapper)
|
||||
{
|
||||
EventType = type;
|
||||
EventWrapper = eventWrapper;
|
||||
}
|
||||
}
|
||||
@@ -21,21 +21,14 @@ public static class MiraEventManager
|
||||
public static bool InvokeEvent<T>(T eventInstance) where T : MiraEvent
|
||||
{
|
||||
EventWrappers.TryGetValue(typeof(T), out var handlers);
|
||||
if (handlers == null || handlers.Count == 0)
|
||||
if (handlers == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var handler in handlers)
|
||||
{
|
||||
try
|
||||
{
|
||||
((Action<T>)handler.EventHandler).Invoke(eventInstance);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Error invoking event handler for {typeof(T).Name}: {ex.ToString()}");
|
||||
}
|
||||
((Action<T>)handler.EventHandler).Invoke(eventInstance);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -50,21 +43,14 @@ public static class MiraEventManager
|
||||
public static bool InvokeEvent(MiraEvent eventInstance, Type type)
|
||||
{
|
||||
EventWrappers.TryGetValue(type, out var handlers);
|
||||
if (handlers == null || handlers.Count == 0)
|
||||
if (handlers == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var handler in handlers)
|
||||
{
|
||||
try
|
||||
{
|
||||
handler.EventHandler.DynamicInvoke(eventInstance);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Error invoking event handler for {type.Name}: {ex.ToString()}");
|
||||
}
|
||||
handler.EventHandler.DynamicInvoke(eventInstance);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -75,78 +61,47 @@ public static class MiraEventManager
|
||||
/// </summary>
|
||||
/// <param name="type">The type of event.</param>
|
||||
/// <param name="methodInfo">The MethodInfo of the event handler.</param>
|
||||
/// <param name="priority">The priority of the event handler. Lower values are called first.</param>
|
||||
/// <returns>An event handle to use when unregistering the event.</returns>
|
||||
public static MiraEventHandle RegisterEventHandler(Type type, MethodInfo methodInfo, int priority = 0)
|
||||
/// <param name="priority">The priority of the event handler. Higher values are called first.</param>
|
||||
public static void RegisterEventHandler(Type type, MethodInfo methodInfo, int priority = 0)
|
||||
{
|
||||
if (!type.IsSubclassOf(typeof(MiraEvent)))
|
||||
{
|
||||
throw new InvalidOperationException($"Type must be a subclass of MiraEvent: {type.FullName}");
|
||||
Logger<MiraApiPlugin>.Error($"Type must be a subclass of MiraEvent: {nameof(type)}");
|
||||
return;
|
||||
}
|
||||
|
||||
EventWrappers.TryAdd(type, []);
|
||||
var handlers = EventWrappers[type];
|
||||
if (!EventWrappers.TryGetValue(type, out var handlers))
|
||||
{
|
||||
handlers = [];
|
||||
EventWrappers.Add(type, handlers);
|
||||
}
|
||||
|
||||
var @delegate = Delegate.CreateDelegate(typeof(Action<>).MakeGenericType(type), methodInfo);
|
||||
var eventWrapper = new MiraEventWrapper(@delegate, priority);
|
||||
|
||||
var index = handlers.BinarySearch(eventWrapper, Comparer<MiraEventWrapper>.Create((a, b) => a.Priority.CompareTo(b.Priority)));
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
index = ~index;
|
||||
}
|
||||
|
||||
handlers.Insert(index, eventWrapper);
|
||||
return new MiraEventHandle(type, eventWrapper);
|
||||
handlers.Add(new MiraEventWrapper(@delegate, priority));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register an event.
|
||||
/// </summary>
|
||||
/// <param name="handler">The callback method/handler for the event.</param>
|
||||
/// <param name="priority">The priority of the event handler. Lower values are called first.</param>
|
||||
/// <param name="priority">The priority of the event handler. Higher values are called first.</param>
|
||||
/// <typeparam name="T">Type of event.</typeparam>
|
||||
/// <returns>An event handle to use when unregistering the event.</returns>
|
||||
public static MiraEventHandle RegisterEventHandler<T>(Action<T> handler, int priority = 0) where T : MiraEvent
|
||||
public static void RegisterEventHandler<T>(Action<T> handler, int priority = 0) where T : MiraEvent
|
||||
{
|
||||
EventWrappers.TryAdd(typeof(T), []);
|
||||
if (!EventWrappers.ContainsKey(typeof(T)))
|
||||
{
|
||||
EventWrappers.Add(typeof(T), []);
|
||||
}
|
||||
|
||||
var handlers = EventWrappers[typeof(T)];
|
||||
var eventWrapper = new MiraEventWrapper(handler, priority);
|
||||
|
||||
var index = handlers.BinarySearch(eventWrapper, Comparer<MiraEventWrapper>.Create((a, b) => a.Priority.CompareTo(b.Priority)));
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
index = ~index;
|
||||
}
|
||||
|
||||
handlers.Insert(index, eventWrapper);
|
||||
return new MiraEventHandle(typeof(T), eventWrapper);
|
||||
handlers.Add(new MiraEventWrapper(handler, priority));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregister an event using the event handle.
|
||||
/// </summary>
|
||||
/// <param name="eventHandle">A handle to the event.</param>
|
||||
/// <returns>True if the event was unregistered successfully, false otherwise.</returns>
|
||||
public static bool UnregisterEventHandler(MiraEventHandle eventHandle)
|
||||
internal static void SortAllHandlers()
|
||||
{
|
||||
if (!EventWrappers.TryGetValue(eventHandle.EventType, out var handlers))
|
||||
foreach (var handlers in EventWrappers.Values)
|
||||
{
|
||||
return false;
|
||||
handlers.Sort((a, b) => a.Priority.CompareTo(b.Priority));
|
||||
}
|
||||
|
||||
if (!handlers.Remove(eventHandle.EventWrapper))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (handlers.Count == 0)
|
||||
{
|
||||
EventWrappers.Remove(eventHandle.EventType);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ public class MiraEventWrapper
|
||||
public Delegate EventHandler { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the priority of the handler. Lower values are called first.
|
||||
/// Gets the priority of the handler. Higher values are called first.
|
||||
/// </summary>
|
||||
public int Priority { get; }
|
||||
|
||||
|
||||
@@ -9,14 +9,14 @@ namespace MiraAPI.Events;
|
||||
public class RegisterEventAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the priority of the event. Lower values are called first.
|
||||
/// Gets the priority of the event.
|
||||
/// </summary>
|
||||
public int Priority { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RegisterEventAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="priority">The priority of the event. Lower values are called first.</param>
|
||||
/// <param name="priority">The priority of the event.</param>
|
||||
public RegisterEventAttribute(int priority = 0)
|
||||
{
|
||||
Priority = priority;
|
||||
|
||||
@@ -15,21 +15,14 @@ public class AfterMurderEvent : MiraEvent
|
||||
/// </summary>
|
||||
public PlayerControl Target { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the player's body, if it exists.
|
||||
/// </summary>
|
||||
public DeadBody? DeadBody { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AfterMurderEvent"/> class.
|
||||
/// </summary>
|
||||
/// <param name="source">The killer.</param>
|
||||
/// <param name="target">The killed player.</param>
|
||||
/// <param name="deadBody">The player's vody, if it exists.</param>
|
||||
public AfterMurderEvent(PlayerControl source, PlayerControl target, DeadBody? deadBody)
|
||||
public AfterMurderEvent(PlayerControl source, PlayerControl target)
|
||||
{
|
||||
Source = source;
|
||||
Target = target;
|
||||
DeadBody = deadBody;
|
||||
}
|
||||
}
|
||||
@@ -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,12 +0,0 @@
|
||||
namespace MiraAPI.Events.Vanilla.Gameplay;
|
||||
|
||||
/// <summary>
|
||||
/// The event that is invoked when the intro cutscene has finished playing.
|
||||
/// </summary>
|
||||
public class IntroEndEvent(IntroCutscene introCutscene) : MiraEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the instance of the IntroCutscene.
|
||||
/// </summary>
|
||||
public IntroCutscene IntroCutscene { get; } = introCutscene;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
namespace MiraAPI.Events.Vanilla.Gameplay;
|
||||
|
||||
/// <summary>
|
||||
/// Start Round event, invoked on IntroCutscene.OnDestroy and ExileController.WrapUp.
|
||||
/// </summary>
|
||||
public class RoundStartEvent : MiraEvent
|
||||
{
|
||||
/// <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="RoundStartEvent"/> class.
|
||||
/// </summary>
|
||||
/// <param name="triggeredByIntro">Whether the event was triggered by the intro or not.</param>
|
||||
public RoundStartEvent(bool triggeredByIntro)
|
||||
{
|
||||
TriggeredByIntro = triggeredByIntro;
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,6 @@ public class UpdateSystemEvent : MiraCancelableEvent
|
||||
/// Initializes a new instance of the <see cref="UpdateSystemEvent"/> class.
|
||||
/// </summary>
|
||||
/// <param name="systemType">The SystemType being updated.</param>
|
||||
/// <param name="player">The player that is updating the system.</param>
|
||||
/// <param name="amount">Amount to update System to.</param>
|
||||
public UpdateSystemEvent(SystemTypes systemType, PlayerControl player, byte amount)
|
||||
{
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
namespace MiraAPI.Events.Vanilla.Meeting;
|
||||
|
||||
/// <summary>
|
||||
/// The event that is invoked when the meeting has ended.
|
||||
/// </summary>
|
||||
public class EndMeetingEvent(MeetingHud meetingHud) : MiraEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the MeetingHud instance.
|
||||
/// </summary>
|
||||
public MeetingHud MeetingHud { get; } = meetingHud;
|
||||
}
|
||||
@@ -24,11 +24,6 @@ public class HandleVoteEvent : MiraCancelableEvent
|
||||
/// </summary>
|
||||
public byte TargetId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether to prevent the vote from commencing.
|
||||
/// </summary>
|
||||
public bool PreventVote { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the player info of the target.
|
||||
/// </summary>
|
||||
@@ -44,7 +39,6 @@ public class HandleVoteEvent : MiraCancelableEvent
|
||||
VoteData = playerVoteData;
|
||||
Player = playerVoteData.Owner;
|
||||
TargetId = targetId;
|
||||
PreventVote = false;
|
||||
TargetPlayerInfo = GameData.Instance.GetPlayerById(targetId);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
namespace MiraAPI.Events.Vanilla.Meeting.Voting;
|
||||
|
||||
/// <summary>
|
||||
/// The event that is invoked when the voting phase of a meeting is complete.
|
||||
/// </summary>
|
||||
/// <param name="meetingHud">The MeetingHud instance.</param>
|
||||
public class VotingCompleteEvent(MeetingHud meetingHud) : MiraEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the MeetingHud instance.
|
||||
/// </summary>
|
||||
public MeetingHud MeetingHud { get; } = meetingHud;
|
||||
}
|
||||
@@ -15,21 +15,14 @@ public class PlayerDeathEvent : MiraEvent
|
||||
/// </summary>
|
||||
public DeathReason DeathReason { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the dead body associated with the player, if any.
|
||||
/// </summary>
|
||||
public DeadBody? DeadBody { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PlayerDeathEvent"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player who died.</param>
|
||||
/// <param name="reason">The reason the player died.</param>
|
||||
/// <param name="deadBody">The player's body, if it exists.</param>
|
||||
public PlayerDeathEvent(PlayerControl player, DeathReason reason, DeadBody? deadBody)
|
||||
public PlayerDeathEvent(PlayerControl player, DeathReason reason)
|
||||
{
|
||||
Player = player;
|
||||
DeathReason = reason;
|
||||
DeadBody = deadBody;
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
[*.cs]
|
||||
dotnet_diagnostic.CS1591.severity = none
|
||||
@@ -21,7 +21,7 @@ public static class CustomGameModeManager
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current gamemode.
|
||||
/// Current gamemode.
|
||||
/// </summary>
|
||||
public static CustomGameMode? ActiveMode { get; internal set; } = new DefaultMode();
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.GameOptions;
|
||||
@@ -12,11 +11,6 @@ public abstract class AbstractOptionGroup
|
||||
{
|
||||
internal List<IModdedOption> Options { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of the options which are a part of this group.
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<IModdedOption> Children => new(Options);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the group. Visible in options menu.
|
||||
/// </summary>
|
||||
|
||||
@@ -6,100 +6,18 @@ using UnityEngine;
|
||||
|
||||
namespace MiraAPI.GameOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for modded options.
|
||||
/// </summary>
|
||||
public interface IModdedOption
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the unique identifier for the option.
|
||||
/// </summary>
|
||||
uint Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the title of the option.
|
||||
/// </summary>
|
||||
string Title { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the StringName for the option, used for localization.
|
||||
/// </summary>
|
||||
StringNames StringName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the MiraPlugin that created this option.
|
||||
/// </summary>
|
||||
IMiraPlugin? ParentMod { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the game setting data for the option.
|
||||
/// </summary>
|
||||
BaseGameSetting Data { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the OptionBehaviour object of the option.
|
||||
/// </summary>
|
||||
OptionBehaviour? OptionBehaviour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the visibility function for the option.
|
||||
/// </summary>
|
||||
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>
|
||||
/// Gets or sets the ConfigDefinition for the option, used for BepInEx configuration.
|
||||
/// </summary>
|
||||
ConfigDefinition? ConfigDefinition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates the option behaviour for the modded option.
|
||||
/// </summary>
|
||||
/// <param name="toggleOpt">The ToggleOption template.</param>
|
||||
/// <param name="numberOpt">The NumberOption template.</param>
|
||||
/// <param name="stringOpt">The StringOption template.</param>
|
||||
/// <param name="container">>The Transform container for the option.</param>
|
||||
/// <returns>>The created OptionBehaviour object.</returns>
|
||||
OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value as a float.
|
||||
/// </summary>
|
||||
/// <returns>>The value of the option as a float.</returns>
|
||||
float GetFloatData();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the NetData for the option, used for network synchronization.
|
||||
/// </summary>
|
||||
/// <returns>>Returns the NetData object for the option.</returns>
|
||||
NetData GetNetData();
|
||||
|
||||
/// <summary>
|
||||
/// Handles incoming network data for the option.
|
||||
/// </summary>
|
||||
/// <param name="data">>The byte array representing the network data.</param>
|
||||
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);
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using BepInEx.Configuration;
|
||||
@@ -43,12 +42,10 @@ public static class ModdedOptionsManager
|
||||
|
||||
Groups.Add(group);
|
||||
TypeToGroup.Add(type, group);
|
||||
pluginInfo.InternalOptionGroups.Add(group);
|
||||
pluginInfo.OptionGroups.Add(group);
|
||||
|
||||
typeof(OptionGroupSingleton<>).MakeGenericType(type)
|
||||
#pragma warning disable S3011
|
||||
.GetField("_instance", BindingFlags.Static | BindingFlags.NonPublic)!
|
||||
#pragma warning restore S3011
|
||||
.SetValue(null, group);
|
||||
|
||||
return true;
|
||||
@@ -122,7 +119,7 @@ public static class ModdedOptionsManager
|
||||
option.ConfigDefinition = new ConfigDefinition(groupName, propertyName);
|
||||
|
||||
option.ParentMod = pluginInfo.MiraPlugin;
|
||||
pluginInfo.InternalOptions.Add(option);
|
||||
pluginInfo.Options.Add(option);
|
||||
ModdedOptions.Add(option.Id, option);
|
||||
group.Options.Add(option);
|
||||
}
|
||||
@@ -143,7 +140,7 @@ public static class ModdedOptionsManager
|
||||
// we dont know how other plugins handle their configs
|
||||
// this way, all the options are saved at once, instead of one by one
|
||||
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);
|
||||
plugin.PluginConfig.SaveOnConfigSet = false;
|
||||
@@ -159,7 +156,7 @@ public static class ModdedOptionsManager
|
||||
option.HandleNetData(netData.Data);
|
||||
}
|
||||
|
||||
foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins)
|
||||
foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins())
|
||||
{
|
||||
plugin.PluginConfig.Save();
|
||||
plugin.PluginConfig.SaveOnConfigSet = oldConfigSetting[plugin];
|
||||
@@ -176,10 +173,7 @@ public static class ModdedOptionsManager
|
||||
/// </summary>
|
||||
/// <param name="__originalMethod">The original setter method.</param>
|
||||
/// <param name="value">The new object value.</param>
|
||||
#pragma warning disable CA1707
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Harmony naming convention")]
|
||||
public static void PropertySetterPatch(MethodBase __originalMethod, object value)
|
||||
#pragma warning restore CA1707
|
||||
{
|
||||
var attribute = OptionAttributes.First(pair => pair.Key.GetSetMethod() == __originalMethod).Value;
|
||||
attribute.SetValue(value);
|
||||
@@ -191,10 +185,7 @@ public static class ModdedOptionsManager
|
||||
/// <param name="__originalMethod">The original getter method.</param>
|
||||
/// <param name="__result">The result of the property getter.</param>
|
||||
/// <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)
|
||||
#pragma warning restore CA1707
|
||||
{
|
||||
var attribute = OptionAttributes.First(pair => pair.Key.GetGetMethod() == __originalMethod).Value;
|
||||
__result = attribute.GetValue();
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
|
||||
namespace MiraAPI.GameOptions;
|
||||
|
||||
#pragma warning disable CA1852
|
||||
internal class ModifierOptionGroup : AbstractOptionGroup
|
||||
#pragma warning restore CA1852
|
||||
{
|
||||
public override string GroupName { get; }
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ public class ModdedEnumOption : ModdedOption<int>
|
||||
/// <summary>
|
||||
/// Gets the string values of the enum.
|
||||
/// </summary>
|
||||
public string[] Values { get; }
|
||||
public string[]? Values { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 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="enumType">The Enum type.</param>
|
||||
/// <param name="values">An option list of string values to use in place of the enum name.</param>
|
||||
/// <param name="includeInPreset">Whether to include this option in the preset or not.</param>
|
||||
public ModdedEnumOption(string title, int defaultValue, Type enumType, string[]? values = null, bool includeInPreset=true) : base(title, defaultValue, includeInPreset)
|
||||
public ModdedEnumOption(string title, int defaultValue, Type enumType, string[]? values = null) : base(title, defaultValue)
|
||||
{
|
||||
Values = values ?? Enum.GetNames(enumType);
|
||||
Data = ScriptableObject.CreateInstance<StringGameSetting>();
|
||||
@@ -38,7 +37,7 @@ public class ModdedEnumOption : ModdedOption<int>
|
||||
data.Type = global::OptionTypes.String;
|
||||
data.Values = values is null ?
|
||||
Enum.GetNames(enumType).Select(CustomStringName.CreateAndRegister).ToArray()
|
||||
: [.. values.Select(CustomStringName.CreateAndRegister)];
|
||||
: values.Select(CustomStringName.CreateAndRegister).ToArray();
|
||||
|
||||
data.Index = Value;
|
||||
}
|
||||
@@ -88,7 +87,7 @@ public class ModdedEnumOption : ModdedOption<int>
|
||||
/// <inheritdoc />
|
||||
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)
|
||||
{
|
||||
return;
|
||||
@@ -110,7 +109,7 @@ public class ModdedEnumOption<T> : ModdedOption<T> where T : Enum
|
||||
/// <summary>
|
||||
/// Gets the string values of the enum.
|
||||
/// </summary>
|
||||
public string[] Values { get; }
|
||||
public string[]? Values { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 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="defaultValue">The default value as an int.</param>
|
||||
/// <param name="values">An option list of string values to use in place of the enum name.</param>
|
||||
/// <param name="includeInPreset">Whether to include this option in the preset or not.</param>
|
||||
public ModdedEnumOption(string title, T defaultValue, string[]? values = null, bool includeInPreset=true) : base(title, defaultValue, includeInPreset)
|
||||
public ModdedEnumOption(string title, T defaultValue, string[]? values = null) : base(title, defaultValue)
|
||||
{
|
||||
Values = values ?? Enum.GetNames(typeof(T));
|
||||
Data = ScriptableObject.CreateInstance<StringGameSetting>();
|
||||
@@ -129,9 +127,9 @@ public class ModdedEnumOption<T> : ModdedOption<T> where T : Enum
|
||||
data.Type = global::OptionTypes.String;
|
||||
data.Values = values is null ?
|
||||
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);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -48,7 +48,6 @@ public class ModdedNumberOption : ModdedOption<float>
|
||||
/// <param name="suffixType">The suffix type.</param>
|
||||
/// <param name="formatString">Optional format string for the option screen.</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(
|
||||
string title,
|
||||
float defaultValue,
|
||||
@@ -57,8 +56,7 @@ public class ModdedNumberOption : ModdedOption<float>
|
||||
float increment,
|
||||
MiraNumberSuffixes suffixType,
|
||||
string? formatString = null,
|
||||
bool zeroInfinity = false,
|
||||
bool includeInPreset = true) : base(title, defaultValue, includeInPreset)
|
||||
bool zeroInfinity = false) : base(title, defaultValue)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
|
||||
@@ -4,7 +4,6 @@ using MiraAPI.Networking;
|
||||
using MiraAPI.PluginLoading;
|
||||
using Reactor.Localization.Utilities;
|
||||
using Reactor.Networking.Rpc;
|
||||
using Reactor.Utilities;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.GameOptions.OptionTypes;
|
||||
@@ -17,19 +16,29 @@ public abstract class ModdedOption<T> : IModdedOption
|
||||
{
|
||||
private IMiraPlugin? _parentMod;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Gets the unique identifier of the option.
|
||||
/// </summary>
|
||||
public uint Id { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Gets the title of the option.
|
||||
/// </summary>
|
||||
public string Title { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Gets the StringName object of the option.
|
||||
/// </summary>
|
||||
public StringNames StringName { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public BaseGameSetting Data { get; protected set; } = null!;
|
||||
/// <summary>
|
||||
/// Gets the BaseGameSetting data of the option.
|
||||
/// </summary>
|
||||
public BaseGameSetting Data { get; protected init; }
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Gets or sets the parent mod of the option.
|
||||
/// </summary>
|
||||
public IMiraPlugin? ParentMod
|
||||
{
|
||||
get => _parentMod;
|
||||
@@ -57,16 +66,19 @@ public abstract class ModdedOption<T> : IModdedOption
|
||||
/// </summary>
|
||||
public Action<T>? ChangedEvent { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Gets or sets the visibility of the option.
|
||||
/// </summary>
|
||||
public Func<bool> Visible { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IncludeInPreset { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Gets or sets the option behaviour of the option.
|
||||
/// </summary>
|
||||
public OptionBehaviour? OptionBehaviour { get; protected set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Gets or sets the config definition of the option.
|
||||
/// </summary>
|
||||
public ConfigDefinition? ConfigDefinition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -74,8 +86,7 @@ public abstract class ModdedOption<T> : IModdedOption
|
||||
/// </summary>
|
||||
/// <param name="title">The option title.</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, bool includeInPreset = true)
|
||||
protected ModdedOption(string title, T defaultValue)
|
||||
{
|
||||
Id = ModdedOptionsManager.NextId;
|
||||
Title = title;
|
||||
@@ -83,7 +94,6 @@ public abstract class ModdedOption<T> : IModdedOption
|
||||
Value = defaultValue;
|
||||
StringName = CustomStringName.CreateAndRegister(Title);
|
||||
Visible = () => true;
|
||||
IncludeInPreset = includeInPreset;
|
||||
}
|
||||
|
||||
internal void ValueChanged(OptionBehaviour optionBehaviour)
|
||||
@@ -95,8 +105,7 @@ public abstract class ModdedOption<T> : IModdedOption
|
||||
/// Sets the value of the option.
|
||||
/// </summary>
|
||||
/// <param name="newValue">The new value.</param>
|
||||
/// <param name="sendRpc">Whether to send the value to other players.</param>
|
||||
public void SetValue(T newValue, bool sendRpc = true)
|
||||
public void SetValue(T newValue)
|
||||
{
|
||||
var oldVal = Value;
|
||||
Value = newValue;
|
||||
@@ -106,7 +115,7 @@ public abstract class ModdedOption<T> : IModdedOption
|
||||
ChangedEvent?.Invoke(Value);
|
||||
}
|
||||
|
||||
if (sendRpc && AmongUsClient.Instance.AmHost)
|
||||
if (AmongUsClient.Instance.AmHost)
|
||||
{
|
||||
if (ParentMod?.GetConfigFile().TryGetEntry<T>(ConfigDefinition, out var entry) == true)
|
||||
{
|
||||
@@ -119,44 +128,22 @@ public abstract class ModdedOption<T> : IModdedOption
|
||||
OnValueChanged(newValue);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SaveToPreset(ConfigFile presetConfig, bool saveDefault=false)
|
||||
{
|
||||
if (ConfigDefinition is null)
|
||||
{
|
||||
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 />
|
||||
/// <summary>
|
||||
/// Gets the float data of the option.
|
||||
/// </summary>
|
||||
/// <returns>A float object representing the option's value.</returns>
|
||||
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();
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Handles incoming net data.
|
||||
/// </summary>
|
||||
/// <param name="data">The NetData's byte array.</param>
|
||||
public abstract void HandleNetData(byte[] data);
|
||||
|
||||
/// <summary>
|
||||
@@ -172,7 +159,14 @@ public abstract class ModdedOption<T> : IModdedOption
|
||||
/// <returns>The value.</returns>
|
||||
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(
|
||||
ToggleOption toggleOpt,
|
||||
NumberOption numberOpt,
|
||||
|
||||
@@ -15,8 +15,7 @@ public class ModdedToggleOption : ModdedOption<bool>
|
||||
/// </summary>
|
||||
/// <param name="title">The option title.</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, bool includeInPreset=true) : base(title, defaultValue, includeInPreset)
|
||||
public ModdedToggleOption(string title, bool defaultValue) : base(title, defaultValue)
|
||||
{
|
||||
Data = ScriptableObject.CreateInstance<CheckboxGameSetting>();
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Globalization;
|
||||
using MiraAPI.Events;
|
||||
using MiraAPI.Events.Mira;
|
||||
using MiraAPI.Patches;
|
||||
using MiraAPI.Utilities;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
@@ -37,11 +36,6 @@ public abstract class CustomActionButton
|
||||
/// </summary>
|
||||
public abstract LoadableAsset<Sprite> Sprite { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the format string for the cooldown timer.
|
||||
/// </summary>
|
||||
public virtual string CooldownTimerFormatString => "0";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the button's effect duration in seconds. If the button has no effect, set to 0.
|
||||
/// </summary>
|
||||
@@ -186,7 +180,7 @@ public abstract class CustomActionButton
|
||||
|
||||
if (!moveButton) return;
|
||||
|
||||
if (HudManagerPatches.BottomLeft == null || HudManagerPatches.BottomRight == null)
|
||||
if (HudManagerPatches._bottomLeft == null || HudManagerPatches._bottomRight == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -194,17 +188,17 @@ public abstract class CustomActionButton
|
||||
switch (location)
|
||||
{
|
||||
case ButtonLocation.BottomLeft:
|
||||
var gridArrange = HudManagerPatches.BottomLeft.GetComponent<GridArrange>();
|
||||
var aspectPosition = HudManagerPatches.BottomLeft.GetComponent<AspectPosition>();
|
||||
var gridArrange = HudManagerPatches._bottomLeft.GetComponent<GridArrange>();
|
||||
var aspectPosition = HudManagerPatches._bottomLeft.GetComponent<AspectPosition>();
|
||||
|
||||
Button.transform.SetParent(HudManagerPatches.BottomLeft.transform);
|
||||
Button.transform.SetParent(HudManagerPatches._bottomLeft.transform);
|
||||
|
||||
gridArrange.Start();
|
||||
gridArrange.ArrangeChilds();
|
||||
aspectPosition.AdjustPosition();
|
||||
break;
|
||||
case ButtonLocation.BottomRight:
|
||||
Button.transform.SetParent(HudManagerPatches.BottomRight);
|
||||
Button.transform.SetParent(HudManagerPatches._bottomRight);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -423,12 +417,9 @@ public abstract class CustomActionButton
|
||||
/// <param name="playerControl">The local PlayerControl.</param>
|
||||
public virtual void FixedUpdateHandler(PlayerControl playerControl)
|
||||
{
|
||||
if (Timer >= 0)
|
||||
if (Timer >= 0 && !TimerPaused)
|
||||
{
|
||||
if (!TimerPaused)
|
||||
{
|
||||
Timer -= Time.deltaTime;
|
||||
}
|
||||
Timer -= Time.deltaTime;
|
||||
}
|
||||
else if (HasEffect && EffectActive)
|
||||
{
|
||||
@@ -437,28 +428,25 @@ public abstract class CustomActionButton
|
||||
OnEffectEnd();
|
||||
}
|
||||
|
||||
if (Button)
|
||||
if (CanUse())
|
||||
{
|
||||
if (CanUse())
|
||||
{
|
||||
Button!.SetEnabled();
|
||||
}
|
||||
else
|
||||
{
|
||||
Button!.SetDisabled();
|
||||
}
|
||||
Button?.SetEnabled();
|
||||
}
|
||||
else
|
||||
{
|
||||
Button?.SetDisabled();
|
||||
}
|
||||
|
||||
if (EffectActive)
|
||||
{
|
||||
Button.SetFillUp(Timer, EffectDuration);
|
||||
if (EffectActive)
|
||||
{
|
||||
Button?.SetFillUp(Timer, EffectDuration);
|
||||
|
||||
Button.cooldownTimerText.text = Timer.ToString(CooldownTimerFormatString, NumberFormatInfo.InvariantInfo);
|
||||
Button.cooldownTimerText.gameObject.SetActive(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Button.SetCooldownFormat(Timer, Cooldown, CooldownTimerFormatString);
|
||||
}
|
||||
Button!.cooldownTimerText.text = Mathf.CeilToInt(Timer).ToString(NumberFormatInfo.InvariantInfo);
|
||||
Button!.cooldownTimerText.gameObject.SetActive(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Button?.SetCoolDown(Timer, Cooldown);
|
||||
}
|
||||
|
||||
FixedUpdate(playerControl);
|
||||
|
||||
@@ -29,11 +29,9 @@ public static class CustomButtonManager
|
||||
}
|
||||
|
||||
CustomButtons.Add(button);
|
||||
pluginInfo.InternalButtons.Add(button);
|
||||
pluginInfo.Buttons.Add(button);
|
||||
typeof(CustomButtonSingleton<>).MakeGenericType(buttonType)
|
||||
#pragma warning disable S3011
|
||||
.GetField("_instance", BindingFlags.Static | BindingFlags.NonPublic)!
|
||||
#pragma warning restore S3011
|
||||
.SetValue(null, button);
|
||||
|
||||
ButtonEventTypes.Add(buttonType, typeof(MiraButtonClickEvent<>).MakeGenericType(buttonType));
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
|
||||
namespace MiraAPI.Hud;
|
||||
|
||||
@@ -14,6 +13,7 @@ public static class CustomButtonSingleton<T> where T : CustomActionButton
|
||||
/// <summary>
|
||||
/// Gets the instance of the button.
|
||||
/// </summary>
|
||||
[SuppressMessage("Design", "CA1000:Do not declare static members on generic types", Justification = "This is a utility class to get the instance of a custom action button.")]
|
||||
#pragma warning disable CA1000
|
||||
public static T Instance => _instance ??= CustomButtonManager.CustomButtons.OfType<T>().Single();
|
||||
#pragma warning restore CA1000
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using AmongUs.GameOptions;
|
||||
using Il2CppInterop.Runtime.Attributes;
|
||||
@@ -9,8 +8,6 @@ using Reactor.Utilities.Attributes;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
|
||||
|
||||
namespace MiraAPI.Hud;
|
||||
|
||||
@@ -19,18 +16,22 @@ namespace MiraAPI.Hud;
|
||||
/// </summary>
|
||||
/// <param name="il2CppPtr">Used by Il2Cpp. Do not use constructor, this is a MonoBehaviour.</param>
|
||||
[RegisterInIl2Cpp]
|
||||
[SuppressMessage("Design", "CA1051:Do not declare visible instance fields", Justification = "Unity Convention")]
|
||||
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Unity Convention")]
|
||||
[SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "Unity Convention")]
|
||||
public class CustomPlayerMenu(IntPtr il2CppPtr) : Minigame(il2CppPtr)
|
||||
{
|
||||
public ShapeshifterPanel panelPrefab;
|
||||
public float xStart = -0.8f;
|
||||
public float yStart = 2.15f;
|
||||
public float xOffset = 1.95f;
|
||||
public float yOffset = -0.65f;
|
||||
public UiElement backButton;
|
||||
public UiElement defaultButtonSelected;
|
||||
public ShapeshifterPanel PanelPrefab;
|
||||
|
||||
public float XStart = -0.8f;
|
||||
|
||||
public float YStart = 2.15f;
|
||||
|
||||
public float XOffset = 1.95f;
|
||||
|
||||
public float YOffset = -0.65f;
|
||||
|
||||
public UiElement BackButton;
|
||||
|
||||
public UiElement DefaultButtonSelected;
|
||||
|
||||
public List<ShapeshifterPanel> potentialVictims;
|
||||
|
||||
/// <summary>
|
||||
@@ -41,17 +42,17 @@ public class CustomPlayerMenu(IntPtr il2CppPtr) : Minigame(il2CppPtr)
|
||||
{
|
||||
var shapeShifterRole = RoleManager.Instance.GetRole(RoleTypes.Shapeshifter);
|
||||
|
||||
var ogMenu = shapeShifterRole.TryCast<ShapeshifterRole>()!.ShapeshifterMenu;
|
||||
var ogMenu = shapeShifterRole.TryCast<ShapeshifterRole>().ShapeshifterMenu;
|
||||
var newMenu = Instantiate(ogMenu);
|
||||
var customMenu = newMenu.gameObject.AddComponent<CustomPlayerMenu>();
|
||||
|
||||
customMenu.panelPrefab = newMenu.PanelPrefab;
|
||||
customMenu.xStart = newMenu.XStart;
|
||||
customMenu.yStart = newMenu.YStart;
|
||||
customMenu.xOffset = newMenu.XOffset;
|
||||
customMenu.yOffset = newMenu.YOffset;
|
||||
customMenu.backButton = newMenu.BackButton;
|
||||
var back = customMenu.backButton.GetComponent<PassiveButton>();
|
||||
customMenu.PanelPrefab = newMenu.PanelPrefab;
|
||||
customMenu.XStart = newMenu.XStart;
|
||||
customMenu.YStart = newMenu.YStart;
|
||||
customMenu.XOffset = newMenu.XOffset;
|
||||
customMenu.YOffset = newMenu.YOffset;
|
||||
customMenu.BackButton = newMenu.BackButton;
|
||||
var back = customMenu.BackButton.GetComponent<PassiveButton>();
|
||||
back.OnClick.RemoveAllListeners();
|
||||
back.OnClick.AddListener((UnityAction)(() =>
|
||||
{
|
||||
@@ -64,7 +65,7 @@ public class CustomPlayerMenu(IntPtr il2CppPtr) : Minigame(il2CppPtr)
|
||||
|
||||
newMenu.DestroyImmediate();
|
||||
|
||||
customMenu.transform.SetParent(Camera.main!.transform, false);
|
||||
customMenu.transform.SetParent(Camera.main.transform, false);
|
||||
customMenu.transform.localPosition = new Vector3(0f, 0f, -50f);
|
||||
return customMenu;
|
||||
}
|
||||
@@ -74,7 +75,6 @@ public class CustomPlayerMenu(IntPtr il2CppPtr) : Minigame(il2CppPtr)
|
||||
ControllerManager.Instance.CloseOverlayMenu(name);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Begin(PlayerTask task)
|
||||
{
|
||||
throw new NotImplementedException("Use the other Begin method.");
|
||||
@@ -90,7 +90,7 @@ public class CustomPlayerMenu(IntPtr il2CppPtr) : Minigame(il2CppPtr)
|
||||
{
|
||||
MinigameStubs.Begin(this, null);
|
||||
|
||||
var back = backButton.GetComponent<PassiveButton>();
|
||||
var back = BackButton.GetComponent<PassiveButton>();
|
||||
back.OnClick.AddListener((UnityAction)(() =>
|
||||
{
|
||||
onClick(null);
|
||||
@@ -107,13 +107,13 @@ public class CustomPlayerMenu(IntPtr il2CppPtr) : Minigame(il2CppPtr)
|
||||
var num = i % 3;
|
||||
var num2 = i / 3;
|
||||
var flag = PlayerControl.LocalPlayer.Data.Role.NameColor == player.Data.Role.NameColor;
|
||||
var shapeshifterPanel = Instantiate(panelPrefab, transform);
|
||||
shapeshifterPanel.transform.localPosition = new Vector3(xStart + num * xOffset, yStart + num2 * yOffset, -1f);
|
||||
var shapeshifterPanel = Instantiate(PanelPrefab, transform);
|
||||
shapeshifterPanel.transform.localPosition = new Vector3(XStart + num * XOffset, YStart + num2 * YOffset, -1f);
|
||||
shapeshifterPanel.SetPlayer(i, player.Data, (Il2CppSystem.Action)(() => { onClick(player); }));
|
||||
shapeshifterPanel.NameText.color = flag ? player.Data.Role.NameColor : Color.white;
|
||||
potentialVictims.Add(shapeshifterPanel);
|
||||
list2.Add(shapeshifterPanel.Button);
|
||||
}
|
||||
ControllerManager.Instance.OpenOverlayMenu(name, backButton, defaultButtonSelected, list2);
|
||||
ControllerManager.Instance.OpenOverlayMenu(name, BackButton, DefaultButtonSelected, list2);
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
using System;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using Reactor.Utilities.Attributes;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace MiraAPI.Hud;
|
||||
|
||||
/// <summary>
|
||||
/// Simple input box for user input.
|
||||
/// </summary>
|
||||
[RegisterInIl2Cpp]
|
||||
public class InputBox : MonoBehaviour
|
||||
{
|
||||
public InputField inputField = null!;
|
||||
|
||||
public void CreateDialog(string title, string placeholder, string submitText, Action<string> onSubmit)
|
||||
{
|
||||
var canvasObj = new GameObject("DialogCanvas");
|
||||
canvasObj.transform.SetParent(transform);
|
||||
canvasObj.layer = LayerMask.NameToLayer("UI");
|
||||
|
||||
var canvas = canvasObj.AddComponent<Canvas>();
|
||||
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
||||
canvas.overrideSorting = true;
|
||||
canvas.sortingOrder = 1000; // Ensure the canvas is on top of other UI elements
|
||||
|
||||
canvasObj.AddComponent<CanvasScaler>().uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
|
||||
canvasObj.AddComponent<GraphicRaycaster>();
|
||||
|
||||
var panelObj = new GameObject("DialogPanel");
|
||||
panelObj.transform.SetParent(canvasObj.transform);
|
||||
var panelRect = panelObj.AddComponent<RectTransform>();
|
||||
var img = panelObj.AddComponent<Image>();
|
||||
|
||||
img.color = new Color(1, 1, 1, 0.9f);
|
||||
img.raycastTarget = true;
|
||||
img.sprite = MiraAssets.RoundedBox.LoadAsset();
|
||||
img.type = Image.Type.Sliced;
|
||||
|
||||
panelRect.sizeDelta = new Vector2(300, 200);
|
||||
panelRect.anchoredPosition = Vector2.zero;
|
||||
|
||||
var titleText = CreateText(panelObj, title);
|
||||
titleText.alignment = TextAnchor.MiddleCenter;
|
||||
titleText.fontSize = 26;
|
||||
titleText.fontStyle = FontStyle.Bold;
|
||||
titleText.color = Color.white;
|
||||
|
||||
var titleRect = titleText.GetComponent<RectTransform>();
|
||||
titleRect.sizeDelta = new Vector2(280, 50);
|
||||
titleRect.anchoredPosition = new Vector2(0, 70);
|
||||
|
||||
var inputObj = new GameObject("InputField");
|
||||
inputObj.transform.SetParent(panelObj.transform);
|
||||
var inputRect = inputObj.AddComponent<RectTransform>();
|
||||
inputRect.sizeDelta = new Vector2(220, 40);
|
||||
inputRect.anchoredPosition = new Vector2(0, 20);
|
||||
|
||||
var inputBg = inputObj.AddComponent<Image>();
|
||||
inputBg.color = new Color(1, 1, 1, 0.8f);
|
||||
|
||||
inputField = inputObj.AddComponent<InputField>();
|
||||
inputField.textComponent = CreateText(inputObj, string.Empty, 18, Color.black);
|
||||
inputField.placeholder = CreateText(inputObj, placeholder, 18, new Color(0.5f, 0.5f, 0.5f));
|
||||
|
||||
var buttonObj = new GameObject("SubmitButton");
|
||||
buttonObj.transform.SetParent(panelObj.transform);
|
||||
var button = buttonObj.AddComponent<Button>();
|
||||
var buttonRect = buttonObj.AddComponent<RectTransform>();
|
||||
buttonRect.sizeDelta = new Vector2(150, 50);
|
||||
buttonRect.anchoredPosition = new Vector2(0, -50);
|
||||
|
||||
var buttonImg = buttonObj.AddComponent<Image>();
|
||||
buttonImg.color = new Color(1f, 1f, 1f, 1f);
|
||||
buttonImg.sprite = MiraAssets.RoundedBox.LoadAsset();
|
||||
buttonImg.type = Image.Type.Sliced;
|
||||
|
||||
var buttonText = CreateText(buttonObj, submitText, 20, Color.white);
|
||||
buttonText.alignment = TextAnchor.MiddleCenter;
|
||||
|
||||
var colors = button.colors;
|
||||
colors.highlightedColor = Color.green;
|
||||
colors.pressedColor = MiraAssets.AcceptedTeal;
|
||||
button.colors = colors;
|
||||
|
||||
button.onClick.AddListener((UnityAction)(() =>
|
||||
{
|
||||
onSubmit(inputField.text);
|
||||
DestroyImmediate(canvasObj);
|
||||
}));
|
||||
|
||||
inputField.ActivateInputField();
|
||||
}
|
||||
|
||||
private static Text CreateText(GameObject parent, string textContent = "", int fontSize = 20, Color? textColor = null, Font? font = null)
|
||||
{
|
||||
var textObj = new GameObject("Text");
|
||||
textObj.transform.SetParent(parent.transform);
|
||||
|
||||
var text = textObj.AddComponent<Text>();
|
||||
text.font = font ?? Resources.GetBuiltinResource<Font>("Arial.ttf");
|
||||
text.text = textContent;
|
||||
text.color = textColor ?? Color.white;
|
||||
text.fontSize = fontSize;
|
||||
text.alignment = TextAnchor.MiddleCenter;
|
||||
|
||||
var rect = textObj.GetComponent<RectTransform>();
|
||||
rect.sizeDelta = new Vector2(200, 40);
|
||||
rect.anchoredPosition = Vector2.zero;
|
||||
return text;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,19 @@
|
||||
using BepInEx;
|
||||
using System;
|
||||
using BepInEx;
|
||||
using BepInEx.Unity.IL2CPP;
|
||||
using HarmonyLib;
|
||||
using Il2CppInterop.Runtime;
|
||||
using MiraAPI.PluginLoading;
|
||||
using MiraAPI.Roles;
|
||||
using MonoMod.Utils;
|
||||
using Reactor;
|
||||
using Reactor.Networking;
|
||||
using Reactor.Networking.Attributes;
|
||||
using Reactor.Utilities;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.ProBuilder;
|
||||
using Delegate = Il2CppSystem.Delegate;
|
||||
|
||||
namespace MiraAPI;
|
||||
|
||||
@@ -16,20 +23,11 @@ namespace MiraAPI;
|
||||
[BepInAutoPlugin("mira.api", "MiraAPI")]
|
||||
[BepInProcess("Among Us.exe")]
|
||||
[BepInDependency(ReactorPlugin.Id)]
|
||||
[BepInDependency(ModCompatibility.SubmergedId, BepInDependency.DependencyFlags.SoftDependency)]
|
||||
[ReactorModFlags(ModFlags.RequireOnAllClients)]
|
||||
public partial class MiraApiPlugin : BasePlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the branding Mira API color.
|
||||
/// </summary>
|
||||
public static Color MiraColor { get; } = new Color32(238, 154, 112, 255);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default color for headers in the options menu.
|
||||
/// </summary>
|
||||
public static Color DefaultHeaderColor { get; } = new Color32(77, 77, 77, 255);
|
||||
|
||||
public static Color DefaultHeaderColor = new Color32(77, 77, 77, 255);
|
||||
private static MiraPluginManager? PluginManager { get; set; }
|
||||
internal Harmony Harmony { get; } = new(Id);
|
||||
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
using BepInEx.Unity.IL2CPP;
|
||||
|
||||
namespace MiraAPI;
|
||||
|
||||
/// <summary>
|
||||
/// Mod compatibility tools.
|
||||
/// </summary>
|
||||
public static class ModCompatibility
|
||||
{
|
||||
/// <summary>
|
||||
/// The ID for the Submerged mod.
|
||||
/// </summary>
|
||||
public const string SubmergedId = "Submerged";
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the Submerged mod is loaded.
|
||||
/// </summary>
|
||||
/// <param name="submergedAssembly">Submerged mod assembly if loaded, null otherwise.</param>
|
||||
/// <returns>True if the Submerged mod is loaded, false otherwise.</returns>
|
||||
public static bool SubmergedLoaded([NotNullWhen(true)] out Assembly? submergedAssembly)
|
||||
{
|
||||
var result = IL2CPPChainloader.Instance.Plugins.TryGetValue(SubmergedId, out var plugin);
|
||||
submergedAssembly = result ? plugin?.Instance.GetType().Assembly : null;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
using System;
|
||||
using MiraAPI.GameOptions;
|
||||
using MiraAPI.PluginLoading;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Modifiers;
|
||||
|
||||
@@ -40,8 +38,8 @@ public abstract class BaseModifier : IOptionable
|
||||
/// Gets the parent mod of the modifier.
|
||||
/// </summary>
|
||||
public MiraPluginInfo ParentMod => Array.Find(
|
||||
MiraPluginManager.Instance.RegisteredPlugins,
|
||||
x => x.InternalModifiers.Exists(y => y.TypeId == TypeId)
|
||||
MiraPluginManager.Instance.RegisteredPlugins(),
|
||||
x => x.Modifiers.Exists(y => y.TypeId == TypeId)
|
||||
) ?? throw new InvalidOperationException("Modifier is not registered.");
|
||||
|
||||
/// <summary>
|
||||
@@ -50,14 +48,9 @@ public abstract class BaseModifier : IOptionable
|
||||
public abstract string ModifierName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the modifier icon. Useless if HideOnUi is true.
|
||||
/// Gets a value indicating whether the modifier is hidden on the UI.
|
||||
/// </summary>
|
||||
public virtual LoadableAsset<Sprite>? ModifierIcon => null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the modifier is hidden on the UI. Will be hidden either way if no description is provided.
|
||||
/// </summary>
|
||||
public virtual bool HideOnUi => GetDescription() == string.Empty;
|
||||
public virtual bool HideOnUi => false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the modifier is shown in the freeplay menu.
|
||||
@@ -70,10 +63,10 @@ public abstract class BaseModifier : IOptionable
|
||||
public virtual bool Unique => true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the HUD description for this modifier. Does nothing if <see cref="HideOnUi"/> is true. Required to be visible on UI.
|
||||
/// Gets the HUD information for this modifier. Defaults to the modifier name. Does nothing if <see cref="HideOnUi"/> is true.
|
||||
/// </summary>
|
||||
/// <returns>The description string for the HUD.</returns>
|
||||
public virtual string GetDescription() => string.Empty;
|
||||
/// <returns>The information string for the HUD.</returns>
|
||||
public virtual string GetHudString() => ModifierName;
|
||||
|
||||
/// <summary>
|
||||
/// Called when the modifier is activated.
|
||||
@@ -111,13 +104,6 @@ public abstract class BaseModifier : IOptionable
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a meeting starts.
|
||||
/// </summary>
|
||||
public virtual void OnMeetingStart()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the player can vent.
|
||||
/// </summary>
|
||||
|
||||
@@ -3,11 +3,13 @@ using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Il2CppInterop.Runtime.Attributes;
|
||||
using MiraAPI.Modifiers.ModifierDisplay;
|
||||
using MiraAPI.Modifiers.Types;
|
||||
using MiraAPI.Utilities;
|
||||
using Reactor.Utilities;
|
||||
using Reactor.Utilities.Attributes;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Modifiers;
|
||||
@@ -24,12 +26,12 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
|
||||
[HideFromIl2Cpp]
|
||||
public ImmutableList<BaseModifier> ActiveModifiers { get; private set; } = ImmutableList<BaseModifier>.Empty;
|
||||
|
||||
private ModifierDisplayComponent? ModifierDisplay { get; set; }
|
||||
|
||||
[HideFromIl2Cpp]
|
||||
private List<BaseModifier> Modifiers { get; set; } = [];
|
||||
|
||||
private PlayerControl _player = null!;
|
||||
private PlayerControl? _player;
|
||||
|
||||
private TextMeshPro? _modifierText;
|
||||
|
||||
private readonly List<BaseModifier> _toRemove = [];
|
||||
|
||||
@@ -40,18 +42,9 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
|
||||
_toRemove.AddRange(Modifiers);
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
private void Start()
|
||||
{
|
||||
_player = GetComponent<PlayerControl>();
|
||||
if (_player == null)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error("ModifierComponent could not find PlayerControl on the same GameObject.");
|
||||
Destroy(this);
|
||||
return;
|
||||
}
|
||||
|
||||
ModifierExtensions.ModifierComponents.Add(_player, this);
|
||||
|
||||
Modifiers = [];
|
||||
|
||||
if (!_player.AmOwner)
|
||||
@@ -59,42 +52,23 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
|
||||
return;
|
||||
}
|
||||
|
||||
ModifierDisplay = ModifierDisplayComponent.CreateDisplay();
|
||||
ModifierDisplay?.UpdateModifiersList(Modifiers);
|
||||
_modifierText = Helpers.CreateTextLabel("ModifierText", HudManager.Instance.transform, AspectPosition.EdgeAlignments.RightTop, new Vector3(10.1f, 3.5f, 0), textAlignment: TextAlignmentOptions.Right);
|
||||
_modifierText.verticalAlignment = VerticalAlignmentOptions.Top;
|
||||
}
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
var removed = _toRemove.Count > 0;
|
||||
foreach (var modifier in _toRemove.ToArray())
|
||||
{
|
||||
_toRemove.Remove(modifier);
|
||||
try
|
||||
{
|
||||
modifier.OnDeactivate();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Error while deactivating modifier {modifier.ModifierName}: {e.ToString()}");
|
||||
}
|
||||
|
||||
modifier.OnDeactivate();
|
||||
Modifiers.Remove(modifier);
|
||||
}
|
||||
|
||||
var added = _toAdd.Count > 0;
|
||||
foreach (var modifier in _toAdd.ToArray())
|
||||
{
|
||||
_toAdd.Remove(modifier);
|
||||
Modifiers.Add(modifier);
|
||||
modifier.Initialized = true;
|
||||
try
|
||||
{
|
||||
modifier.OnActivate();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Error while activating modifier {modifier.ModifierName}: {e.ToString()}");
|
||||
}
|
||||
modifier.OnActivate();
|
||||
|
||||
if (modifier is TimedModifier { AutoStart: true } timer)
|
||||
{
|
||||
@@ -102,31 +76,21 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
|
||||
}
|
||||
}
|
||||
|
||||
if (removed || added)
|
||||
if (_toAdd.Count > 0 || _toRemove.Count > 0)
|
||||
{
|
||||
if (_player.AmOwner && HudManager.InstanceExists)
|
||||
if (_player?.AmOwner == true)
|
||||
{
|
||||
ModifierDisplay?.UpdateModifiersList(Modifiers);
|
||||
HudManager.Instance?.SetHudActive(_player, _player.Data.Role, HudManager.Instance.TaskPanel.isActiveAndEnabled);
|
||||
}
|
||||
_toAdd.Clear();
|
||||
_toRemove.Clear();
|
||||
|
||||
ActiveModifiers = Modifiers.ToImmutableList();
|
||||
}
|
||||
|
||||
foreach (var modifier in ActiveModifiers)
|
||||
{
|
||||
try
|
||||
{
|
||||
modifier.FixedUpdate();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Error while (fixed) updating modifier {modifier.ModifierName}: {e.ToString()}");
|
||||
}
|
||||
}
|
||||
|
||||
if (_player.AmOwner && ModifierDisplay && HudManager.InstanceExists)
|
||||
{
|
||||
ModifierDisplay!.gameObject.SetActive(MeetingHud.Instance || HudManager.Instance.TaskPanel.isActiveAndEnabled);
|
||||
modifier.FixedUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,21 +98,34 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
|
||||
{
|
||||
foreach (var modifier in ActiveModifiers)
|
||||
{
|
||||
try
|
||||
{
|
||||
modifier.Update();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Error while updating modifier {modifier.ModifierName}: {e.ToString()}");
|
||||
}
|
||||
modifier.Update();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
// Remove this component from the global list when destroyed
|
||||
ModifierExtensions.ModifierComponents.Remove(_player);
|
||||
if (!_modifierText || _player?.AmOwner == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (MeetingHud.Instance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var showText = false;
|
||||
var builder = new StringBuilder("<b><size=130%>Modifiers:</b></size>\n");
|
||||
|
||||
foreach (var modifier in ActiveModifiers)
|
||||
{
|
||||
if (modifier.HideOnUi)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
showText = true;
|
||||
builder.AppendLine(modifier.GetHudString());
|
||||
}
|
||||
|
||||
_modifierText!.text = showText ? builder.ToString() : string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -515,61 +492,4 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
|
||||
{
|
||||
return ActiveModifiers.Exists(x => x.UniqueId == id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a player has an active modifier by its type.
|
||||
/// </summary>
|
||||
/// <param name="checkInactive">Whether to check inactive modifiers (those pending to be added).</param>
|
||||
/// <param name="predicate">The predicate to check the modifier.</param>
|
||||
/// <typeparam name="T">The Type of the Modifier.</typeparam>
|
||||
/// <returns>True if the Modifier is present, false otherwise.</returns>
|
||||
[HideFromIl2Cpp]
|
||||
public bool HasModifier<T>(bool checkInactive, Func<T, bool>? predicate=null) where T : BaseModifier
|
||||
{
|
||||
return ActiveModifiers.Exists(MatchExpr) || (checkInactive && _toAdd.Exists(MatchExpr));
|
||||
bool MatchExpr(BaseModifier bm) => bm is T modifier && (predicate == null || predicate(modifier));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a player has an active modifier by its type.
|
||||
/// </summary>
|
||||
/// <param name="type">The modifier type.</param>
|
||||
/// <param name="checkInactive">Whether to check inactive modifiers (those pending to be added).</param>
|
||||
/// <param name="predicate">The predicate to check the modifier.</param>
|
||||
/// <returns>True if the Modifier is present, false otherwise.</returns>
|
||||
[HideFromIl2Cpp]
|
||||
public bool HasModifier(Type type, bool checkInactive, Func<BaseModifier, bool>? predicate=null)
|
||||
{
|
||||
return ActiveModifiers.Exists(MatchExpr) || (checkInactive && _toAdd.Exists(MatchExpr));
|
||||
bool MatchExpr(BaseModifier bm) => bm.GetType() == type && (predicate == null || predicate(bm));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a player has an active modifier by its type ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The modifier's type ID.</param>
|
||||
/// <param name="checkInactive">Whether to check inactive modifiers (those pending to be added).</param>
|
||||
/// <param name="predicate">The predicate to check the modifier.</param>
|
||||
/// <returns>True if the modifier is present, false otherwise.</returns>
|
||||
[HideFromIl2Cpp]
|
||||
public bool HasModifier(uint id, bool checkInactive, Func<BaseModifier, bool>? predicate=null)
|
||||
{
|
||||
var type = ModifierManager.GetModifierType(id) ?? throw new InvalidOperationException(
|
||||
$"Cannot get modifier with id {id} because it is not registered.");
|
||||
|
||||
return HasModifier(type, checkInactive, predicate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a player has an active modifier by its unique ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The modifier's guid.</param>
|
||||
/// <param name="checkInactive">Whether to check inactive modifiers (those pending to be added).</param>
|
||||
/// <returns>True if the modifier is present, false otherwise.</returns>
|
||||
[HideFromIl2Cpp]
|
||||
public bool HasModifier(Guid id, bool checkInactive)
|
||||
{
|
||||
return ActiveModifiers.Exists(MatchExpr) || (checkInactive && _toAdd.Exists(MatchExpr));
|
||||
bool MatchExpr(BaseModifier bm) => bm.UniqueId == id;
|
||||
}
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using HarmonyLib;
|
||||
using Il2CppInterop.Runtime.Attributes;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using Reactor.Utilities.Attributes;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace MiraAPI.Modifiers.ModifierDisplay;
|
||||
|
||||
/// <summary>
|
||||
/// The code used to display Mira modifiers.
|
||||
/// </summary>
|
||||
[RegisterInIl2Cpp]
|
||||
public class ModifierDisplayComponent(nint cppPtr) : MonoBehaviour(cppPtr)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the instance of the Modifier Display.
|
||||
/// </summary>
|
||||
public static ModifierDisplayComponent? Instance { get; private set; }
|
||||
|
||||
private RectTransform _children = null!;
|
||||
private GameObject _modTemplate = null!;
|
||||
private PassiveButton _toggleButton = null!;
|
||||
private TextMeshPro _toggleBtnText = null!;
|
||||
|
||||
private RectTransform _pagination = null!;
|
||||
private TextMeshPro _pageText = null!;
|
||||
private PassiveButton _nextButton = null!;
|
||||
private PassiveButton _backButton = null!;
|
||||
|
||||
private int _currentPage;
|
||||
private const int ItemsPerPage = 3;
|
||||
|
||||
private readonly Dictionary<BaseModifier, ModifierUiComponent> _modifiers = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read only dictionary of the created modifier UI components.
|
||||
/// </summary>
|
||||
[HideFromIl2Cpp]
|
||||
public ReadOnlyDictionary<BaseModifier, ModifierUiComponent> Modifiers => new(_modifiers);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the modifier display hud is currently open.
|
||||
/// </summary>
|
||||
public bool IsOpen { get; private set; }
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
Instance = this;
|
||||
|
||||
_children = transform.GetChild(0).GetComponent<RectTransform>();
|
||||
_modTemplate = transform.GetChild(2).gameObject;
|
||||
|
||||
_toggleButton = transform.GetChild(1).GetComponent<PassiveButton>();
|
||||
_toggleButton.OnClick = new Button.ButtonClickedEvent();
|
||||
_toggleButton.OnClick.AddListener((UnityAction)(() =>
|
||||
{
|
||||
IsOpen = !IsOpen;
|
||||
_children.gameObject.SetActive(!_children.gameObject.activeSelf);
|
||||
}));
|
||||
|
||||
_toggleButton.ClickSound = HudManager.Instance.GameMenu.StreamerModeButton.GetComponent<PassiveButton>().ClickSound;
|
||||
_toggleButton.HoverSound = HudManager.Instance.GameMenu.StreamerModeButton.GetComponent<PassiveButton>().HoverSound;
|
||||
_toggleButton.inactiveSprites = _toggleButton.transform.GetChild(1).gameObject;
|
||||
_toggleBtnText = _toggleButton.transform.GetChild(0).GetComponent<TextMeshPro>();
|
||||
|
||||
_pagination = _children.transform.GetChild(0).GetComponent<RectTransform>();
|
||||
_pageText = _pagination.transform.GetChild(0).GetComponent<TextMeshPro>();
|
||||
_nextButton = _pagination.transform.GetChild(1).GetComponent<PassiveButton>();
|
||||
_backButton = _pagination.transform.GetChild(2).GetComponent<PassiveButton>();
|
||||
|
||||
_toggleBtnText.font = _pageText.font = _nextButton.buttonText.font = _backButton.buttonText.font = HudManager.Instance.TaskPanel.taskText.font;
|
||||
_toggleBtnText.fontMaterial = _pageText.fontMaterial = _nextButton.buttonText.fontMaterial = _backButton.buttonText.fontMaterial = HudManager.Instance.TaskPanel.taskText.fontMaterial;
|
||||
|
||||
_nextButton.OnClick = new Button.ButtonClickedEvent();
|
||||
_nextButton.OnClick.AddListener((UnityAction)(() =>
|
||||
{
|
||||
_currentPage++;
|
||||
UpdatePage();
|
||||
}));
|
||||
|
||||
_backButton.OnClick = new Button.ButtonClickedEvent();
|
||||
_backButton.OnClick.AddListener((UnityAction)(() =>
|
||||
{
|
||||
_currentPage--;
|
||||
UpdatePage();
|
||||
}));
|
||||
|
||||
IsOpen = false;
|
||||
_children.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use this to refresh the modifiers UI. Useful if you hide modifiers uis based on options.
|
||||
/// </summary>
|
||||
public void RefreshModifiers()
|
||||
{
|
||||
UpdateModifiersList([.. PlayerControl.LocalPlayer.GetModifierComponent().ActiveModifiers]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use this to toggle the visibility of the modifiers tab.
|
||||
/// </summary>
|
||||
public void ToggleTab()
|
||||
{
|
||||
IsOpen = !IsOpen;
|
||||
_children.gameObject.SetActive(IsOpen);
|
||||
RefreshModifiers();
|
||||
}
|
||||
|
||||
internal void DestroyComponent(ModifierUiComponent component)
|
||||
{
|
||||
_modifiers.Remove(component.Modifier!);
|
||||
component.gameObject.DestroyImmediate();
|
||||
|
||||
RefreshModifiers();
|
||||
}
|
||||
|
||||
[HideFromIl2Cpp]
|
||||
internal static ModifierDisplayComponent CreateDisplay()
|
||||
{
|
||||
var gameObject = Instantiate(MiraAssets.ModifierDisplay.LoadAsset(), HudManager.Instance.transform);
|
||||
var minigame = gameObject.AddComponent<ModifierDisplayComponent>();
|
||||
return minigame;
|
||||
}
|
||||
|
||||
[HideFromIl2Cpp]
|
||||
private ModifierUiComponent CreateForModifier(BaseModifier modifier)
|
||||
{
|
||||
var newMod = Instantiate(_modTemplate, _children.transform);
|
||||
newMod.transform.name = modifier.ModifierName;
|
||||
var comp = newMod.AddComponent<ModifierUiComponent>();
|
||||
comp.Modifier = modifier;
|
||||
return comp;
|
||||
}
|
||||
|
||||
private void UpdatePage()
|
||||
{
|
||||
var totalPages = Mathf.CeilToInt((float)_modifiers.Count / ItemsPerPage);
|
||||
_currentPage = Mathf.Clamp(_currentPage, 0, Mathf.Max(0, totalPages - 1));
|
||||
|
||||
if (_modifiers.Count <= ItemsPerPage)
|
||||
{
|
||||
_modifiers.Do(x => x.Value.gameObject.SetActive(true));
|
||||
_pagination.gameObject.SetActive(false);
|
||||
return;
|
||||
}
|
||||
|
||||
_pagination.gameObject.SetActive(true);
|
||||
if (_pageText != null)
|
||||
{
|
||||
_pageText.text = $"Page {_currentPage + 1}/{Mathf.Max(totalPages, 1)}";
|
||||
}
|
||||
|
||||
_nextButton.gameObject.SetActive(true);
|
||||
_backButton.gameObject.SetActive(true);
|
||||
|
||||
_modifiers.Do(x => x.Value.gameObject.SetActive(false));
|
||||
var start = _currentPage * ItemsPerPage;
|
||||
var end = Mathf.Min(start + ItemsPerPage, _modifiers.Count);
|
||||
var modifiers = _modifiers.ToArray();
|
||||
|
||||
for (var i = start; i < end; i++)
|
||||
{
|
||||
modifiers[i].Value.gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
_pagination.SetAsLastSibling();
|
||||
}
|
||||
|
||||
[HideFromIl2Cpp]
|
||||
internal void UpdateModifiersList(List<BaseModifier> modifiers)
|
||||
{
|
||||
var filteredModifiers = modifiers.Where(x => !x.HideOnUi && x.GetDescription() != string.Empty).ToList();
|
||||
var modifiersToAdd = filteredModifiers.ToList();
|
||||
|
||||
foreach (var mod in _modifiers.ToArray())
|
||||
{
|
||||
if (modifiersToAdd.Contains(mod.Key))
|
||||
{
|
||||
modifiersToAdd.Remove(mod.Key);
|
||||
continue;
|
||||
}
|
||||
|
||||
mod.Value.gameObject.Destroy();
|
||||
_modifiers.Remove(mod.Key);
|
||||
}
|
||||
|
||||
_toggleBtnText.text = $"Modifiers ({filteredModifiers.Count})";
|
||||
|
||||
if (filteredModifiers.Count == 0)
|
||||
{
|
||||
_toggleButton.gameObject.SetActive(false);
|
||||
return;
|
||||
}
|
||||
|
||||
_toggleButton.gameObject.SetActive(true);
|
||||
|
||||
foreach (var mod in modifiersToAdd)
|
||||
{
|
||||
_modifiers.Add(mod, CreateForModifier(mod));
|
||||
}
|
||||
|
||||
UpdatePage();
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
using Il2CppInterop.Runtime.Attributes;
|
||||
using Reactor.Utilities.Attributes;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Modifiers.ModifierDisplay;
|
||||
|
||||
/// <summary>
|
||||
/// The code placed on every Modifier HUD object. Used to handle updating.
|
||||
/// </summary>
|
||||
[RegisterInIl2Cpp]
|
||||
public class ModifierUiComponent(nint cppPtr) : MonoBehaviour(cppPtr)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the modifier which this component is for.
|
||||
/// </summary>
|
||||
[HideFromIl2Cpp]
|
||||
public BaseModifier? Modifier { get; internal set; }
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
private SpriteRenderer modBg = null!;
|
||||
private TextMeshPro desc = null!;
|
||||
private SpriteRenderer icon = null!;
|
||||
private TextMeshPro nameText = null!;
|
||||
private RectTransform descRect = null!;
|
||||
// ReSharper restore InconsistentNaming
|
||||
private void Awake()
|
||||
{
|
||||
if (Modifier == null) return;
|
||||
|
||||
modBg = transform.GetChild(0).GetComponent<SpriteRenderer>();
|
||||
desc = modBg.transform.GetChild(0).GetComponent<TextMeshPro>();
|
||||
icon = modBg.transform.GetChild(1).GetComponent<SpriteRenderer>();
|
||||
var nameBg = transform.GetChild(1);
|
||||
nameText = nameBg.GetChild(0).GetComponent<TextMeshPro>();
|
||||
descRect = desc.gameObject.GetComponent<RectTransform>();
|
||||
|
||||
nameText.font = desc.font = HudManager.Instance.TaskPanel.taskText.font;
|
||||
nameText.fontMaterial = desc.fontMaterial = HudManager.Instance.TaskPanel.taskText.fontMaterial;
|
||||
}
|
||||
|
||||
// Might not be the best solution, but used to update name/description/icon if updated in the modifier.
|
||||
private void FixedUpdate()
|
||||
{
|
||||
if (Modifier == null || ModifierDisplayComponent.Instance == null || !ModifierDisplayComponent.Instance.IsOpen) return;
|
||||
|
||||
if (Modifier.HideOnUi || Modifier.GetDescription() == string.Empty)
|
||||
{
|
||||
ModifierDisplayComponent.Instance.DestroyComponent(this);
|
||||
return;
|
||||
}
|
||||
|
||||
nameText.text = Modifier.ModifierName;
|
||||
desc.text = Modifier.GetDescription();
|
||||
modBg.gameObject.SetActive(true);
|
||||
|
||||
icon.gameObject.SetActive(Modifier.ModifierIcon != null);
|
||||
if (icon.gameObject && icon.gameObject.activeSelf && Modifier.ModifierIcon != null)
|
||||
{
|
||||
icon.sprite = Modifier.ModifierIcon?.LoadAsset();
|
||||
icon.size = new Vector2(0.7f, 0.7f);
|
||||
icon.tileMode = SpriteTileMode.Continuous;
|
||||
descRect.localPosition = new Vector3(0.38f, -0.1f, -5);
|
||||
descRect.sizeDelta = new Vector2(2.38f, 0.65f);
|
||||
}
|
||||
else
|
||||
{
|
||||
descRect.localPosition = new Vector3(0, -0.1f, -5);
|
||||
descRect.sizeDelta = new Vector2(3.1f, 0.65f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ public static class ModifierExtensions
|
||||
_ = ModifierManager.GetModifierType(typeId) ?? throw new InvalidOperationException(
|
||||
$"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>
|
||||
@@ -63,7 +63,7 @@ public static class ModifierExtensions
|
||||
/// </summary>
|
||||
/// <param name="target">The player to remove the modifier from.</param>
|
||||
/// <param name="uniqueId">The unique ID of the modifier.</param>
|
||||
[MethodRpc((uint)MiraRpc.RemoveModifier, SendImmediately = true)]
|
||||
[MethodRpc((uint)MiraRpc.RemoveModifier)]
|
||||
public static void RpcRemoveModifier(this PlayerControl target, Guid uniqueId)
|
||||
{
|
||||
target.RemoveModifier(uniqueId);
|
||||
@@ -180,17 +180,6 @@ public static class ModifierExtensions
|
||||
return player.GetModifierComponent().HasModifier(typeId, predicate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the player has a specific modifier by its GUID.
|
||||
/// </summary>
|
||||
/// <param name="player">The PlayerControl instance.</param>
|
||||
/// <param name="uniqueId">The unique ID of the modifier.</param>
|
||||
/// <returns>True if the player has the modifier, false otherwise.</returns>
|
||||
public static bool HasModifier(this PlayerControl player, Guid uniqueId)
|
||||
{
|
||||
return player.GetModifierComponent().HasModifier(uniqueId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all modifiers from a player.
|
||||
/// </summary>
|
||||
@@ -215,6 +204,17 @@ public static class ModifierExtensions
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the player has a specific modifier by its GUID.
|
||||
/// </summary>
|
||||
/// <param name="player">The PlayerControl instance.</param>
|
||||
/// <param name="uniqueId">The unique ID of the modifier.</param>
|
||||
/// <returns>True if the player has the modifier, false otherwise.</returns>
|
||||
public static bool HasModifier(this PlayerControl player, Guid uniqueId)
|
||||
{
|
||||
return player.GetModifierComponent().HasModifier(uniqueId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get a modifier by its type.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using MiraAPI.Utilities;
|
||||
@@ -12,7 +11,7 @@ namespace MiraAPI.Modifiers;
|
||||
/// </summary>
|
||||
public static class ModifierFactory
|
||||
{
|
||||
private static readonly Dictionary<(Type, Type[]), Func<object[], BaseModifier>> ConstructorCache = [];
|
||||
private static readonly Dictionary<(Type, Type[]), Func<object[], BaseModifier>> _constructorCache = [];
|
||||
|
||||
private static Func<object[], BaseModifier> CreateConstructor(Type type, params object[] args)
|
||||
{
|
||||
@@ -46,13 +45,13 @@ public static class ModifierFactory
|
||||
/// <returns>An instance of the modifier.</returns>
|
||||
public static BaseModifier CreateInstance(Type type, params object[] args)
|
||||
{
|
||||
var argTypes = args.Select(arg => arg.GetType()).ToArray();
|
||||
var argTypes = args.Select(arg => arg?.GetType() ?? typeof(object)).ToArray();
|
||||
var key = (type, argTypes);
|
||||
|
||||
if (!ConstructorCache.TryGetValue(key, out var constructor))
|
||||
if (!_constructorCache.TryGetValue(key, out var constructor))
|
||||
{
|
||||
constructor = CreateConstructor(type, args);
|
||||
ConstructorCache[key] = constructor;
|
||||
_constructorCache[key] = constructor;
|
||||
}
|
||||
|
||||
return constructor(args);
|
||||
@@ -96,7 +95,6 @@ public static class ModifierFactory<T> where T : BaseModifier
|
||||
/// </summary>
|
||||
/// <param name="args">Parameters for the constructor.</param>
|
||||
/// <returns>An instance of the modifier.</returns>
|
||||
[SuppressMessage("Design", "CA1000:Do not declare static members on generic types", Justification = "This is a factory class.")]
|
||||
public static T CreateInstance(params object[] args)
|
||||
{
|
||||
return Constructor(args);
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Runtime.Serialization;
|
||||
using MiraAPI.Modifiers.Types;
|
||||
using MiraAPI.PluginLoading;
|
||||
using MiraAPI.Roles;
|
||||
using MiraAPI.Utilities;
|
||||
using Reactor.Utilities;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using Random = System.Random;
|
||||
@@ -74,7 +75,7 @@ public static class ModifierManager
|
||||
modifier = (BaseModifier)FormatterServices.GetUninitializedObject(modifierType);
|
||||
}
|
||||
|
||||
info.InternalModifiers.Add(modifier);
|
||||
info.Modifiers.Add(modifier);
|
||||
|
||||
if (modifier is not GameModifier gameModifier)
|
||||
{
|
||||
@@ -102,57 +103,68 @@ public static class ModifierManager
|
||||
{
|
||||
var rand = new Random();
|
||||
|
||||
// Filter and sort modifiers by descending priority.
|
||||
var modifiers = IdToTypeModifierMap
|
||||
.Where(x=>x.Value.IsAssignableTo(typeof(GameModifier)))
|
||||
.Select(x => Activator.CreateInstance(x.Value) as GameModifier)
|
||||
.OfType<GameModifier>()
|
||||
.Where(x => x.GetAmountPerGame() > 0 && x.GetAssignmentChance() > 0)
|
||||
.OrderByDescending(x => x.Priority())
|
||||
.ToArray();
|
||||
|
||||
foreach (var modifier in modifiers)
|
||||
foreach (var priority in PrioritiesToIdsMap.Keys.OrderByDescending(x => x))
|
||||
{
|
||||
var assignments = modifier.GetAmountPerGame();
|
||||
var filteredModifiers = new List<uint>();
|
||||
|
||||
var validPlayers = plrs.Where(x => IsGameModifierValid(x, modifier, modifier.TypeId)).ToList();
|
||||
if (validPlayers.Count == 0)
|
||||
foreach (var id in PrioritiesToIdsMap[priority])
|
||||
{
|
||||
Logger<MiraApiPlugin>.Warning($"No valid players for modifier {modifier.ModifierName}");
|
||||
var mod = Activator.CreateInstance(IdToTypeModifierMap[id]) as GameModifier;
|
||||
var chance = Math.Clamp(mod!.GetAssignmentChance(), 0, 100);
|
||||
var count = mod.GetAmountPerGame();
|
||||
|
||||
if (chance == 0 || count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var maxCount = plrs.Count(x => IsGameModifierValid(x, mod, id));
|
||||
|
||||
if (maxCount == 0)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Warning($"No players are valid for {mod.ModifierName}");
|
||||
continue;
|
||||
}
|
||||
|
||||
var num = Math.Clamp(count, 0, maxCount);
|
||||
|
||||
for (var i = 0; i < num; i++)
|
||||
{
|
||||
var randomNum = rand.Next(100);
|
||||
|
||||
if (randomNum < chance)
|
||||
{
|
||||
filteredModifiers.Add(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (filteredModifiers.Count == 0)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Warning($"No filtered modifiers for priority {priority}");
|
||||
continue;
|
||||
}
|
||||
|
||||
assignments = Math.Min(assignments, validPlayers.Count);
|
||||
var availablePlayers = new List<PlayerControl>(validPlayers);
|
||||
var shuffledList = filteredModifiers.Randomize();
|
||||
|
||||
for (var i = 0; i < assignments; i++)
|
||||
if (shuffledList.Count > plrs.Count)
|
||||
{
|
||||
var chance = Math.Clamp(modifier.GetAssignmentChance(), 0, 100);
|
||||
if (rand.Next(100) >= chance)
|
||||
shuffledList = shuffledList.GetRange(0, plrs.Count);
|
||||
}
|
||||
|
||||
foreach (var id in shuffledList)
|
||||
{
|
||||
var mod = Activator.CreateInstance(IdToTypeModifierMap[id]) as GameModifier;
|
||||
var plr = plrs.Where(x => IsGameModifierValid(x, mod!, id)).Random();
|
||||
|
||||
if (!plr || plr == null)
|
||||
{
|
||||
continue;
|
||||
Logger<MiraApiPlugin>.Warning($"Somehow valid players for modifier {mod!.ModifierName} disappeared");
|
||||
}
|
||||
|
||||
var candidates = availablePlayers
|
||||
.Where(x => IsGameModifierValid(x, modifier, modifier.TypeId))
|
||||
.ToList();
|
||||
|
||||
if (candidates.Count == 0)
|
||||
else
|
||||
{
|
||||
Logger<MiraApiPlugin>.Warning(
|
||||
$"No available players for modifier {modifier.ModifierName} at assignment {i + 1}");
|
||||
break;
|
||||
plr.RpcAddModifier(id);
|
||||
}
|
||||
|
||||
var plr = candidates.Random();
|
||||
if (plr == null)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Warning($"Valid player for modifier {modifier.ModifierName} disappeared");
|
||||
continue;
|
||||
}
|
||||
|
||||
plr.RpcAddModifier(modifier.TypeId);
|
||||
availablePlayers.Remove(plr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ public abstract class GameModifier : BaseModifier
|
||||
public virtual bool IsModifierValidOn(RoleBehaviour role) => true;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the player won the game with this modifier. The modifier with the highest priority will override other modifiers.
|
||||
/// Determines whether the player won the game with this modifier.
|
||||
/// </summary>
|
||||
/// <param name="reason">The reason why the game ended.</param>
|
||||
/// <returns>True if the player won, false if they lost. Return null to use the player's default win condition.</returns>
|
||||
|
||||
@@ -45,12 +45,12 @@ public abstract class TimedModifier : BaseModifier
|
||||
public override bool HideOnUi => !TimerActive;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the HUD description for Timed Modifier, including the time remaining.
|
||||
/// Gets the HUD information for Timed Modifier, including the time remaining.
|
||||
/// </summary>
|
||||
/// <returns>A string with the time remaining.</returns>
|
||||
public override string GetDescription()
|
||||
/// <returns>A string with the hud information + timer.</returns>
|
||||
public override string GetHudString()
|
||||
{
|
||||
return $"Remaining Time: {Math.Round(Duration - TimeRemaining, 0)}s/{Duration}s";
|
||||
return base.GetHudString() + $" <size=70%>({Math.Round(Duration - TimeRemaining, 0)}s/{Duration}s)</size>";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using AmongUs.Data;
|
||||
using AmongUs.GameOptions;
|
||||
using Assets.CoreScripts;
|
||||
using BepInEx.Unity.IL2CPP.Utils;
|
||||
@@ -97,7 +96,7 @@ public static class CustomMurderRpc
|
||||
var flag = PlayerControl.LocalPlayer.Data.Role.Role == RoleTypes.GuardianAngel;
|
||||
if (flag && PlayerControl.LocalPlayer.Data.PlayerId == target.protectedByGuardianId)
|
||||
{
|
||||
DataManager.Player.Stats.IncrementStat(StatID.Role_GuardianAngel_CrewmatesProtected);
|
||||
StatsManager.Instance.IncrementStat(StringNames.StatsGuardianAngelCrewmatesProtected);
|
||||
AchievementManager.Instance.OnProtectACrewmate();
|
||||
}
|
||||
|
||||
@@ -128,14 +127,14 @@ public static class CustomMurderRpc
|
||||
DebugAnalytics.Instance.Analytics.Kill(target.Data, source.Data);
|
||||
if (source.AmOwner)
|
||||
{
|
||||
DataManager.Player.Stats.IncrementStat(
|
||||
StatsManager.Instance.IncrementStat(
|
||||
GameManager.Instance.IsHideAndSeek()
|
||||
? StatID.HideAndSeek_ImpostorKills
|
||||
: StatID.ImpostorKills);
|
||||
? StringNames.StatsImpostorKills_HideAndSeek
|
||||
: StringNames.StatsImpostorKills);
|
||||
|
||||
if (source.CurrentOutfitType == PlayerOutfitType.Shapeshifted)
|
||||
{
|
||||
DataManager.Player.Stats.IncrementStat(StatID.Role_Shapeshifter_ShiftedKills);
|
||||
StatsManager.Instance.IncrementStat(StringNames.StatsShapeshifterShiftedKills);
|
||||
}
|
||||
|
||||
if (Constants.ShouldPlaySfx() && playKillSound)
|
||||
@@ -153,7 +152,7 @@ public static class CustomMurderRpc
|
||||
target.gameObject.layer = LayerMask.NameToLayer("Ghost");
|
||||
if (target.AmOwner)
|
||||
{
|
||||
DataManager.Player.Stats.IncrementStat(StatID.TimesMurdered);
|
||||
StatsManager.Instance.IncrementStat(StringNames.StatsTimesMurdered);
|
||||
if (Minigame.Instance)
|
||||
{
|
||||
try
|
||||
@@ -264,7 +263,7 @@ public static class CustomMurderRpc
|
||||
deadBody.enabled = true;
|
||||
}
|
||||
|
||||
var afterMurderEvent = new AfterMurderEvent(source, target, deadBody);
|
||||
var afterMurderEvent = new AfterMurderEvent(source, target);
|
||||
MiraEventManager.InvokeEvent(afterMurderEvent);
|
||||
|
||||
if (!isParticipant)
|
||||
|
||||
@@ -44,9 +44,4 @@ public enum MiraRpc : uint
|
||||
/// Custom RPC to undo a player's vote.
|
||||
/// </summary>
|
||||
RemoveVote,
|
||||
|
||||
/// <summary>
|
||||
/// Custom RPC to replace Among Us CastVote.
|
||||
/// </summary>
|
||||
CastVote,
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
[*.{cs,vb}]
|
||||
dotnet_diagnostic.CA1707.severity = none
|
||||
dotnet_diagnostic.CS1591.severity = none
|
||||
dotnet_diagnostic.SA1611.severity = none
|
||||
dotnet_diagnostic.SA1615.severity = none
|
||||
@@ -5,44 +5,29 @@ using MiraAPI.GameEnd;
|
||||
|
||||
namespace MiraAPI.Patches.Events;
|
||||
|
||||
[HarmonyPatch(typeof(EndGameManager), nameof(EndGameManager.SetEverythingUp))]
|
||||
internal static class EndGamePatches
|
||||
{
|
||||
[HarmonyPatch(typeof(GameManager), nameof(GameManager.RpcEndGame))]
|
||||
public static class GameManagerPatches
|
||||
public static bool Prefix(EndGameManager __instance)
|
||||
{
|
||||
public static bool Prefix(GameOverReason endReason)
|
||||
if (CustomGameOver.Instance is not { } gameOver)
|
||||
{
|
||||
var @event = new BeforeGameEndEvent(endReason);
|
||||
MiraEventManager.InvokeEvent(@event);
|
||||
|
||||
return !@event.IsCancelled;
|
||||
return true;
|
||||
}
|
||||
|
||||
var result = gameOver.BeforeEndGameSetup(__instance);
|
||||
return result;
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(EndGameManager), nameof(EndGameManager.SetEverythingUp))]
|
||||
public static class EndGameManagerPatches
|
||||
public static void Postfix(EndGameManager __instance)
|
||||
{
|
||||
public static bool Prefix(EndGameManager __instance)
|
||||
if (CustomGameOver.Instance is { } gameOver)
|
||||
{
|
||||
if (CustomGameOver.Instance is not { } gameOver)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var result = gameOver.BeforeEndGameSetup(__instance);
|
||||
return result;
|
||||
gameOver.AfterEndGameSetup(__instance);
|
||||
CustomGameOver.Instance = null;
|
||||
}
|
||||
|
||||
public static void Postfix(EndGameManager __instance)
|
||||
{
|
||||
if (CustomGameOver.Instance is { } gameOver)
|
||||
{
|
||||
gameOver.AfterEndGameSetup(__instance);
|
||||
CustomGameOver.Instance = null;
|
||||
}
|
||||
|
||||
var @event = new GameEndEvent(__instance);
|
||||
MiraEventManager.InvokeEvent(@event);
|
||||
}
|
||||
var @event = new GameEndEvent(__instance);
|
||||
MiraEventManager.InvokeEvent(@event);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Events;
|
||||
using MiraAPI.Events.Vanilla.Gameplay;
|
||||
using MiraAPI.Utilities;
|
||||
|
||||
namespace MiraAPI.Patches.Events;
|
||||
|
||||
@@ -14,17 +13,13 @@ public static class KillAnimationPatches
|
||||
/// <summary>
|
||||
/// Used to trigger the <see cref="AfterMurderEvent"/>.
|
||||
/// </summary>
|
||||
/// <param name="source">The killer.</param>
|
||||
/// <param name="target">The target.</param>
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(KillAnimation._CoPerformKill_d__2), nameof(KillAnimation._CoPerformKill_d__2.MoveNext))]
|
||||
public static void KillAnimPostfix(KillAnimation._CoPerformKill_d__2 __instance)
|
||||
[HarmonyPatch(typeof(KillAnimation), nameof(KillAnimation.CoPerformKill))]
|
||||
public static void KillAnimPostfix(PlayerControl source, PlayerControl target)
|
||||
{
|
||||
// Checking if state is -1 to wait for the end of coroutine.
|
||||
if (__instance.__1__state != -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var afterMurderEvent = new AfterMurderEvent(__instance.source, __instance.target, Helpers.GetBodyById(__instance.target.PlayerId));
|
||||
var afterMurderEvent = new AfterMurderEvent(source, target);
|
||||
MiraEventManager.InvokeEvent(afterMurderEvent);
|
||||
}
|
||||
}
|
||||
@@ -1,52 +1,21 @@
|
||||
using AmongUs.InnerNet.GameDataMessages;
|
||||
using HarmonyLib;
|
||||
using InnerNet;
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Events;
|
||||
using MiraAPI.Events.Vanilla.Meeting;
|
||||
using MiraAPI.Utilities;
|
||||
using Reactor.Utilities;
|
||||
|
||||
namespace MiraAPI.Patches.Events;
|
||||
|
||||
[HarmonyPatch]
|
||||
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 @event = new ReportBodyEvent(source, target, body);
|
||||
var @event = new ReportBodyEvent(__instance, target, body);
|
||||
MiraEventManager.InvokeEvent(@event);
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Events;
|
||||
using MiraAPI.Events.Vanilla.Gameplay;
|
||||
|
||||
namespace MiraAPI.Patches.Events;
|
||||
|
||||
[HarmonyPatch(typeof(SpawnInMinigame), nameof(SpawnInMinigame.Close))]
|
||||
internal static class SpawnInMinigamePatch
|
||||
{
|
||||
public static void Postfix()
|
||||
{
|
||||
var @event = new RoundStartEvent(false);
|
||||
MiraEventManager.InvokeEvent(@event);
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Events;
|
||||
using MiraAPI.Events.Vanilla.Gameplay;
|
||||
|
||||
namespace MiraAPI.Patches.Events;
|
||||
|
||||
[HarmonyPatch]
|
||||
internal static class SubmergedSpawnGamePatch
|
||||
{
|
||||
public static bool Prepare()
|
||||
{
|
||||
return ModCompatibility.SubmergedLoaded(out _);
|
||||
}
|
||||
|
||||
public static IEnumerable<MethodBase> TargetMethods()
|
||||
{
|
||||
if (ModCompatibility.SubmergedLoaded(out var subAss) &&
|
||||
subAss.GetType("Submerged.SpawnIn.SubmarineSelectSpawn") is { } selectSpawn)
|
||||
{
|
||||
yield return AccessTools.Method(selectSpawn, "OnDestroy");
|
||||
}
|
||||
}
|
||||
|
||||
public static void Postfix()
|
||||
{
|
||||
var @event = new RoundStartEvent(false);
|
||||
MiraEventManager.InvokeEvent(@event);
|
||||
}
|
||||
}
|
||||
@@ -23,10 +23,10 @@ public static class VentEventPatches
|
||||
|
||||
[HarmonyPrefix]
|
||||
[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 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);
|
||||
MiraEventManager.InvokeEvent(@event);
|
||||
@@ -40,10 +40,10 @@ public static class VentEventPatches
|
||||
|
||||
[HarmonyPrefix]
|
||||
[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 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);
|
||||
MiraEventManager.InvokeEvent(@event);
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace MiraAPI.Patches.Freeplay;
|
||||
[HarmonyPatch(typeof(TaskAdderGame))]
|
||||
internal static class TaskAdderPatches
|
||||
{
|
||||
private static Scroller _scroller = null!;
|
||||
private static Scroller? _scroller;
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(TaskAdderGame.Begin))]
|
||||
@@ -67,9 +67,9 @@ internal static class TaskAdderPatches
|
||||
modifiersFolder.FolderName = "Modifiers";
|
||||
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);
|
||||
newFolder.name = plugin.PluginId+":Roles";
|
||||
@@ -78,7 +78,7 @@ internal static class TaskAdderPatches
|
||||
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);
|
||||
newFolder.name = plugin.PluginId+":Modifiers";
|
||||
@@ -293,9 +293,9 @@ internal static class TaskAdderPatches
|
||||
chip.GetComponent<SpriteRenderer>().maskInteraction = SpriteMaskInteraction.VisibleInsideMask;
|
||||
}
|
||||
|
||||
if (_scroller)
|
||||
if (_scroller && _scroller != null)
|
||||
{
|
||||
_scroller.CalculateAndSetYBounds(__instance.ActiveItems.Count, 6, 5, 1.4f);
|
||||
_scroller.CalculateAndSetYBounds(__instance.ActiveItems.Count, 6, 3, 1f);
|
||||
_scroller.SetYBoundsMin(0.0f);
|
||||
_scroller.ScrollToTop();
|
||||
}
|
||||
@@ -317,9 +317,9 @@ internal static class TaskAdderPatches
|
||||
ref float num2,
|
||||
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)
|
||||
{
|
||||
continue;
|
||||
@@ -368,9 +368,9 @@ internal static class TaskAdderPatches
|
||||
|
||||
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 ||
|
||||
roleBehaviour is ICustomRole { Configuration.ShowInFreeplay: false })
|
||||
{
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
[*.cs]
|
||||
dotnet_diagnostic.S125.severity = none
|
||||
@@ -4,7 +4,7 @@ using MiraAPI.Hud;
|
||||
namespace MiraAPI.Patches.Hud;
|
||||
|
||||
/// <summary>
|
||||
/// Reset button patches.
|
||||
/// Reset button patches
|
||||
/// </summary>
|
||||
[HarmonyPatch]
|
||||
public static class ButtonResetPatches
|
||||
|
||||
@@ -13,9 +13,9 @@ namespace MiraAPI.Patches;
|
||||
public static class HudManagerPatches
|
||||
{
|
||||
// Custom buttons parent.
|
||||
public static GameObject? BottomLeft { get; private set; }
|
||||
public static Transform? BottomRight { get; private set; }
|
||||
public static Transform? Buttons { get; private set; }
|
||||
internal static GameObject? _bottomLeft;
|
||||
internal static Transform? _bottomRight;
|
||||
internal static Transform? _buttons;
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
@@ -36,30 +36,30 @@ public static class HudManagerPatches
|
||||
[HarmonyPatch(nameof(HudManager.Start))]
|
||||
public static void StartPostfix(HudManager __instance)
|
||||
{
|
||||
if (Buttons == null)
|
||||
if (_buttons == null)
|
||||
{
|
||||
Buttons = __instance.transform.Find("Buttons");
|
||||
_buttons = __instance.transform.Find("Buttons");
|
||||
}
|
||||
|
||||
if (BottomRight == null)
|
||||
if (_bottomRight == null)
|
||||
{
|
||||
BottomRight = Buttons.Find("BottomRight");
|
||||
_bottomRight = _buttons.Find("BottomRight");
|
||||
}
|
||||
|
||||
if (BottomLeft == null)
|
||||
if (_bottomLeft == null)
|
||||
{
|
||||
BottomLeft = Object.Instantiate(BottomRight.gameObject, Buttons);
|
||||
_bottomLeft = Object.Instantiate(_bottomRight.gameObject, _buttons);
|
||||
}
|
||||
|
||||
foreach (var t in BottomLeft.GetComponentsInChildren<ActionButton>(true))
|
||||
foreach (var t in _bottomLeft.GetComponentsInChildren<ActionButton>(true))
|
||||
{
|
||||
t.gameObject.Destroy();
|
||||
}
|
||||
|
||||
var gridArrange = BottomLeft.GetComponent<GridArrange>();
|
||||
var aspectPosition = BottomLeft.GetComponent<AspectPosition>();
|
||||
var gridArrange = _bottomLeft.GetComponent<GridArrange>();
|
||||
var aspectPosition = _bottomLeft.GetComponent<AspectPosition>();
|
||||
|
||||
BottomLeft.name = "BottomLeft";
|
||||
_bottomLeft.name = "BottomLeft";
|
||||
gridArrange.Alignment = GridArrange.StartAlign.Right;
|
||||
aspectPosition.Alignment = AspectPosition.EdgeAlignments.LeftBottom;
|
||||
|
||||
@@ -67,8 +67,8 @@ public static class HudManagerPatches
|
||||
{
|
||||
var location = button.Location switch
|
||||
{
|
||||
ButtonLocation.BottomLeft => BottomLeft.transform,
|
||||
ButtonLocation.BottomRight => BottomRight,
|
||||
ButtonLocation.BottomLeft => _bottomLeft.transform,
|
||||
ButtonLocation.BottomRight => _bottomRight,
|
||||
_ => null,
|
||||
};
|
||||
|
||||
|
||||
@@ -56,11 +56,11 @@ public static class IntroCutscenePatches
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(IntroCutscene.OnDestroy))]
|
||||
public static void GameBeginPatch(IntroCutscene __instance)
|
||||
public static void GameBeginPatch()
|
||||
{
|
||||
MiraEventManager.InvokeEvent(new IntroEndEvent(__instance));
|
||||
MiraEventManager.InvokeEvent(new RoundStartEvent(true));
|
||||
}
|
||||
CustomGameModeManager.ActiveMode?.Initialize();
|
||||
}*/
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using AmongUs.Data;
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Utilities;
|
||||
using Reactor.Utilities;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace MiraAPI.Patches.Menu;
|
||||
|
||||
[HarmonyPatch(typeof(HatsTab))]
|
||||
public static class HatsTabPatches
|
||||
{
|
||||
private static SortedList<string, List<HatData>> sortedHats = [];
|
||||
private static int currentPage;
|
||||
|
||||
[HarmonyPatch(nameof(HatsTab.OnEnable))]
|
||||
[HarmonyPrefix]
|
||||
public static bool OnEnablePrefix(HatsTab __instance)
|
||||
{
|
||||
__instance.currentHat = HatManager.Instance.GetHatById(DataManager.Player.Customization.Hat);
|
||||
var allHats = HatManager.Instance.GetUnlockedHats().ToImmutableList();
|
||||
|
||||
if (sortedHats.Count == 0)
|
||||
{
|
||||
var comparer = new ControllableComparer<string>(["vanilla"], [], StringComparer.InvariantCulture);
|
||||
sortedHats = new SortedList<string, List<HatData>>(comparer);
|
||||
foreach (var hat in allHats)
|
||||
{
|
||||
if (!sortedHats.ContainsKey(hat.StoreName)) sortedHats[hat.StoreName] = [];
|
||||
sortedHats[hat.StoreName].Add(hat);
|
||||
}
|
||||
}
|
||||
|
||||
GenerateHats(__instance, currentPage);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(HatsTab), nameof(HatsTab.Update))]
|
||||
[HarmonyPrefix]
|
||||
|
||||
public static void UpdatePrefix(HatsTab __instance)
|
||||
{
|
||||
if (sortedHats.Count == 0) return;
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.LeftControl) || Input.GetKeyDown(KeyCode.RightControl) || Input.GetKeyDown(KeyCode.LeftArrow))
|
||||
{
|
||||
currentPage--;
|
||||
currentPage = currentPage < 0 ? sortedHats.Count - 1 : currentPage;
|
||||
GenerateHats(__instance, currentPage);
|
||||
}
|
||||
else if (Input.GetKeyDown(KeyCode.Tab) || Input.GetKeyDown(KeyCode.RightArrow))
|
||||
{
|
||||
currentPage++;
|
||||
currentPage = currentPage > sortedHats.Count - 1 ? 0 : currentPage;
|
||||
GenerateHats(__instance, currentPage);
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerator? loadRoutine;
|
||||
private static int hatIndex;
|
||||
|
||||
private static void GenerateHats(HatsTab __instance, int page)
|
||||
{
|
||||
if (loadRoutine != null) Coroutines.Stop(loadRoutine);
|
||||
|
||||
hatIndex = 0;
|
||||
foreach (var instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.Destroy();
|
||||
__instance.ColorChips.Clear();
|
||||
__instance.scroller.Inner.GetComponentsInChildren<TextMeshPro>().Do(x => x.gameObject.Destroy());
|
||||
|
||||
var groupNameText = __instance.GetComponentInChildren<TextMeshPro>(false);
|
||||
|
||||
var (groupName, hats) = sortedHats.ToArray()[page];
|
||||
var text = Object.Instantiate(groupNameText, __instance.scroller.Inner);
|
||||
text.enabled = true;
|
||||
text.gameObject.transform.localScale = Vector3.one;
|
||||
text.GetComponent<TextTranslatorTMP>().Destroy();
|
||||
text.EnableStencilMasking();
|
||||
text.text = $"{groupName}\nPress Ctrl or Tab to cycle pages";
|
||||
text.alignment = TextAlignmentOptions.Center;
|
||||
text.fontSize = 3f;
|
||||
text.fontSizeMax = 3f;
|
||||
text.fontSizeMin = 0f;
|
||||
var xLerp = __instance.XRange.Lerp(0.5f);
|
||||
var yLerp = __instance.YStart - hatIndex / __instance.NumPerRow * __instance.YOffset;
|
||||
text.transform.localPosition = new Vector3(xLerp, yLerp, -1f);
|
||||
|
||||
hatIndex += 5;
|
||||
loadRoutine = Coroutines.Start(CoGenerateChips(__instance, hats));
|
||||
}
|
||||
|
||||
private static IEnumerator CoGenerateChips(HatsTab __instance, List<HatData> hats)
|
||||
{
|
||||
__instance.scroller.ScrollToTop();
|
||||
var batchSize = 5;
|
||||
for (var i = 0; i < hats.Count; i += batchSize)
|
||||
{
|
||||
var batch = hats.Skip(i).Take(batchSize).ToList();
|
||||
|
||||
foreach (var hat in batch.OrderBy(HatManager.Instance.allHats.IndexOf))
|
||||
{
|
||||
var hatXposition = __instance.XRange.Lerp(hatIndex % __instance.NumPerRow / (__instance.NumPerRow - 1f));
|
||||
var hatYposition = __instance.YStart - hatIndex / __instance.NumPerRow * __instance.YOffset;
|
||||
GenerateColorChip(__instance, new Vector2(hatXposition, hatYposition), hat);
|
||||
hatIndex += 1;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
__instance.scroller.ContentYBounds.max = -(__instance.YStart - (hatIndex + 1) / __instance.NumPerRow * __instance.YOffset) - 3f;
|
||||
yield return new WaitForSeconds(0.01f);
|
||||
}
|
||||
__instance.currentHatIsEquipped = true;
|
||||
loadRoutine = null;
|
||||
}
|
||||
|
||||
private static void GenerateColorChip(HatsTab __instance, Vector2 position, HatData hat)
|
||||
{
|
||||
var colorChip = Object.Instantiate(__instance.ColorTabPrefab, __instance.scroller.Inner);
|
||||
colorChip.gameObject.name = hat.ProductId;
|
||||
colorChip.Button.OnClick.AddListener((Action)(() => __instance.ClickEquip()));
|
||||
colorChip.Button.OnMouseOver.AddListener((Action)(() => __instance.SelectHat(hat)));
|
||||
colorChip.Button.OnMouseOut.AddListener((Action)(() => __instance.SelectHat(HatManager.Instance.GetHatById(DataManager.Player.Customization.Hat))));
|
||||
colorChip.Inner.SetHat(hat, __instance.HasLocalPlayer() ? PlayerControl.LocalPlayer.Data.DefaultOutfit.ColorId : DataManager.Player.Customization.Color);
|
||||
colorChip.Button.ClickMask = __instance.scroller.Hitbox;
|
||||
colorChip.SelectionHighlight.gameObject.SetActive(false);
|
||||
__instance.UpdateMaterials(colorChip.Inner.FrontLayer, hat);
|
||||
colorChip.Inner.SetMaskType(PlayerMaterial.MaskType.SimpleUI);
|
||||
colorChip.transform.localPosition = new Vector3(position.x, position.y, -1f);
|
||||
colorChip.Inner.transform.localPosition = hat.ChipOffset + new Vector2(0f, -0.3f);
|
||||
colorChip.Tag = hat;
|
||||
__instance.ColorChips.Add(colorChip);
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
|
||||
namespace MiraAPI.Patches.Menu;
|
||||
|
||||
/// <summary>
|
||||
/// General MainMenuManage patches.
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(MainMenuManager))]
|
||||
public static class MainMenuManagerPatches
|
||||
{
|
||||
/// <summary>
|
||||
/// A postifix on Awake to load all the addressables registered.
|
||||
/// </summary>
|
||||
[HarmonyPatch(nameof(MainMenuManager.Awake))]
|
||||
[HarmonyPostfix]
|
||||
public static void AwakePostfix()
|
||||
{
|
||||
AddressablesLoader.LoadAll();
|
||||
}
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
using AmongUs.Data;
|
||||
using HarmonyLib;
|
||||
using Innersloth.Assets;
|
||||
using MiraAPI.Utilities;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace MiraAPI.Patches.Menu;
|
||||
|
||||
[HarmonyPatch(typeof(NameplatesTab))]
|
||||
public static class NameplatesTabPatches
|
||||
{
|
||||
private static readonly SortedList<string, List<NamePlateData>> SortedNameplates = new(new ControllableComparer<string>(["vanilla"], [], StringComparer.InvariantCulture));
|
||||
private static int currentPage;
|
||||
internal static void AddRange(IEnumerable<(string Key, NamePlateData Visor)> data)
|
||||
{
|
||||
foreach (var item in data)
|
||||
{
|
||||
if (!SortedNameplates.ContainsKey(item.Key)) SortedNameplates.Add(item.Key, []);
|
||||
SortedNameplates[item.Key].Add(item.Visor);
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(nameof(NameplatesTab.OnEnable))]
|
||||
[HarmonyPrefix]
|
||||
public static bool OnEnablePrefix(NameplatesTab __instance)
|
||||
{
|
||||
__instance.plateId = HatManager.Instance.GetNamePlateById(DataManager.Player.Customization.namePlate).ProdId;
|
||||
|
||||
if (!SortedNameplates.ContainsKey("Vanilla")) AddRange(DestroyableSingleton<HatManager>.Instance.GetUnlockedNamePlates().Select(x => ("Vanilla", x)));
|
||||
GenerateHats(__instance, currentPage);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(NameplatesTab), nameof(NameplatesTab.Update))]
|
||||
[HarmonyPrefix]
|
||||
|
||||
public static void UpdatePrefix(NameplatesTab __instance)
|
||||
{
|
||||
if (Input.GetKeyDown(KeyCode.LeftControl) || Input.GetKeyDown(KeyCode.RightControl) || Input.GetKeyDown(KeyCode.LeftArrow))
|
||||
{
|
||||
currentPage--;
|
||||
currentPage = currentPage < 0 ? SortedNameplates.Count - 1 : currentPage;
|
||||
GenerateHats(__instance, currentPage);
|
||||
}
|
||||
else if (Input.GetKeyDown(KeyCode.Tab) || Input.GetKeyDown(KeyCode.RightArrow))
|
||||
{
|
||||
currentPage++;
|
||||
currentPage = currentPage > SortedNameplates.Count - 1 ? 0 : currentPage;
|
||||
GenerateHats(__instance, currentPage);
|
||||
}
|
||||
}
|
||||
|
||||
private static void GenerateHats(NameplatesTab __instance, int page)
|
||||
{
|
||||
foreach (ColorChip instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.Destroy();
|
||||
__instance.ColorChips.Clear();
|
||||
__instance.scroller.Inner.GetComponentsInChildren<TextMeshPro>().Do(x => x.gameObject.Destroy());
|
||||
|
||||
var groupNameText = __instance.GetComponentInChildren<TextMeshPro>(false);
|
||||
|
||||
int hatIndex = 0;
|
||||
|
||||
var (groupName, nameplates) = SortedNameplates.ToArray()[page];
|
||||
var text = Object.Instantiate(groupNameText, __instance.scroller.Inner);
|
||||
text.enabled = true;
|
||||
text.gameObject.transform.localScale = Vector3.one;
|
||||
text.GetComponent<TextTranslatorTMP>().Destroy();
|
||||
text.EnableStencilMasking();
|
||||
text.text = $"{groupName}\nPress Ctrl or Tab to cycle pages";
|
||||
text.alignment = TextAlignmentOptions.Center;
|
||||
text.fontSize = 3f;
|
||||
text.fontSizeMax = 3f;
|
||||
text.fontSizeMin = 0f;
|
||||
float xLerp = __instance.XRange.Lerp(0.5f);
|
||||
float yLerp = __instance.YStart - hatIndex / __instance.NumPerRow * __instance.YOffset;
|
||||
text.transform.localPosition = new Vector3(xLerp, yLerp, -1f);
|
||||
|
||||
hatIndex += 2;
|
||||
foreach (var visor in nameplates.OrderBy(HatManager.Instance.allNamePlates.IndexOf))
|
||||
{
|
||||
float hatXposition = __instance.XRange.Lerp(hatIndex % __instance.NumPerRow / (__instance.NumPerRow - 1f));
|
||||
float hatYposition = __instance.YStart - hatIndex / __instance.NumPerRow * __instance.YOffset;
|
||||
GenerateColorChip(__instance, new Vector2(hatXposition, hatYposition), visor);
|
||||
hatIndex += 1;
|
||||
}
|
||||
|
||||
__instance.scroller.ContentYBounds.max = -(__instance.YStart - (hatIndex + 1) / __instance.NumPerRow * __instance.YOffset) - 3f;
|
||||
__instance.currentNameplateIsEquipped = true;
|
||||
}
|
||||
|
||||
private static void GenerateColorChip(NameplatesTab __instance, Vector2 position, NamePlateData namePlate)
|
||||
{
|
||||
var colorChip = Object.Instantiate(__instance.ColorTabPrefab, __instance.scroller.Inner);
|
||||
colorChip.gameObject.name = namePlate.ProductId;
|
||||
colorChip.Button.OnClick.AddListener((Action)(() => __instance.ClickEquip()));
|
||||
colorChip.Button.OnMouseOver.AddListener((Action)(() => __instance.SelectNameplate(namePlate)));
|
||||
colorChip.Button.OnMouseOut.AddListener((Action)(() => __instance.SelectNameplate(HatManager.Instance.GetNamePlateById(DataManager.Player.Customization.NamePlate))));
|
||||
colorChip.Button.ClickMask = __instance.scroller.Hitbox;
|
||||
colorChip.ProductId = namePlate.ProdId;
|
||||
|
||||
var x = (NamePlateViewData viewdata) =>
|
||||
{
|
||||
colorChip.transform.GetChild(1).GetComponent<SpriteRenderer>().sprite = viewdata?.Image;
|
||||
// (colorChip as NameplateChip).image.sprite = viewdata?.Image;
|
||||
};
|
||||
__instance.StartCoroutine(AddressableAssetExtensions.CoLoadAssetAsync<NamePlateViewData>(__instance, namePlate.GetAssetReference(), x));
|
||||
colorChip.transform.localPosition = new Vector3(position.x, position.y, -1f);
|
||||
colorChip.Tag = namePlate;
|
||||
__instance.ColorChips.Add(colorChip);
|
||||
}
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
using AmongUs.Data;
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Utilities;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace MiraAPI.Patches.Menu;
|
||||
|
||||
[HarmonyPatch(typeof(SkinsTab))]
|
||||
public static class SkinsTabPatches
|
||||
{
|
||||
private static SortedList<string, List<SkinData>> sortedSkins = [];
|
||||
private static int currentPage;
|
||||
|
||||
[HarmonyPatch(nameof(SkinsTab.OnEnable))]
|
||||
[HarmonyPrefix]
|
||||
public static bool OnEnablePrefix(SkinsTab __instance)
|
||||
{
|
||||
__instance.skinId = HatManager.Instance.GetSkinById(DataManager.Player.Customization.Skin).ProdId;
|
||||
var allSkins = HatManager.Instance.GetUnlockedSkins().ToImmutableList();
|
||||
|
||||
if (sortedSkins.Count == 0)
|
||||
{
|
||||
var comparer = new ControllableComparer<string>(["vanilla"], [], StringComparer.InvariantCulture);
|
||||
sortedSkins = new SortedList<string, List<SkinData>>(comparer);
|
||||
foreach (var skin in allSkins)
|
||||
{
|
||||
if (!sortedSkins.ContainsKey(skin.StoreName)) sortedSkins[skin.StoreName] = [];
|
||||
sortedSkins[skin.StoreName].Add(skin);
|
||||
}
|
||||
}
|
||||
|
||||
GenerateHats(__instance, currentPage);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(SkinsTab), nameof(SkinsTab.Update))]
|
||||
[HarmonyPrefix]
|
||||
|
||||
public static void UpdatePrefix(SkinsTab __instance)
|
||||
{
|
||||
if (Input.GetKeyDown(KeyCode.LeftControl) || Input.GetKeyDown(KeyCode.RightControl) || Input.GetKeyDown(KeyCode.LeftArrow))
|
||||
{
|
||||
currentPage--;
|
||||
currentPage = currentPage < 0 ? sortedSkins.Count - 1 : currentPage;
|
||||
GenerateHats(__instance, currentPage);
|
||||
}
|
||||
else if (Input.GetKeyDown(KeyCode.Tab) || Input.GetKeyDown(KeyCode.RightArrow))
|
||||
{
|
||||
currentPage++;
|
||||
currentPage = currentPage > sortedSkins.Count - 1 ? 0 : currentPage;
|
||||
GenerateHats(__instance, currentPage);
|
||||
}
|
||||
}
|
||||
|
||||
private static void GenerateHats(SkinsTab __instance, int page)
|
||||
{
|
||||
foreach (ColorChip instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.Destroy();
|
||||
__instance.ColorChips.Clear();
|
||||
__instance.scroller.Inner.GetComponentsInChildren<TextMeshPro>().Do(x => x.gameObject.Destroy());
|
||||
|
||||
var groupNameText = __instance.GetComponentInChildren<TextMeshPro>(false);
|
||||
|
||||
int hatIndex = 0;
|
||||
|
||||
var (groupName, skins) = sortedSkins.ToArray()[page];
|
||||
var text = Object.Instantiate(groupNameText, __instance.scroller.Inner);
|
||||
text.enabled = true;
|
||||
text.gameObject.transform.localScale = Vector3.one;
|
||||
text.GetComponent<TextTranslatorTMP>().Destroy();
|
||||
text.EnableStencilMasking();
|
||||
text.text = $"{groupName}\nPress Ctrl or Tab to cycle pages";
|
||||
text.alignment = TextAlignmentOptions.Center;
|
||||
text.fontSize = 3f;
|
||||
text.fontSizeMax = 3f;
|
||||
text.fontSizeMin = 0f;
|
||||
float xLerp = __instance.XRange.Lerp(0.5f);
|
||||
float yLerp = __instance.YStart - hatIndex / __instance.NumPerRow * __instance.YOffset;
|
||||
text.transform.localPosition = new Vector3(xLerp, yLerp, -1f);
|
||||
|
||||
hatIndex += 5;
|
||||
foreach (var skin in skins.OrderBy(HatManager.Instance.allSkins.IndexOf))
|
||||
{
|
||||
float hatXposition = __instance.XRange.Lerp(hatIndex % __instance.NumPerRow / (__instance.NumPerRow - 1f));
|
||||
float hatYposition = __instance.YStart - hatIndex / __instance.NumPerRow * __instance.YOffset;
|
||||
GenerateColorChip(__instance, new Vector2(hatXposition, hatYposition), skin);
|
||||
hatIndex += 1;
|
||||
}
|
||||
|
||||
__instance.scroller.ContentYBounds.max = -(__instance.YStart - (hatIndex + 1) / __instance.NumPerRow * __instance.YOffset) - 3f;
|
||||
__instance.currentSkinIsEquipped = true;
|
||||
}
|
||||
|
||||
private static void GenerateColorChip(SkinsTab __instance, Vector2 position, SkinData skin)
|
||||
{
|
||||
var colorChip = Object.Instantiate(__instance.ColorTabPrefab, __instance.scroller.Inner);
|
||||
colorChip.gameObject.name = skin.ProductId;
|
||||
colorChip.Button.OnClick.AddListener((Action)(() => __instance.ClickEquip()));
|
||||
colorChip.Button.OnMouseOver.AddListener((Action)(() => __instance.SelectSkin(skin)));
|
||||
colorChip.Button.OnMouseOut.AddListener((Action)(() => __instance.SelectSkin(HatManager.Instance.GetSkinById(DataManager.Player.Customization.Skin))));
|
||||
colorChip.Button.ClickMask = __instance.scroller.Hitbox;
|
||||
colorChip.ProductId = skin.ProductId;
|
||||
colorChip.SelectionHighlight.gameObject.SetActive(false);
|
||||
__instance.UpdateMaterials(colorChip.Inner.FrontLayer, skin);
|
||||
skin.SetPreview(colorChip.Inner.FrontLayer, __instance.HasLocalPlayer() ? PlayerControl.LocalPlayer.Data.DefaultOutfit.ColorId : DataManager.Player.Customization.Color);
|
||||
colorChip.transform.localPosition = new Vector3(position.x, position.y, -1f);
|
||||
colorChip.Inner.transform.localPosition = skin.ChipOffset + new Vector2(0f, -0.3f);
|
||||
colorChip.Tag = skin;
|
||||
__instance.ColorChips.Add(colorChip);
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
using AmongUs.Data;
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Utilities;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace MiraAPI.Patches.Menu;
|
||||
|
||||
[HarmonyPatch(typeof(VisorsTab))]
|
||||
public static class VisorsTabPatches
|
||||
{
|
||||
private static readonly SortedList<string, List<VisorData>> SortedVisors = new(new ControllableComparer<string>(["vanilla"], [], StringComparer.InvariantCulture));
|
||||
private static int currentPage;
|
||||
internal static void AddRange(IEnumerable<(string Key, VisorData Visor)> data)
|
||||
{
|
||||
foreach (var item in data)
|
||||
{
|
||||
if (!SortedVisors.ContainsKey(item.Key)) SortedVisors.Add(item.Key, []);
|
||||
SortedVisors[item.Key].Add(item.Visor);
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(nameof(VisorsTab.OnEnable))]
|
||||
[HarmonyPrefix]
|
||||
public static bool OnEnablePrefix(VisorsTab __instance)
|
||||
{
|
||||
__instance.visorId = HatManager.Instance.GetVisorById(DataManager.Player.Customization.Visor).ProdId;
|
||||
|
||||
if (!SortedVisors.ContainsKey("Vanilla")) AddRange(DestroyableSingleton<HatManager>.Instance.GetUnlockedVisors().Select(x => ("Vanilla", x)));
|
||||
GenerateHats(__instance, currentPage);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(VisorsTab), nameof(VisorsTab.Update))]
|
||||
[HarmonyPrefix]
|
||||
|
||||
public static void UpdatePrefix(VisorsTab __instance)
|
||||
{
|
||||
if (Input.GetKeyDown(KeyCode.LeftControl) || Input.GetKeyDown(KeyCode.RightControl) || Input.GetKeyDown(KeyCode.LeftArrow))
|
||||
{
|
||||
currentPage--;
|
||||
currentPage = currentPage < 0 ? SortedVisors.Count - 1 : currentPage;
|
||||
GenerateHats(__instance, currentPage);
|
||||
}
|
||||
else if (Input.GetKeyDown(KeyCode.Tab) || Input.GetKeyDown(KeyCode.RightArrow))
|
||||
{
|
||||
currentPage++;
|
||||
currentPage = currentPage > SortedVisors.Count - 1 ? 0 : currentPage;
|
||||
GenerateHats(__instance, currentPage);
|
||||
}
|
||||
}
|
||||
|
||||
private static void GenerateHats(VisorsTab __instance, int page)
|
||||
{
|
||||
foreach (ColorChip instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.Destroy();
|
||||
__instance.ColorChips.Clear();
|
||||
__instance.scroller.Inner.GetComponentsInChildren<TextMeshPro>().Do(x => x.gameObject.Destroy());
|
||||
|
||||
var groupNameText = __instance.GetComponentInChildren<TextMeshPro>(false);
|
||||
|
||||
int hatIndex = 0;
|
||||
|
||||
var (groupName, visors) = SortedVisors.ToArray()[page];
|
||||
var text = Object.Instantiate(groupNameText, __instance.scroller.Inner);
|
||||
text.enabled = true;
|
||||
text.gameObject.transform.localScale = Vector3.one;
|
||||
text.GetComponent<TextTranslatorTMP>().Destroy();
|
||||
text.EnableStencilMasking();
|
||||
text.text = $"{groupName}\nPress Ctrl or Tab to cycle pages";
|
||||
text.alignment = TextAlignmentOptions.Center;
|
||||
text.fontSize = 3f;
|
||||
text.fontSizeMax = 3f;
|
||||
text.fontSizeMin = 0f;
|
||||
float xLerp = __instance.XRange.Lerp(0.5f);
|
||||
float yLerp = __instance.YStart - hatIndex / __instance.NumPerRow * __instance.YOffset;
|
||||
text.transform.localPosition = new Vector3(xLerp, yLerp, -1f);
|
||||
|
||||
hatIndex += 5;
|
||||
foreach (var visor in visors.OrderBy(HatManager.Instance.allVisors.IndexOf))
|
||||
{
|
||||
float hatXposition = __instance.XRange.Lerp(hatIndex % __instance.NumPerRow / (__instance.NumPerRow - 1f));
|
||||
float hatYposition = __instance.YStart - hatIndex / __instance.NumPerRow * __instance.YOffset;
|
||||
GenerateColorChip(__instance, new Vector2(hatXposition, hatYposition), visor);
|
||||
hatIndex += 1;
|
||||
}
|
||||
|
||||
__instance.scroller.ContentYBounds.max = -(__instance.YStart - (hatIndex + 1) / __instance.NumPerRow * __instance.YOffset) - 3f;
|
||||
__instance.currentVisorIsEquipped = true;
|
||||
}
|
||||
|
||||
private static void GenerateColorChip(VisorsTab __instance, Vector2 position, VisorData visor)
|
||||
{
|
||||
var colorChip = Object.Instantiate(__instance.ColorTabPrefab, __instance.scroller.Inner);
|
||||
colorChip.gameObject.name = visor.ProductId;
|
||||
colorChip.Button.OnClick.AddListener((Action)(() => __instance.ClickEquip()));
|
||||
colorChip.Button.OnMouseOver.AddListener((Action)(() => __instance.SelectVisor(visor)));
|
||||
colorChip.Button.OnMouseOut.AddListener((Action)(() => __instance.SelectVisor(HatManager.Instance.GetVisorById(DataManager.Player.Customization.Visor))));
|
||||
colorChip.Button.ClickMask = __instance.scroller.Hitbox;
|
||||
colorChip.ProductId = visor.ProductId;
|
||||
colorChip.SelectionHighlight.gameObject.SetActive(false);
|
||||
__instance.UpdateMaterials(colorChip.Inner.FrontLayer, visor);
|
||||
visor.SetPreview(colorChip.Inner.FrontLayer, __instance.HasLocalPlayer() ? PlayerControl.LocalPlayer.Data.DefaultOutfit.ColorId : DataManager.Player.Customization.Color);
|
||||
colorChip.transform.localPosition = new Vector3(position.x, position.y, -1f);
|
||||
colorChip.Inner.transform.localPosition = visor.ChipOffset + new Vector2(0f, -0.3f);
|
||||
colorChip.Tag = visor;
|
||||
__instance.ColorChips.Add(colorChip);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
using HarmonyLib;
|
||||
|
||||
namespace MiraAPI.Patches;
|
||||
|
||||
/// <summary>
|
||||
/// Used to fix the Minigame crash errors (credits Submerged/PH Gaming for the fix).
|
||||
/// </summary>
|
||||
[HarmonyPatch]
|
||||
public static class MinigameBeginErrorPatch
|
||||
{
|
||||
private static readonly Logger _logger = new(Logger.Category.Gameplay, "Minigame");
|
||||
|
||||
[HarmonyPatch(typeof(Minigame), nameof(Minigame.Begin))]
|
||||
[HarmonyPrefix]
|
||||
public static void Prefix(Minigame __instance)
|
||||
{
|
||||
__instance.logger ??= _logger;
|
||||
}
|
||||
}
|
||||
@@ -1,63 +1,40 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Modifiers;
|
||||
using MiraAPI.Modifiers.Types;
|
||||
|
||||
namespace MiraAPI.Patches.Modifiers;
|
||||
|
||||
[HarmonyPatch(typeof(AmongUsClient), nameof(AmongUsClient.OnGameEnd))]
|
||||
internal static class EndGameDidWinPatch
|
||||
[HarmonyPatch(typeof(AmongUsClient), nameof(AmongUsClient.CoEndGame))]
|
||||
public static class EndGameDidWinPatch
|
||||
{
|
||||
private static List<CachedPlayerData> CachedWinners { get; set; } = [];
|
||||
|
||||
public static void Prefix(EndGameResult endGameResult)
|
||||
public static void Prefix()
|
||||
{
|
||||
CachedWinners.Clear();
|
||||
var gameOverReason = endGameResult.GameOverReason;
|
||||
var gameOverReason = EndGameResult.CachedGameOverReason;
|
||||
EndGameResult.CachedWinners.Clear();
|
||||
|
||||
var players = GameData.Instance.AllPlayers.ToArray();
|
||||
for (var i = 0; i < GameData.Instance.PlayerCount; i++)
|
||||
{
|
||||
var networkedPlayerInfo = players[i];
|
||||
if (!networkedPlayerInfo)
|
||||
if (networkedPlayerInfo == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var didWin = networkedPlayerInfo.Role.DidWin(gameOverReason);
|
||||
|
||||
if (!networkedPlayerInfo.Object)
|
||||
var modifierWin = networkedPlayerInfo.Object?.GetModifierComponent().GetModifiers<GameModifier>()
|
||||
.FirstOrDefault(x => x.DidWin(gameOverReason) != null)?.DidWin(gameOverReason);
|
||||
if (modifierWin != null)
|
||||
{
|
||||
if (didWin)
|
||||
{
|
||||
CachedWinners.Add(new CachedPlayerData(networkedPlayerInfo));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var modifier in networkedPlayerInfo.Object.GetModifiers<GameModifier>().OrderByDescending(x => x.Priority()))
|
||||
{
|
||||
var result = modifier.DidWin(gameOverReason);
|
||||
if (!result.HasValue) continue;
|
||||
|
||||
didWin = result.Value;
|
||||
break;
|
||||
didWin = modifierWin.Value;
|
||||
}
|
||||
|
||||
if (didWin)
|
||||
{
|
||||
CachedWinners.Add(new CachedPlayerData(networkedPlayerInfo));
|
||||
EndGameResult.CachedWinners.Add(new CachedPlayerData(networkedPlayerInfo));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Postfix(EndGameResult endGameResult)
|
||||
{
|
||||
EndGameResult.CachedWinners.Clear();
|
||||
foreach (var winner in CachedWinners)
|
||||
{
|
||||
EndGameResult.CachedWinners.Add(winner);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,12 +14,18 @@ using Object = UnityEngine.Object;
|
||||
|
||||
namespace MiraAPI.Patches.Options;
|
||||
|
||||
/// <summary>
|
||||
/// Patches the GameOptionsMenu to add custom options.
|
||||
/// </summary>
|
||||
[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]
|
||||
[HarmonyPatch(nameof(GameOptionsMenu.Update))]
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public static void UpdatePatch(GameOptionsMenu __instance)
|
||||
{
|
||||
if (GameSettingMenuPatches.SelectedModIdx == 0)
|
||||
@@ -35,7 +41,7 @@ internal static class GameOptionsMenuPatch
|
||||
else
|
||||
{
|
||||
var filteredGroups =
|
||||
GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
|
||||
GameSettingMenuPatches.SelectedMod?.OptionGroups
|
||||
.Where(x => x.OptionableType == null && !x.ShowInModifiersMenu) ?? [];
|
||||
|
||||
foreach (var group in filteredGroups)
|
||||
@@ -101,7 +107,7 @@ internal static class GameOptionsMenuPatch
|
||||
|
||||
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) ?? [];
|
||||
|
||||
foreach (var modGroup in groups)
|
||||
@@ -112,7 +118,7 @@ internal static class GameOptionsMenuPatch
|
||||
|
||||
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) ?? [];
|
||||
foreach (var group in groups)
|
||||
{
|
||||
@@ -122,7 +128,6 @@ internal static class GameOptionsMenuPatch
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GameOptionsMenu.CreateSettings))]
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public static bool SettingsPatch(GameOptionsMenu __instance)
|
||||
{
|
||||
if (GameSettingMenuPatches.SelectedModIdx == 0)
|
||||
@@ -138,7 +143,7 @@ internal static class GameOptionsMenuPatch
|
||||
return false;
|
||||
}
|
||||
|
||||
var filteredGroups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
|
||||
var filteredGroups = GameSettingMenuPatches.SelectedMod?.OptionGroups
|
||||
.Where(x => x.OptionableType == null && !x.ShowInModifiersMenu) ?? [];
|
||||
|
||||
foreach (var group in filteredGroups)
|
||||
@@ -151,7 +156,6 @@ internal static class GameOptionsMenuPatch
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GameOptionsMenu.Initialize))]
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public static bool InitPatch(GameOptionsMenu __instance)
|
||||
{
|
||||
if (__instance.Children != null && __instance.Children.Count != 0)
|
||||
|
||||
@@ -1,348 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BepInEx.Configuration;
|
||||
using HarmonyLib;
|
||||
using MiraAPI.PluginLoading;
|
||||
using MiraAPI.Presets;
|
||||
using MiraAPI.Roles;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using Reactor.Utilities;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.UI;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace MiraAPI.Patches.Options;
|
||||
|
||||
internal static class GamePresetsTabPatches
|
||||
{
|
||||
private static GameObject _saveButton = null!;
|
||||
private static GameObject _refreshButton = null!;
|
||||
private static GameObject _folderButton = null!;
|
||||
private static GameObject _newDivider = null!;
|
||||
private static GameObject _presetHolder = null!;
|
||||
private static GridArrange _arrange = null!;
|
||||
|
||||
private static readonly object Lock = new();
|
||||
|
||||
public static void DivideSize(GameObject obj, float amount)
|
||||
{
|
||||
foreach (var collider in obj.GetComponentsInChildren<Collider2D>(true))
|
||||
{
|
||||
if (collider.TryCast<BoxCollider2D>() is { } col)
|
||||
{
|
||||
col.size = new Vector2(col.size.x / amount, col.size.y);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var rend in obj.GetComponentsInChildren<SpriteRenderer>(true))
|
||||
{
|
||||
rend.size = new Vector2(rend.size.x / amount, rend.size.y);
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(GamePresetsTab), nameof(GamePresetsTab.OnEnable))]
|
||||
public static class GamePresetsOnEnablePatch
|
||||
{
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public static void Postfix(GamePresetsTab __instance)
|
||||
{
|
||||
if (!GameSettingMenu.Instance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CreatePresetMenu(__instance);
|
||||
|
||||
__instance.StandardPresetButton.gameObject.SetActive(GameSettingMenuPatches.SelectedModIdx == 0);
|
||||
__instance.SecondPresetButton.gameObject.SetActive(GameSettingMenuPatches.SelectedModIdx == 0);
|
||||
|
||||
if (GameSettingMenuPatches.SelectedModIdx != 0)
|
||||
{
|
||||
__instance.PresetDescriptionText.text = "Select or create an options preset for this mod";
|
||||
}
|
||||
else
|
||||
{
|
||||
__instance.SetSelectedText();
|
||||
}
|
||||
|
||||
// Show the save and refresh buttons
|
||||
if (_saveButton)
|
||||
{
|
||||
_saveButton.SetActive(GameSettingMenuPatches.SelectedModIdx != 0);
|
||||
}
|
||||
if (_refreshButton)
|
||||
{
|
||||
_refreshButton.SetActive(GameSettingMenuPatches.SelectedModIdx != 0);
|
||||
}
|
||||
if (_folderButton)
|
||||
{
|
||||
_folderButton.SetActive(GameSettingMenuPatches.SelectedModIdx != 0);
|
||||
}
|
||||
if (_newDivider)
|
||||
{
|
||||
_newDivider.SetActive(GameSettingMenuPatches.SelectedModIdx != 0);
|
||||
}
|
||||
|
||||
var prefab = GameSettingMenu.Instance.GameSettingsButton;
|
||||
|
||||
foreach (var preset in MiraPluginManager.Instance.RegisteredPlugins.SelectMany(x => x.Presets))
|
||||
{
|
||||
var button = Object.Instantiate(prefab, _presetHolder.transform);
|
||||
button.buttonText.text = preset.Name;
|
||||
if (button.buttonText.TryGetComponent<TextTranslatorTMP>(out var tmp))
|
||||
{
|
||||
tmp.DestroyImmediate();
|
||||
}
|
||||
|
||||
button.buttonText.alignment = TextAlignmentOptions.Center;
|
||||
|
||||
button.OnClick = new Button.ButtonClickedEvent();
|
||||
button.OnClick.AddListener(
|
||||
(UnityAction)(() =>
|
||||
{
|
||||
preset.LoadPreset();
|
||||
}));
|
||||
button.gameObject.SetActive(false);
|
||||
preset.PresetButton = button.gameObject;
|
||||
}
|
||||
|
||||
foreach (var mod in MiraPluginManager.Instance.RegisteredPlugins)
|
||||
{
|
||||
foreach (var button in mod.Presets.Select(x => x.PresetButton))
|
||||
{
|
||||
if (button != null)
|
||||
{
|
||||
button.SetActive(mod == GameSettingMenuPatches.SelectedMod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_arrange.Start();
|
||||
_arrange.ArrangeChilds();
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(GamePresetsTab), nameof(GamePresetsTab.OnDisable))]
|
||||
public static class GamePresetsOnDisablePatch
|
||||
{
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public static void Postfix()
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error("OnDisable called");
|
||||
|
||||
if (_saveButton)
|
||||
{
|
||||
_saveButton.SetActive(false);
|
||||
}
|
||||
if (_refreshButton)
|
||||
{
|
||||
_refreshButton.SetActive(false);
|
||||
}
|
||||
if (_folderButton)
|
||||
{
|
||||
_folderButton.SetActive(false);
|
||||
}
|
||||
|
||||
if (_newDivider)
|
||||
{
|
||||
_newDivider.SetActive(false);
|
||||
}
|
||||
|
||||
foreach (var mod in MiraPluginManager.Instance.RegisteredPlugins)
|
||||
{
|
||||
foreach (var button in mod.Presets.Select(x => x.PresetButton))
|
||||
{
|
||||
if (button != null)
|
||||
{
|
||||
button.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void CreatePresetMenu(GamePresetsTab presetTab)
|
||||
{
|
||||
var prefab = GameSettingMenu.Instance.GameSettingsButton;
|
||||
if (prefab == null)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error("GameSettingsButton prefab is null");
|
||||
return;
|
||||
}
|
||||
|
||||
var tmpTranslator = presetTab.PresetDescriptionText.gameObject.GetComponent<TextTranslatorTMP>();
|
||||
if (tmpTranslator)
|
||||
{
|
||||
tmpTranslator.Destroy();
|
||||
}
|
||||
|
||||
if (!_newDivider)
|
||||
{
|
||||
var oldDiv = presetTab.transform.FindChild("DividerImage");
|
||||
_newDivider = Object.Instantiate(oldDiv.gameObject, oldDiv.transform.parent);
|
||||
_newDivider.transform.localPosition = new Vector3(1.85f, 0.13f, 0f);
|
||||
_newDivider.transform.localScale = new Vector3(1.13f, 1, 1);
|
||||
_newDivider.transform.localRotation = Quaternion.Euler(new Vector3(0, 0, 90));
|
||||
}
|
||||
|
||||
PassiveButton saveButton;
|
||||
if (!_saveButton)
|
||||
{
|
||||
saveButton = Object.Instantiate(prefab, presetTab.transform);
|
||||
_saveButton = saveButton.gameObject;
|
||||
_saveButton.gameObject.name = "SaveButton";
|
||||
_saveButton.transform.localPosition = new Vector3(3.2f, 1.7f, -2);
|
||||
|
||||
// set the button text and alignment
|
||||
var saveText = saveButton.buttonText;
|
||||
saveText.text = "Save";
|
||||
saveText.GetComponent<TextTranslatorTMP>().Destroy();
|
||||
saveText.alignment = TextAlignmentOptions.Center;
|
||||
saveText.transform.parent.localPosition = new Vector3(
|
||||
-.525f,
|
||||
saveText.transform.parent.localPosition.y,
|
||||
saveText.transform.parent.localPosition.z);
|
||||
|
||||
DivideSize(saveButton.gameObject, 2f);
|
||||
|
||||
saveButton.OnClick = new Button.ButtonClickedEvent();
|
||||
saveButton.OnClick.AddListener(
|
||||
(UnityAction)(() =>
|
||||
{
|
||||
if (GameSettingMenuPatches.SelectedModIdx == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SavePresetPopup.CreatePopup(
|
||||
name =>
|
||||
{
|
||||
if (GameSettingMenuPatches.SelectedMod == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == string.Empty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var presetFile = new ConfigFile(
|
||||
Path.Combine(
|
||||
PresetManager.PresetDirectory,
|
||||
GameSettingMenuPatches.SelectedMod.PluginId,
|
||||
$"{name}.cfg"),
|
||||
false)
|
||||
{
|
||||
SaveOnConfigSet = false,
|
||||
};
|
||||
|
||||
foreach (var option in GameSettingMenuPatches.SelectedMod.InternalOptions.Where(x => x.IncludeInPreset))
|
||||
{
|
||||
option.SaveToPreset(presetFile);
|
||||
}
|
||||
|
||||
foreach (var role in GameSettingMenuPatches.SelectedMod.InternalRoles.Values.OfType<ICustomRole>().Where(x=>!x.Configuration.HideSettings))
|
||||
{
|
||||
role.SaveToPreset(presetFile);
|
||||
}
|
||||
|
||||
presetFile.Save();
|
||||
Refresh();
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
saveButton = _saveButton.GetComponent<PassiveButton>();
|
||||
|
||||
PassiveButton refreshButton;
|
||||
if (!_refreshButton)
|
||||
{
|
||||
refreshButton = Object.Instantiate(saveButton, presetTab.transform);
|
||||
_refreshButton = refreshButton.gameObject;
|
||||
_refreshButton.name = "RefreshButton";
|
||||
_refreshButton.transform.localPosition = new Vector3(2.85f, 1.1f, -2);
|
||||
|
||||
var icon = new GameObject("Sprite");
|
||||
icon.transform.SetParent(refreshButton.transform);
|
||||
icon.transform.localPosition = new Vector3(-0.47f, -0.08f, -5f);
|
||||
icon.transform.localScale = new Vector3(0.5f, 0.5f, 1);
|
||||
icon.layer = refreshButton.gameObject.layer;
|
||||
|
||||
var iconSpriteRend = icon.AddComponent<SpriteRenderer>();
|
||||
iconSpriteRend.sprite = MiraAssets.RefreshIcon.LoadAsset();
|
||||
|
||||
DivideSize(refreshButton.gameObject, 2f);
|
||||
refreshButton.buttonText.gameObject.Destroy();
|
||||
|
||||
refreshButton.OnClick = new Button.ButtonClickedEvent();
|
||||
refreshButton.OnClick.AddListener((UnityAction)Refresh);
|
||||
}
|
||||
refreshButton = _refreshButton.GetComponent<PassiveButton>();
|
||||
|
||||
if (!_folderButton)
|
||||
{
|
||||
var openFolderButton = Object.Instantiate(refreshButton, presetTab.transform);
|
||||
_folderButton = openFolderButton.gameObject;
|
||||
_folderButton.name = "OpenFolderButton";
|
||||
_folderButton.transform.localPosition = new Vector3(3.55f, 1.1f, -2);
|
||||
|
||||
var folderRend = openFolderButton.transform.FindChild("Sprite").gameObject.GetComponent<SpriteRenderer>();
|
||||
folderRend.sprite = MiraAssets.FolderIcon.LoadAsset();
|
||||
folderRend.transform.localScale = new Vector3(0.4f, 0.4f, 1);
|
||||
|
||||
openFolderButton.OnClick = new Button.ButtonClickedEvent();
|
||||
openFolderButton.OnClick.AddListener(
|
||||
(UnityAction)(() =>
|
||||
{
|
||||
var directory = Path.Combine(
|
||||
PresetManager.PresetDirectory,
|
||||
GameSettingMenuPatches.SelectedMod?.PluginId ?? string.Empty);
|
||||
Directory.CreateDirectory(directory);
|
||||
Process.Start(
|
||||
new ProcessStartInfo
|
||||
{
|
||||
FileName = directory,
|
||||
UseShellExecute = true,
|
||||
Verb = "open",
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
_saveButton.SetActive(false);
|
||||
_refreshButton.SetActive(false);
|
||||
_folderButton.SetActive(false);
|
||||
_newDivider.SetActive(false);
|
||||
|
||||
if (!_presetHolder)
|
||||
{
|
||||
_presetHolder = new GameObject("PresetHolder");
|
||||
_presetHolder.transform.SetParent(presetTab.transform, false);
|
||||
_presetHolder.transform.localScale = new Vector3(0.9f, 0.9f, 1);
|
||||
_presetHolder.transform.localPosition = new Vector3(-1.9f, 1.7f, 0);
|
||||
_arrange = _presetHolder.AddComponent<GridArrange>();
|
||||
_arrange.Alignment = GridArrange.StartAlign.Right;
|
||||
_arrange.CellSize = new Vector2(2.15f, -.55f);
|
||||
_arrange.MaxColumns = 2;
|
||||
}
|
||||
}
|
||||
|
||||
private static void Refresh()
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error("Refreshing presets");
|
||||
if (GameSettingMenuPatches.SelectedMod == null)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error("Selected mod is null, cannot refresh presets");
|
||||
return;
|
||||
}
|
||||
lock (Lock)
|
||||
{
|
||||
PresetManager.LoadPresets(GameSettingMenuPatches.SelectedMod);
|
||||
}
|
||||
GameSettingMenu.Instance.ChangeTab(0, false); // refresh the tab
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Modifiers;
|
||||
using MiraAPI.PluginLoading;
|
||||
using MiraAPI.Presets;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using Reactor.Utilities;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
@@ -32,39 +31,13 @@ internal static class GameSettingMenuPatches
|
||||
private static PassiveButton? _modifiersButton;
|
||||
private static PassiveButton? _smallRolesButton;
|
||||
|
||||
private static Dictionary<int, Vector3> OptionsPositions { get; } = [];
|
||||
private static Dictionary<int, Vector3> ModifiersPositions { get; } = [];
|
||||
|
||||
private static void SaveScrollPositions(GameSettingMenu gameSettingMenu)
|
||||
{
|
||||
if (_modifiersTab)
|
||||
{
|
||||
ModifiersPositions[SelectedModIdx] = _modifiersTab!.scrollBar.Inner.localPosition;
|
||||
}
|
||||
|
||||
RoleSettingMenuPatches.RolePositions[SelectedModIdx] = gameSettingMenu.RoleSettingsTab.scrollBar.Inner.localPosition;
|
||||
OptionsPositions[SelectedModIdx] = gameSettingMenu.GameSettingsTab.scrollBar.Inner.localPosition;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GameSettingMenu.ChangeTab))]
|
||||
|
||||
public static void ChangeTabPrefix(GameSettingMenu __instance)
|
||||
{
|
||||
SaveScrollPositions(__instance);
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(GameSettingMenu.ChangeTab))]
|
||||
public static void ChangeTabPostfix(GameSettingMenu __instance, int tabNum, bool previewOnly)
|
||||
{
|
||||
if ((previewOnly && Controller.currentTouchType == Controller.TouchType.Joystick) || !previewOnly)
|
||||
{
|
||||
if (_modifiersTab)
|
||||
{
|
||||
_modifiersTab!.gameObject.SetActive(tabNum == 3);
|
||||
}
|
||||
|
||||
if (_modifiersTab) _modifiersTab!.gameObject.SetActive(tabNum == 3);
|
||||
_modifiersButton?.SelectButton(tabNum == 3);
|
||||
_smallRolesButton?.SelectButton(tabNum == 2);
|
||||
if (tabNum == 3)
|
||||
@@ -88,10 +61,7 @@ internal static class GameSettingMenuPatches
|
||||
_modifiersButton?.SelectButton(tabNum == 3);
|
||||
_smallRolesButton?.SelectButton(tabNum == 2);
|
||||
|
||||
if (tabNum != 3)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (tabNum != 3) return;
|
||||
|
||||
if (__instance.RoleSettingsButton.gameObject.active)
|
||||
{
|
||||
@@ -141,9 +111,8 @@ internal static class GameSettingMenuPatches
|
||||
passiveButton.OnClick.AddListener(
|
||||
(UnityAction)(() =>
|
||||
{
|
||||
SaveScrollPositions(__instance);
|
||||
SelectedModIdx += 1;
|
||||
if (SelectedModIdx > MiraPluginManager.Instance.RegisteredPlugins.Length)
|
||||
if (SelectedModIdx > MiraPluginManager.Instance.RegisteredPlugins().Length)
|
||||
{
|
||||
SelectedModIdx = 0;
|
||||
}
|
||||
@@ -160,11 +129,10 @@ internal static class GameSettingMenuPatches
|
||||
backButton.gameObject.GetComponent<PassiveButton>().OnClick.AddListener(
|
||||
(UnityAction)(() =>
|
||||
{
|
||||
SaveScrollPositions(__instance);
|
||||
SelectedModIdx -= 1;
|
||||
if (SelectedModIdx < 0)
|
||||
{
|
||||
SelectedModIdx = MiraPluginManager.Instance.RegisteredPlugins.Length;
|
||||
SelectedModIdx = MiraPluginManager.Instance.RegisteredPlugins().Length;
|
||||
}
|
||||
|
||||
UpdateText(__instance, __instance.GameSettingsTab, __instance.RoleSettingsTab);
|
||||
@@ -241,61 +209,54 @@ internal static class GameSettingMenuPatches
|
||||
|
||||
_modifierBtnOgPos = _modifiersButton.transform.localPosition;
|
||||
|
||||
foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins)
|
||||
{
|
||||
PresetManager.LoadPresets(plugin);
|
||||
}
|
||||
|
||||
UpdateText(__instance, __instance.GameSettingsTab, __instance.RoleSettingsTab);
|
||||
}
|
||||
|
||||
private static void ChangeRoleSettingButton(bool replace, GameSettingMenu menu)
|
||||
private static void ChangeRoleSettingButton(bool replace, GameSettingMenu __instance)
|
||||
{
|
||||
menu.RoleSettingsButton.OnClick = new ButtonClickedEvent();
|
||||
menu.RoleSettingsButton.OnMouseOver = new UnityEvent();
|
||||
__instance.RoleSettingsButton.OnClick = new ButtonClickedEvent();
|
||||
__instance.RoleSettingsButton.OnMouseOver = new UnityEvent();
|
||||
|
||||
if (replace)
|
||||
{
|
||||
menu.RoleSettingsButton.buttonText.text = "Modifiers Settings";
|
||||
menu.RoleSettingsButton.OnClick.AddListener(
|
||||
__instance.RoleSettingsButton.buttonText.text = "Modifiers Settings";
|
||||
__instance.RoleSettingsButton.OnClick.AddListener(
|
||||
(UnityAction)(() =>
|
||||
{
|
||||
menu.ChangeTab(3, false);
|
||||
__instance.ChangeTab(3, false);
|
||||
}));
|
||||
menu.RoleSettingsButton.OnMouseOver.AddListener(
|
||||
__instance.RoleSettingsButton.OnMouseOver.AddListener(
|
||||
(UnityAction)(() =>
|
||||
{
|
||||
menu.ChangeTab(3, true);
|
||||
__instance.ChangeTab(3, true);
|
||||
}));
|
||||
}
|
||||
else
|
||||
{
|
||||
menu.RoleSettingsButton.buttonText.text = "Roles Settings";
|
||||
menu.RoleSettingsButton.OnClick.AddListener(
|
||||
__instance.RoleSettingsButton.buttonText.text = "Roles Settings";
|
||||
__instance.RoleSettingsButton.OnClick.AddListener(
|
||||
(UnityAction)(() =>
|
||||
{
|
||||
menu.ChangeTab(2, false);
|
||||
__instance.ChangeTab(2, false);
|
||||
}));
|
||||
menu.RoleSettingsButton.OnMouseOver.AddListener(
|
||||
__instance.RoleSettingsButton.OnMouseOver.AddListener(
|
||||
(UnityAction)(() =>
|
||||
{
|
||||
menu.ChangeTab(2, true);
|
||||
__instance.ChangeTab(2, true);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
private static void UpdateText(GameSettingMenu menu, GameOptionsMenu settings, RolesSettingsMenu roles)
|
||||
{
|
||||
if (_text is not null && SelectedModIdx == 0)
|
||||
{
|
||||
_text.text = "Default";
|
||||
_text.fontSizeMax = 3.2f;
|
||||
SelectedMod = null;
|
||||
}
|
||||
else if (_text is not null)
|
||||
{
|
||||
_text.fontSizeMax = 2.3f;
|
||||
SelectedMod = MiraPluginManager.Instance.RegisteredPlugins[SelectedModIdx - 1];
|
||||
SelectedMod = MiraPluginManager.Instance.RegisteredPlugins()[SelectedModIdx - 1];
|
||||
|
||||
var name = SelectedMod.MiraPlugin.OptionsTitleText;
|
||||
_text.text = name[..Math.Min(name.Length, 25)];
|
||||
@@ -308,11 +269,9 @@ internal static class GameSettingMenuPatches
|
||||
|
||||
if (SelectedModIdx != 0)
|
||||
{
|
||||
var modHasRoles = SelectedMod!.InternalRoles.Count != 0;
|
||||
var modHasModifiers = SelectedMod.InternalOptionGroups.Exists(
|
||||
x => x.ShowInModifiersMenu || x.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true);
|
||||
var modHasOptions =
|
||||
SelectedMod.InternalOptionGroups.Exists(x => x.OptionableType == null && !x.ShowInModifiersMenu);
|
||||
var modHasRoles = SelectedMod!.CustomRoles.Count != 0;
|
||||
var modHasModifiers = SelectedMod.OptionGroups.Exists(x => x.ShowInModifiersMenu || x.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true);
|
||||
var modHasOptions = SelectedMod.OptionGroups.Exists(x => x.OptionableType == null && !x.ShowInModifiersMenu);
|
||||
|
||||
_modifiersButton.gameObject.SetActive(true);
|
||||
_smallRolesButton.gameObject.SetActive(true);
|
||||
@@ -352,7 +311,6 @@ internal static class GameSettingMenuPatches
|
||||
{
|
||||
menu.ChangeTab(0, false);
|
||||
}
|
||||
|
||||
// If the mod has roles, we can enable the bigger role button and disable the small one.
|
||||
if (modHasRoles)
|
||||
{
|
||||
@@ -409,11 +367,6 @@ internal static class GameSettingMenuPatches
|
||||
ChangeRoleSettingButton(replaceWithModifiers, menu);
|
||||
}
|
||||
|
||||
if (menu.PresetsTab.gameObject.active)
|
||||
{
|
||||
menu.ChangeTab(0, false);
|
||||
}
|
||||
|
||||
CleanupTab(settings, roles);
|
||||
}
|
||||
|
||||
@@ -425,30 +378,13 @@ internal static class GameSettingMenuPatches
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
child.gameObject.DestroyImmediate();
|
||||
}
|
||||
|
||||
options.Clear();
|
||||
}
|
||||
|
||||
private static void CleanupTab(GameOptionsMenu settings, RolesSettingsMenu roles)
|
||||
{
|
||||
if (roles.roleChances != null)
|
||||
{
|
||||
CleanupRoleSettings(roles);
|
||||
}
|
||||
|
||||
if (settings.Children != null)
|
||||
{
|
||||
CleanupSettings(settings);
|
||||
}
|
||||
|
||||
if (_modifiersTab?.Children?.Count > 0)
|
||||
{
|
||||
CleanupSettings(_modifiersTab, true);
|
||||
}
|
||||
|
||||
void CleanupRoleSettings(RolesSettingsMenu rolesMenu)
|
||||
{
|
||||
if (rolesMenu.advancedSettingChildren != null)
|
||||
@@ -467,24 +403,15 @@ internal static class GameSettingMenuPatches
|
||||
{
|
||||
role.gameObject.DestroyImmediate();
|
||||
}
|
||||
|
||||
rolesMenu.roleChances?.Clear();
|
||||
|
||||
rolesMenu.AdvancedRolesSettings.gameObject.SetActive(false);
|
||||
rolesMenu.RoleChancesSettings.gameObject.SetActive(true);
|
||||
rolesMenu.SetQuotaTab();
|
||||
if (RoleSettingMenuPatches.RolePositions.TryGetValue(SelectedModIdx, out var pos))
|
||||
{
|
||||
rolesMenu.scrollBar.Inner.localPosition = pos;
|
||||
rolesMenu.scrollBar.UpdateScrollBars();
|
||||
}
|
||||
else
|
||||
{
|
||||
rolesMenu.scrollBar.ScrollToTop();
|
||||
}
|
||||
rolesMenu.scrollBar.ScrollToTop();
|
||||
}
|
||||
|
||||
void CleanupSettings(GameOptionsMenu gameOptMenu, bool modifiers = false)
|
||||
void CleanupSettings(GameOptionsMenu gameOptMenu)
|
||||
{
|
||||
ClearOptions(gameOptMenu.Children);
|
||||
gameOptMenu.Children = null;
|
||||
@@ -495,17 +422,22 @@ internal static class GameSettingMenuPatches
|
||||
.ForEach(header => header.gameObject.DestroyImmediate());
|
||||
|
||||
gameOptMenu.Initialize();
|
||||
|
||||
var positions = modifiers ? ModifiersPositions : OptionsPositions;
|
||||
if (positions.TryGetValue(SelectedModIdx, out var pos))
|
||||
{
|
||||
gameOptMenu.scrollBar.Inner.localPosition = pos;
|
||||
gameOptMenu.scrollBar.UpdateScrollBars();
|
||||
}
|
||||
else
|
||||
{
|
||||
gameOptMenu.scrollBar.ScrollToTop();
|
||||
}
|
||||
gameOptMenu.scrollBar.ScrollToTop();
|
||||
}
|
||||
|
||||
if (roles.roleChances != null && SelectedMod?.CustomRoles.Count > 0)
|
||||
{
|
||||
CleanupRoleSettings(roles);
|
||||
}
|
||||
|
||||
if (settings.Children != null && SelectedMod?.OptionGroups.Count > 0)
|
||||
{
|
||||
CleanupSettings(settings);
|
||||
}
|
||||
|
||||
if (_modifiersTab?.Children?.Count > 0)
|
||||
{
|
||||
CleanupSettings(_modifiersTab);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ public static class LobbyViewPanePatches
|
||||
|
||||
private static MiraPluginInfo? SelectedMod => SelectedModIdx == 0
|
||||
? null
|
||||
: MiraPluginManager.Instance.RegisteredPlugins[SelectedModIdx - 1];
|
||||
: MiraPluginManager.Instance.RegisteredPlugins()[SelectedModIdx - 1];
|
||||
|
||||
private static PassiveButton? ModifiersTabButton { get; set; }
|
||||
|
||||
@@ -74,7 +74,7 @@ public static class LobbyViewPanePatches
|
||||
(UnityAction)(() =>
|
||||
{
|
||||
SelectedModIdx += 1;
|
||||
if (SelectedModIdx > MiraPluginManager.Instance.RegisteredPlugins.Length)
|
||||
if (SelectedModIdx > MiraPluginManager.Instance.RegisteredPlugins().Length)
|
||||
{
|
||||
SelectedModIdx = 0;
|
||||
}
|
||||
@@ -98,7 +98,7 @@ public static class LobbyViewPanePatches
|
||||
SelectedModIdx -= 1;
|
||||
if (SelectedModIdx < 0)
|
||||
{
|
||||
SelectedModIdx = MiraPluginManager.Instance.RegisteredPlugins.Length;
|
||||
SelectedModIdx = MiraPluginManager.Instance.RegisteredPlugins().Length;
|
||||
}
|
||||
|
||||
Refresh(__instance);
|
||||
@@ -113,25 +113,24 @@ public static class LobbyViewPanePatches
|
||||
menu.scrollBar.ScrollToTop();
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(LobbyViewSettingsPane.ChangeTab))]
|
||||
[HarmonyPatch(nameof(LobbyViewSettingsPane.RefreshTab))]
|
||||
// CHANGED BECAUSE OF INLINING
|
||||
public static void SetTabPatch(LobbyViewSettingsPane __instance)
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(LobbyViewSettingsPane.SetTab))]
|
||||
public static bool SetTabPatch(LobbyViewSettingsPane __instance)
|
||||
{
|
||||
if (__instance.currentTab != ModifiersTabName || SelectedMod == null)
|
||||
{
|
||||
ModifiersTabButton?.SelectButton(false);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.taskTabButton.SelectButton(false);
|
||||
__instance.rolesTabButton.SelectButton(false);
|
||||
|
||||
ModifiersTabButton?.SelectButton(true);
|
||||
var filteredGroups = SelectedMod.InternalOptionGroups
|
||||
var filteredGroups = SelectedMod.OptionGroups
|
||||
.Where(x => x.GroupVisible() && (x.ShowInModifiersMenu || x.OptionableType?.IsAssignableTo(typeof(BaseModifier))==true));
|
||||
DrawOptions(__instance, filteredGroups);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
@@ -143,12 +142,7 @@ public static class LobbyViewPanePatches
|
||||
return true;
|
||||
}
|
||||
|
||||
if (__instance.currentTab == ModifiersTabName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var filteredGroups = SelectedMod.InternalOptionGroups
|
||||
var filteredGroups = SelectedMod.OptionGroups
|
||||
.Where(x => x.OptionableType == null && x.GroupVisible.Invoke());
|
||||
DrawOptions(__instance, filteredGroups);
|
||||
return false;
|
||||
@@ -163,11 +157,6 @@ public static class LobbyViewPanePatches
|
||||
return true;
|
||||
}
|
||||
|
||||
if (__instance.currentTab == ModifiersTabName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DrawRolesTab(__instance);
|
||||
return false;
|
||||
}
|
||||
@@ -261,7 +250,7 @@ public static class LobbyViewPanePatches
|
||||
|
||||
var list = new List<Type>();
|
||||
|
||||
var roleGroups = SelectedMod.InternalRoles.Values.OfType<ICustomRole>()
|
||||
var roleGroups = SelectedMod.CustomRoles.Values.OfType<ICustomRole>()
|
||||
.ToLookup(x => x.RoleOptionsGroup);
|
||||
|
||||
// sort the groups by priority
|
||||
@@ -271,7 +260,7 @@ public static class LobbyViewPanePatches
|
||||
|
||||
foreach (var grouping in sortedRoleGroups)
|
||||
{
|
||||
if (!grouping.Any() || grouping.All(x => x.Configuration.HideSettings))
|
||||
if (!grouping.Any())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -302,11 +291,6 @@ public static class LobbyViewPanePatches
|
||||
|
||||
foreach (var customRole in grouping)
|
||||
{
|
||||
if (customRole.Configuration.HideSettings)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var roleBehaviour = customRole as RoleBehaviour;
|
||||
if (roleBehaviour == null)
|
||||
{
|
||||
@@ -326,7 +310,7 @@ public static class LobbyViewPanePatches
|
||||
viewSettingsInfoPanelRoleVariant.transform.localScale = Vector3.one;
|
||||
viewSettingsInfoPanelRoleVariant.transform.localPosition = new Vector3(num2, num, -2f);
|
||||
|
||||
var advancedRoleOptions = SelectedMod.InternalOptionGroups
|
||||
var advancedRoleOptions = SelectedMod.OptionGroups
|
||||
.Where(x => x.OptionableType == customRole.GetType())
|
||||
.SelectMany(x => x.Options)
|
||||
.ToList();
|
||||
@@ -351,7 +335,7 @@ public static class LobbyViewPanePatches
|
||||
viewSettingsInfoPanelRoleVariant.chanceTitle.color =
|
||||
viewSettingsInfoPanelRoleVariant.chanceBackground.color =
|
||||
viewSettingsInfoPanelRoleVariant.background.color =
|
||||
customRole.RoleColor.FindAlternateColor();
|
||||
customRole.RoleColor.GetAlternateColor();
|
||||
instance.settingsInfo.Add(viewSettingsInfoPanelRoleVariant.gameObject);
|
||||
num -= 0.664f;
|
||||
}
|
||||
@@ -413,7 +397,7 @@ public static class LobbyViewPanePatches
|
||||
return 0;
|
||||
}
|
||||
|
||||
var role = SelectedMod.InternalRoles.Values.FirstOrDefault(x => x.GetType() == roleType);
|
||||
var role = SelectedMod.CustomRoles.Values.FirstOrDefault(x => x.GetType() == roleType);
|
||||
|
||||
if (role == null)
|
||||
{
|
||||
@@ -436,7 +420,7 @@ public static class LobbyViewPanePatches
|
||||
var num = viewPanel.yPosStart;
|
||||
var num2 = 1.08f;
|
||||
|
||||
var filteredOptions = SelectedMod.InternalOptionGroups
|
||||
var filteredOptions = SelectedMod.OptionGroups
|
||||
.Where(x => x.OptionableType == roleType)
|
||||
.SelectMany(x=>x.Options)
|
||||
.ToList();
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
using HarmonyLib;
|
||||
using Il2CppSystem;
|
||||
using MiraAPI.Roles;
|
||||
using UnityEngine;
|
||||
using Object = Il2CppSystem.Object;
|
||||
|
||||
namespace MiraAPI.Patches.Options;
|
||||
|
||||
@@ -25,13 +23,15 @@ public static class NotificationPopperPatch
|
||||
return true;
|
||||
}
|
||||
|
||||
var textColor = customRole.OptionsMenuColor.ToTextColor();
|
||||
var text = teamType == RoleTeamTypes.Crewmate
|
||||
? Palette.CrewmateSettingChangeText.ToTextColor()
|
||||
: Palette.ImpostorRed.ToTextColor();
|
||||
|
||||
var item = TranslationController.Instance.GetString(
|
||||
StringNames.LobbyChangeSettingNotificationRole,
|
||||
string.Concat(
|
||||
"<font=\"Barlow-Black SDF\" material=\"Barlow-Black Outline\">",
|
||||
textColor,
|
||||
text,
|
||||
TranslationController.Instance.GetString(key, Array.Empty<Object>()),
|
||||
"</color></font>"
|
||||
),
|
||||
|
||||
@@ -23,32 +23,8 @@ public static class OptionsPatches
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(ToggleOption), nameof(ToggleOption.Toggle))]
|
||||
// UpdateValue was inlined.
|
||||
[HarmonyPatch(typeof(ToggleOption), nameof(ToggleOption.UpdateValue))]
|
||||
public static bool ToggleUpdate(ToggleOption __instance)
|
||||
{
|
||||
if (!__instance.IsCustom())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.CheckMark.enabled = !__instance.CheckMark.enabled;
|
||||
__instance.OnValueChanged.Invoke(__instance);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(StringOption), nameof(StringOption.UpdateValue))]
|
||||
[HarmonyPatch(typeof(NumberOption), nameof(NumberOption.UpdateValue))]
|
||||
public static bool UpdateValuePrefix(OptionBehaviour __instance)
|
||||
{
|
||||
return !__instance.IsCustom();
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(NumberOption), nameof(NumberOption.AdjustButtonsActiveState))]
|
||||
[HarmonyPatch(typeof(StringOption), nameof(StringOption.AdjustButtonsActiveState))]
|
||||
public static bool AdjustButtonsPrefix(OptionBehaviour __instance)
|
||||
{
|
||||
return !__instance.IsCustom();
|
||||
}
|
||||
@@ -62,84 +38,17 @@ public static class OptionsPatches
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.TitleText.text = TranslationController.Instance.GetString(__instance.Title, Array.Empty<Object>());
|
||||
__instance.TitleText.text =
|
||||
TranslationController.Instance.GetString(__instance.Title, Array.Empty<Object>());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(NumberOption), nameof(NumberOption.Increase))]
|
||||
public static bool NumberIncrease(NumberOption __instance)
|
||||
[HarmonyPatch(typeof(NumberOption), nameof(NumberOption.UpdateValue))]
|
||||
public static bool NumberUpdate(NumberOption __instance)
|
||||
{
|
||||
if (!__instance.IsCustom())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.Value += __instance.Increment;
|
||||
if (__instance.Value > __instance.ValidRange.max)
|
||||
{
|
||||
__instance.Value = __instance.ValidRange.min;
|
||||
}
|
||||
__instance.OnValueChanged.Invoke(__instance);
|
||||
__instance.AdjustButtonsActiveState();
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(NumberOption), nameof(NumberOption.Decrease))]
|
||||
public static bool NumberDecrease(NumberOption __instance)
|
||||
{
|
||||
if (!__instance.IsCustom())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.Value -= __instance.Increment;
|
||||
if (__instance.Value < __instance.ValidRange.min)
|
||||
{
|
||||
__instance.Value = __instance.ValidRange.max;
|
||||
}
|
||||
__instance.OnValueChanged.Invoke(__instance);
|
||||
__instance.AdjustButtonsActiveState();
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(StringOption), nameof(StringOption.Increase))]
|
||||
public static bool StringOptionIncrease(StringOption __instance)
|
||||
{
|
||||
if (!__instance.IsCustom())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.Value += 1;
|
||||
if (__instance.Value >= __instance.Values.Length)
|
||||
{
|
||||
__instance.Value = 0;
|
||||
}
|
||||
__instance.OnValueChanged.Invoke(__instance);
|
||||
__instance.AdjustButtonsActiveState();
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(StringOption), nameof(StringOption.Decrease))]
|
||||
public static bool StringOptionDecrease(StringOption __instance)
|
||||
{
|
||||
if (!__instance.IsCustom())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.Value -= 1;
|
||||
if (__instance.Value < 0)
|
||||
{
|
||||
__instance.Value = __instance.Values.Length - 1;
|
||||
}
|
||||
__instance.OnValueChanged.Invoke(__instance);
|
||||
__instance.AdjustButtonsActiveState();
|
||||
return false;
|
||||
return !__instance.IsCustom();
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
@@ -151,9 +60,19 @@ public static class OptionsPatches
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.TitleText.text = TranslationController.Instance.GetString(__instance.Title, Array.Empty<Object>());
|
||||
__instance.ValueText.text = TranslationController.Instance.GetString(__instance.Values[__instance.Value], Array.Empty<Object>());
|
||||
__instance.TitleText.text =
|
||||
TranslationController.Instance.GetString(__instance.Title, Array.Empty<Object>());
|
||||
__instance.ValueText.text = TranslationController.Instance.GetString(
|
||||
__instance.Values[__instance.Value],
|
||||
Array.Empty<Object>());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(StringOption), nameof(StringOption.UpdateValue))]
|
||||
public static bool StringUpdate(StringOption __instance)
|
||||
{
|
||||
return !__instance.IsCustom();
|
||||
}
|
||||
}
|
||||
@@ -22,24 +22,15 @@ namespace MiraAPI.Patches.Options;
|
||||
[HarmonyPatch(typeof(RolesSettingsMenu))]
|
||||
public static class RoleSettingMenuPatches
|
||||
{
|
||||
public static Dictionary<int, Vector3> RolePositions { get; set; } = [];
|
||||
|
||||
private static Dictionary<RoleOptionsGroup, bool> RoleGroupHidden { get; set; } = [];
|
||||
private static List<GameObject> Headers { get; set; } = [];
|
||||
private static List<RoleOptionSetting> RoleOptionSettings { get; set; } = [];
|
||||
private static Dictionary<RoleOptionsGroup, bool> RoleGroupHidden { get; set; } = new();
|
||||
private static List<GameObject> Headers { get; set; } = new();
|
||||
private static List<RoleOptionSetting> RoleOptionSettings { get; set; } = new();
|
||||
|
||||
private static float ScrollerNum { get; set; } = 0.522f;
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(RolesSettingsMenu.SetQuotaTab))]
|
||||
public static void SetQuotaTabPostfix(RolesSettingsMenu __instance)
|
||||
{
|
||||
__instance.scrollBar.SetScrollBounds(__instance);
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(RolesSettingsMenu.SetQuotaTab))]
|
||||
public static bool SetQuotaTabPatch(RolesSettingsMenu __instance)
|
||||
public static bool PatchStart(RolesSettingsMenu __instance)
|
||||
{
|
||||
Headers.ForEach(Object.Destroy);
|
||||
RoleOptionSettings.ForEach(Object.Destroy);
|
||||
@@ -76,7 +67,7 @@ public static class RoleSettingMenuPatches
|
||||
|
||||
var num3 = 0;
|
||||
|
||||
var roleGroups = GameSettingMenuPatches.SelectedMod?.InternalRoles.Values.OfType<ICustomRole>()
|
||||
var roleGroups = GameSettingMenuPatches.SelectedMod?.CustomRoles.Values.OfType<ICustomRole>()
|
||||
.ToLookup(x => x.RoleOptionsGroup);
|
||||
|
||||
if (roleGroups is null)
|
||||
@@ -92,9 +83,10 @@ public static class RoleSettingMenuPatches
|
||||
var quotaThing = __instance.categoryHeaderEditRoleOrigin.transform.FindChild("QuotaHeader");
|
||||
var template = __instance.transform.parent.parent.GetComponent<GameSettingMenu>().GameSettingsTab.categoryHeaderOrigin;
|
||||
|
||||
|
||||
foreach (var grouping in sortedRoleGroups)
|
||||
{
|
||||
if (!grouping.Any() || grouping.All(x=>x.Configuration.HideSettings))
|
||||
if (!grouping.Any())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -234,28 +226,13 @@ public static class RoleSettingMenuPatches
|
||||
ScrollerNum -= 0.4f;
|
||||
}
|
||||
}
|
||||
__instance.scrollBar.SetScrollBounds(__instance);
|
||||
__instance.scrollBar.SetScrollBounds();
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void SetScrollBounds(this Scroller scroller, RolesSettingsMenu roleMenu)
|
||||
private static void SetScrollBounds(this Scroller scroller)
|
||||
{
|
||||
if (GameSettingMenuPatches.SelectedModIdx == 0)
|
||||
{
|
||||
scroller.CalculateAndSetYBounds(roleMenu.roleChances.Count + 3, 1f, 6f, 0.43f);
|
||||
return;
|
||||
}
|
||||
|
||||
scroller.CalculateAndSetYBounds(1.5f * Headers.Count + RoleOptionSettings.Count, 1f, 6f, 0.43f);
|
||||
if (RolePositions.TryGetValue(GameSettingMenuPatches.SelectedModIdx, out var scroll))
|
||||
{
|
||||
scroller.Inner.localPosition = scroll;
|
||||
scroller.UpdateScrollBars();
|
||||
}
|
||||
else
|
||||
{
|
||||
scroller.ScrollToTop();
|
||||
}
|
||||
scroller.CalculateAndSetYBounds(1 + 1.5f * Headers.Count + RoleOptionSettings.Count, 1f, 6f, 0.43f);
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
@@ -269,12 +246,13 @@ public static class RoleSettingMenuPatches
|
||||
[HarmonyPatch(nameof(RolesSettingsMenu.OpenChancesTab))]
|
||||
public static void OpenChancesTabPostfix(RolesSettingsMenu __instance)
|
||||
{
|
||||
if (!__instance.scrollBar)
|
||||
if (GameSettingMenuPatches.SelectedModIdx == 0 || !__instance.scrollBar)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
__instance.scrollBar.SetScrollBounds(__instance);
|
||||
__instance.scrollBar.SetScrollBounds();
|
||||
__instance.scrollBar.ScrollToTop();
|
||||
}
|
||||
|
||||
private static void ValueChanged(OptionBehaviour obj)
|
||||
@@ -332,7 +310,7 @@ public static class RoleSettingMenuPatches
|
||||
var num = hasImage ? -0.872f : -1;
|
||||
|
||||
// TODO: create sub groups under the role settings.
|
||||
var filteredOptions = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups
|
||||
var filteredOptions = GameSettingMenuPatches.SelectedMod?.OptionGroups
|
||||
.Where(x=> x.OptionableType == role.GetType())
|
||||
.SelectMany(x=>x.Options)
|
||||
.ToList() ?? [];
|
||||
@@ -379,8 +357,6 @@ public static class RoleSettingMenuPatches
|
||||
return;
|
||||
}
|
||||
|
||||
RolePositions[GameSettingMenuPatches.SelectedModIdx] = __instance.scrollBar.Inner.localPosition;
|
||||
|
||||
__instance.roleDescriptionText.text = customRole.RoleLongDescription;
|
||||
__instance.roleTitleText.text = TranslationController.Instance.GetString(
|
||||
role.StringName,
|
||||
@@ -498,7 +474,7 @@ public static class RoleSettingMenuPatches
|
||||
roleOptionSetting.titleText.horizontalAlignment = HorizontalAlignmentOptions.Left;
|
||||
|
||||
if (GameSettingMenuPatches.SelectedMod is null ||
|
||||
GameSettingMenuPatches.SelectedMod.InternalOptionGroups
|
||||
GameSettingMenuPatches.SelectedMod.OptionGroups
|
||||
.Exists(x => x.OptionableType == role.GetType()))
|
||||
{
|
||||
var newButton = Object.Instantiate(roleOptionSetting.buttons[0], roleOptionSetting.transform);
|
||||
@@ -530,7 +506,7 @@ public static class RoleSettingMenuPatches
|
||||
roleOptionSetting.ChancePlusBtn.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
if (index < GameSettingMenuPatches.SelectedMod?.InternalRoles.Count - 1)
|
||||
if (index < GameSettingMenuPatches.SelectedMod?.CustomRoles.Count - 1)
|
||||
{
|
||||
ScrollerNum -= 0.43f;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ using MiraAPI.Events.Vanilla.Gameplay;
|
||||
using MiraAPI.Events.Vanilla.Player;
|
||||
using MiraAPI.Hud;
|
||||
using MiraAPI.Modifiers;
|
||||
using MiraAPI.Utilities;
|
||||
using MiraAPI.Roles;
|
||||
using MiraAPI.Voting;
|
||||
using Reactor.Utilities.Extensions;
|
||||
|
||||
@@ -15,11 +15,14 @@ namespace MiraAPI.Patches;
|
||||
/// General patches for the PlayerControl class.
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(PlayerControl))]
|
||||
internal static class PlayerControlPatches
|
||||
public static class PlayerControlPatches
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the modifier component to the player on start.
|
||||
/// </summary>
|
||||
/// <param name="__instance">PlayerControl instance.</param>
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(PlayerControl.Start))]
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public static void PlayerControlStartPostfix(PlayerControl __instance)
|
||||
{
|
||||
if (__instance.gameObject.TryGetComponent<ModifierComponent>(out var modifierComp))
|
||||
@@ -36,12 +39,15 @@ internal static class PlayerControlPatches
|
||||
__instance.gameObject.AddComponent<PlayerVoteData>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls the OnDeath method for all active modifiers.
|
||||
/// </summary>
|
||||
/// <param name="__instance">PlayerControl instance.</param>
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(PlayerControl.Die))]
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public static void PlayerControlDiePostfix(PlayerControl __instance, DeathReason reason)
|
||||
{
|
||||
var deathEvent = new PlayerDeathEvent(__instance, reason, Helpers.GetBodyById(__instance.PlayerId));
|
||||
var deathEvent = new PlayerDeathEvent(__instance, reason);
|
||||
MiraEventManager.InvokeEvent(deathEvent);
|
||||
|
||||
var modifiersComponent = __instance.GetComponent<ModifierComponent>();
|
||||
@@ -52,9 +58,13 @@ internal static class PlayerControlPatches
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to trigger the <see cref="CompleteTaskEvent"/>.
|
||||
/// </summary>
|
||||
/// <param name="__instance">PlayerControl instance.</param>
|
||||
/// <param name="idx">The task id.</param>
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(PlayerControl.CompleteTask))]
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public static void PlayerCompleteTaskPostfix(PlayerControl __instance, uint idx)
|
||||
{
|
||||
var playerTask = __instance.myTasks.ToArray().First(playerTask => playerTask.Id == idx);
|
||||
@@ -65,10 +75,14 @@ internal static class PlayerControlPatches
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to trigger the <see cref="BeforeMurderEvent"/>.
|
||||
/// </summary>
|
||||
/// <param name="__instance">The source player.</param>
|
||||
/// <param name="target">The target.</param>
|
||||
/// <param name="didSucceed">Whether the kill succeeded.</param>
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(PlayerControl.RpcMurderPlayer))]
|
||||
// ReSharper disable once InconsistentNaming
|
||||
// Note: This method is partially inlined in 2025.5.20 but we already use custom murder RPCs.
|
||||
public static void PlayerControlMurderPrefix(PlayerControl __instance, PlayerControl target, ref bool didSucceed)
|
||||
{
|
||||
var beforeMurderEvent = new BeforeMurderEvent(__instance, target);
|
||||
@@ -77,9 +91,12 @@ internal static class PlayerControlPatches
|
||||
didSucceed = beforeMurderEvent.IsCancelled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// FixedUpdate handler for custom roles and custom buttons.
|
||||
/// </summary>
|
||||
/// <param name="__instance">PlayerControl instance.</param>
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(PlayerControl.FixedUpdate))]
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public static void PlayerControlFixedUpdatePostfix(PlayerControl __instance)
|
||||
{
|
||||
if (!__instance.AmOwner)
|
||||
@@ -102,4 +119,15 @@ internal static class PlayerControlPatches
|
||||
button.FixedUpdateHandler(__instance);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear from modifier component cache.
|
||||
/// </summary>
|
||||
/// <param name="__instance">PlayerControl instance.</param>
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(PlayerControl.OnDestroy))]
|
||||
public static void PlayerControlOnDestroyPrefix(PlayerControl __instance)
|
||||
{
|
||||
ModifierExtensions.ModifierComponents.Remove(__instance);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Events;
|
||||
using MiraAPI.Events.Vanilla.Gameplay;
|
||||
using MiraAPI.Events.Vanilla.Meeting;
|
||||
using MiraAPI.Roles;
|
||||
|
||||
@@ -10,7 +9,7 @@ namespace MiraAPI.Patches.Roles;
|
||||
/// Patches for custom ejection messages.
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(ExileController))]
|
||||
internal static class EjectionPatches
|
||||
public static class EjectionPatches
|
||||
{
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(ExileController.Begin))]
|
||||
@@ -25,21 +24,11 @@ internal static class EjectionPatches
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GameManager.Instance.LogicOptions.GetConfirmImpostor() || role.GetCustomEjectionMessage(__instance.initData.networkedPlayer) == null)
|
||||
if (role.GetCustomEjectionMessage(__instance.initData.networkedPlayer) == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
__instance.completeString = role.GetCustomEjectionMessage(__instance.initData.networkedPlayer);
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(ExileController), nameof(ExileController.WrapUp))]
|
||||
public static class WrapUpPatch
|
||||
{
|
||||
public static void Postfix()
|
||||
{
|
||||
var @event = new RoundStartEvent(false);
|
||||
MiraEventManager.InvokeEvent(@event);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ public static class GameStartupPatch
|
||||
private static bool _runOnce;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for registering roles when the game opens, might be a janky solution, but it works.
|
||||
/// This is used for registering roles when the game opens, might be a janky solution, but it works
|
||||
/// </summary>
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(MainMenuManager.Start))]
|
||||
|
||||
@@ -7,8 +7,7 @@ namespace MiraAPI.Patches.Roles;
|
||||
/// <summary>
|
||||
/// Patches the haunt menu to show the actual role name rather then team.
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(HauntMenuMinigame), nameof(HauntMenuMinigame.SetHauntTarget))]
|
||||
// SetFilterText was INLINED
|
||||
[HarmonyPatch(typeof(HauntMenuMinigame), nameof(HauntMenuMinigame.SetFilterText))]
|
||||
public static class HauntMenuMinigamePatch
|
||||
{
|
||||
public static void Postfix(HauntMenuMinigame __instance)
|
||||
@@ -25,4 +24,4 @@ public static class HauntMenuMinigamePatch
|
||||
__instance.FilterText.text = role.NiceName;
|
||||
__instance.FilterText.color = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,29 +5,15 @@ using Reactor.Utilities.Extensions;
|
||||
|
||||
namespace MiraAPI.Patches.Roles;
|
||||
|
||||
/// <summary>
|
||||
/// HudManager patches for roles.
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(HudManager))]
|
||||
public static class HudManagerPatches
|
||||
internal static class HudManagerPatches
|
||||
{
|
||||
// Custom role tab.
|
||||
private static TaskPanelBehaviour? _roleTab;
|
||||
|
||||
/// <summary>
|
||||
/// Fixes Kill Button not showing for Neutral killing role.
|
||||
/// </summary>
|
||||
/// <param name="__instance">HudManager instance.</param>
|
||||
/// <param name="localPlayer">The local PlayerControl.</param>
|
||||
/// <param name="role">The player's RoleBehaviour.</param>
|
||||
/// <param name="isActive">Whether the Hud should be set active or not.</param>
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(HudManager.SetHudActive), typeof(PlayerControl), typeof(RoleBehaviour), typeof(bool))]
|
||||
public static void SetHudActivePostfix(
|
||||
HudManager __instance,
|
||||
PlayerControl localPlayer,
|
||||
RoleBehaviour role,
|
||||
bool isActive)
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public static void SetHudActivePostfix(HudManager __instance, PlayerControl localPlayer, RoleBehaviour role, bool isActive)
|
||||
{
|
||||
var flag = localPlayer.Data != null && localPlayer.Data.IsDead;
|
||||
|
||||
@@ -44,11 +30,9 @@ public static class HudManagerPatches
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update custom role tab and custom role hud elements.
|
||||
/// </summary>
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(HudManager.Update))]
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public static void UpdatePostfix(HudManager __instance)
|
||||
{
|
||||
var local = PlayerControl.LocalPlayer;
|
||||
@@ -69,9 +53,9 @@ public static class HudManagerPatches
|
||||
|
||||
CustomRoleManager.UpdateRoleTab(_roleTab, customRole);
|
||||
}
|
||||
else if (_roleTab)
|
||||
else if (_roleTab != null)
|
||||
{
|
||||
_roleTab?.gameObject.Destroy();
|
||||
_roleTab.gameObject.Destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,60 +5,18 @@ using MiraAPI.Events;
|
||||
using MiraAPI.Events.Vanilla.Gameplay;
|
||||
using MiraAPI.Modifiers;
|
||||
using MiraAPI.Roles;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Patches.Roles;
|
||||
|
||||
[HarmonyPatch(typeof(RoleManager))]
|
||||
public static class RoleManagerPatches
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(RoleManager.SetRole))]
|
||||
// NOTE: As of 2025.5.20 update, this is INLINED in ONE place: GameManager::ReviveEveryoneFreeplay(void).
|
||||
// Should not affect normal gameplay, but may cause issues in Freeplay.
|
||||
public static bool SetRolePatch(RoleManager __instance, PlayerControl targetPlayer, RoleTypes roleType)
|
||||
public static void SetRolePatch(RoleManager __instance, PlayerControl targetPlayer, RoleTypes roleType)
|
||||
{
|
||||
if (!targetPlayer)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var data = targetPlayer.Data;
|
||||
if (data == null)
|
||||
{
|
||||
Debug.LogError("It shouldn't be possible, but " + targetPlayer.name + " still doesn't have PlayerData during role selection.");
|
||||
return false;
|
||||
}
|
||||
if (data.Role)
|
||||
{
|
||||
data.Role.Deinitialize(targetPlayer);
|
||||
Object.Destroy(data.Role.gameObject);
|
||||
}
|
||||
var roleBehaviour = Object.Instantiate<RoleBehaviour>(__instance.AllRoles.First(r => r.Role == roleType), data.gameObject.transform);
|
||||
roleBehaviour.Initialize(targetPlayer);
|
||||
targetPlayer.Data.Role = roleBehaviour;
|
||||
targetPlayer.Data.RoleType = roleType;
|
||||
|
||||
// basically everything is the same except this one if statement
|
||||
// innersloth decided to check the roleType manually instead of the role behaviour.
|
||||
if (!roleBehaviour.IsDead)
|
||||
{
|
||||
targetPlayer.Data.RoleWhenAlive = new Il2CppSystem.Nullable<RoleTypes>(roleType);
|
||||
}
|
||||
roleBehaviour.AdjustTasks(targetPlayer);
|
||||
switch (roleBehaviour.IsDead)
|
||||
{
|
||||
case true when !targetPlayer.Data.IsDead:
|
||||
targetPlayer.Die(DeathReason.Kill, false);
|
||||
return false;
|
||||
case false when targetPlayer.Data.IsDead:
|
||||
targetPlayer.Revive();
|
||||
break;
|
||||
}
|
||||
|
||||
var @event = new SetRoleEvent(targetPlayer, roleType);
|
||||
MiraEventManager.InvokeEvent(@event);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using AmongUs.GameOptions;
|
||||
using HarmonyLib;
|
||||
using Il2CppSystem.Collections.Generic;
|
||||
using MiraAPI.Roles;
|
||||
using Reactor.Utilities;
|
||||
|
||||
@@ -9,32 +8,15 @@ namespace MiraAPI.Patches.Roles;
|
||||
/// <summary>
|
||||
/// Patches to return the correct role counts.
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(RoleOptionsCollectionV09))]
|
||||
[HarmonyPatch(typeof(RoleOptionsCollectionV08))]
|
||||
public static class RoleOptionsCollectionPatch
|
||||
{
|
||||
/// <summary>
|
||||
/// This patch fixes GetNumPerGame being inlined (2025.5.20) in the original code.
|
||||
/// </summary>
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(RoleOptionsCollectionV09.AnyRolesEnabled))]
|
||||
public static bool AnyRolesEnabledPrefix(RoleOptionsCollectionV09 __instance, ref bool __result)
|
||||
{
|
||||
foreach (KeyValuePair<RoleTypes, RoleDataV09> keyValuePair in __instance.roles)
|
||||
{
|
||||
if (__instance.GetNumPerGame(keyValuePair.Key) > 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the role chance for custom Launchpad roles based on config.
|
||||
/// Set the role chance for custom Launchpad roles based on config
|
||||
/// </summary>
|
||||
/// <returns>Return false to skip original method, true to not.</returns>
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(RoleOptionsCollectionV09.GetChancePerGame))]
|
||||
[HarmonyPatch(nameof(RoleOptionsCollectionV08.GetChancePerGame))]
|
||||
public static bool GetChancePrefix(RoleTypes role, ref int __result)
|
||||
{
|
||||
if (!CustomRoleManager.GetCustomRoleBehaviour(role, out var customRole) || customRole == null)
|
||||
@@ -60,11 +42,11 @@ public static class RoleOptionsCollectionPatch
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the amount for custom Launchpad roles based on config.
|
||||
/// Set the amount for custom Launchpad roles based on config
|
||||
/// </summary>
|
||||
/// <returns>Return false to skip original method, true to not.</returns>
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(RoleOptionsCollectionV09.GetNumPerGame))]
|
||||
[HarmonyPatch(nameof(RoleOptionsCollectionV08.GetNumPerGame))]
|
||||
public static bool GetNumPrefix(RoleTypes role, ref int __result)
|
||||
{
|
||||
if (!CustomRoleManager.GetCustomRoleBehaviour(role, out var customRole) || customRole == null)
|
||||
|
||||
@@ -4,7 +4,7 @@ using MiraAPI.Roles;
|
||||
namespace MiraAPI.Patches.Roles;
|
||||
|
||||
[HarmonyPatch(typeof(SabotageButton))]
|
||||
public static class SabotageButtonPatch
|
||||
public class SabotageButtonPatch
|
||||
{
|
||||
/// <summary>
|
||||
/// Patches the Sabotage button to check if the player's custom role can use sabotage.
|
||||
|
||||
@@ -33,4 +33,15 @@ public static class MinigameStubs
|
||||
{
|
||||
// nothing needed
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fixes the error when creating custom minigames. Credit to Submerged for discovering this fix.
|
||||
/// </summary>
|
||||
/// <param name="__instance">The Minigame instance.</param>
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(Minigame), nameof(Minigame.Begin))]
|
||||
public static void MinigameErrorFixPatch(Minigame __instance)
|
||||
{
|
||||
__instance.logger ??= new Logger("Minigame", Logger.Level.Info, Logger.Category.Gameplay);
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,6 @@ using Il2CppSystem.Text;
|
||||
|
||||
namespace MiraAPI.Patches.Stubs;
|
||||
|
||||
/// <summary>
|
||||
/// Stub methods for the RoleBehaviour class. Needed because of Il2Cpp limitations on injected classes.
|
||||
/// </summary>
|
||||
[HarmonyPatch]
|
||||
public static class RoleBehaviourStubs
|
||||
{
|
||||
@@ -60,7 +57,7 @@ public static class RoleBehaviourStubs
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static bool CanUse(RoleBehaviour instance, IUsable console)
|
||||
{
|
||||
return false;
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -87,7 +84,7 @@ public static class RoleBehaviourStubs
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static bool DidWin(RoleBehaviour instance, GameOverReason gameOverReason)
|
||||
{
|
||||
return false;
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -100,7 +97,7 @@ public static class RoleBehaviourStubs
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static PlayerControl FindClosestTarget(RoleBehaviour instance)
|
||||
{
|
||||
return null!;
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -113,7 +110,7 @@ public static class RoleBehaviourStubs
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static float GetAbilityDistance(RoleBehaviour instance)
|
||||
{
|
||||
return 0;
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -127,7 +124,7 @@ public static class RoleBehaviourStubs
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static bool IsValidTarget(RoleBehaviour instance, NetworkedPlayerInfo target)
|
||||
{
|
||||
return false;
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -8,13 +8,17 @@ using UnityEngine;
|
||||
namespace MiraAPI.Patches;
|
||||
|
||||
/// <summary>
|
||||
/// Used to change vent behaviour for the event system, custom roles, and modifiers.
|
||||
/// Vent patches to make sure the player is able to use the vent.
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(Vent), nameof(Vent.CanUse))]
|
||||
internal static class VentCanUsePatch
|
||||
[HarmonyPatch(typeof(Vent))]
|
||||
public static class VentPatches
|
||||
{
|
||||
// ReSharper disable InconsistentNaming
|
||||
public static void Postfix(Vent __instance, ref float __result, [HarmonyArgument(0)] NetworkedPlayerInfo pc, [HarmonyArgument(1)] ref bool canUse, [HarmonyArgument(2)] ref bool couldUse)
|
||||
/// <summary>
|
||||
/// CanUse patch.
|
||||
/// </summary>
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(Vent.CanUse))]
|
||||
public static void VentCanUsePostfix(Vent __instance, ref float __result, [HarmonyArgument(0)] NetworkedPlayerInfo pc, [HarmonyArgument(1)] ref bool canUse, [HarmonyArgument(2)] ref bool couldUse)
|
||||
{
|
||||
var @event = new PlayerCanUseEvent(__instance.Cast<IUsable>());
|
||||
MiraEventManager.InvokeEvent(@event);
|
||||
@@ -1,18 +1,15 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using HarmonyLib;
|
||||
using Il2CppInterop.Runtime.InteropTypes.Arrays;
|
||||
using MiraAPI.Events;
|
||||
using MiraAPI.Events.Vanilla.Meeting;
|
||||
using MiraAPI.Events.Vanilla.Meeting.Voting;
|
||||
using MiraAPI.Modifiers;
|
||||
using MiraAPI.Utilities;
|
||||
using MiraAPI.Voting;
|
||||
|
||||
namespace MiraAPI.Patches.Voting;
|
||||
|
||||
[HarmonyPatch(typeof(MeetingHud))]
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Harmony Convention")]
|
||||
internal static class MeetingHudPatches
|
||||
{
|
||||
[HarmonyPostfix]
|
||||
@@ -29,62 +26,11 @@ internal static class MeetingHudPatches
|
||||
{
|
||||
voteData.SetRemainingVotes(0);
|
||||
}
|
||||
|
||||
foreach (var modifier in plr.GetModifierComponent().ActiveModifiers)
|
||||
{
|
||||
modifier.OnMeetingStart();
|
||||
}
|
||||
}
|
||||
|
||||
var @event = new StartMeetingEvent(__instance);
|
||||
MiraEventManager.InvokeEvent(@event);
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(MeetingHud.OnDestroy))]
|
||||
public static void OnDestroyPatch(MeetingHud __instance)
|
||||
{
|
||||
MiraEventManager.InvokeEvent(new EndMeetingEvent(__instance));
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(MeetingHud.VotingComplete))]
|
||||
public static void VotingCompletePatch(MeetingHud __instance)
|
||||
{
|
||||
MiraEventManager.InvokeEvent(new VotingCompleteEvent(__instance));
|
||||
}
|
||||
|
||||
// this is necessary because the actual ForceSkipAll method is inlined.
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(MeetingHud.Update))]
|
||||
public static void ForceSkipPatch(MeetingHud __instance)
|
||||
{
|
||||
if (__instance.state is not (MeetingHud.VoteStates.NotVoted or MeetingHud.VoteStates.Voted))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var logicOptionsNormal = GameManager.Instance.LogicOptions.Cast<LogicOptionsNormal>();
|
||||
var votingTime = logicOptionsNormal.GetVotingTime();
|
||||
if (votingTime <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var num2 = __instance.discussionTimer - logicOptionsNormal.GetDiscussionTime();
|
||||
|
||||
if (!AmongUsClient.Instance.AmHost || num2 < votingTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var plr in PlayerControl.AllPlayerControls)
|
||||
{
|
||||
var voteData = plr.GetVoteData();
|
||||
voteData.SetRemainingVotes(0);
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(MeetingHud.Select))]
|
||||
public static bool SelectPatch(MeetingHud __instance, int suspectStateIdx)
|
||||
@@ -140,6 +86,84 @@ internal static class MeetingHudPatches
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(MeetingHud.CastVote))]
|
||||
public static bool CastVotePatch(MeetingHud __instance, byte srcPlayerId, byte suspectPlayerId)
|
||||
{
|
||||
var plr = GameData.Instance.GetPlayerById(srcPlayerId);
|
||||
|
||||
if (plr == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var voteData = plr.Object.GetVoteData();
|
||||
if (voteData == null || voteData.VotesRemaining == 0 ||
|
||||
voteData.VotedFor(suspectPlayerId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
VotingUtils.HandleVote(voteData, suspectPlayerId);
|
||||
|
||||
__instance.SetDirtyBit(1U);
|
||||
__instance.CheckForEndVoting();
|
||||
|
||||
if (voteData.VotesRemaining != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
__instance.playerStates.First(x => x.TargetPlayerId == srcPlayerId).SetVote(suspectPlayerId);
|
||||
PlayerControl.LocalPlayer.RpcSendChatNote(srcPlayerId, ChatNoteTypes.DidVote);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(MeetingHud.CmdCastVote))]
|
||||
public static void CmdCastVotePatch(MeetingHud __instance, byte playerId, byte suspectIdx)
|
||||
{
|
||||
var plr = GameData.Instance.GetPlayerById(playerId);
|
||||
var voteData = plr.Object.GetVoteData();
|
||||
|
||||
if (!AmongUsClient.Instance.AmHost)
|
||||
{
|
||||
if (voteData == null || voteData.VotesRemaining == 0 ||
|
||||
voteData.VotedFor(suspectIdx))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
VotingUtils.HandleVote(voteData, suspectIdx);
|
||||
|
||||
if (voteData.VotesRemaining == 0)
|
||||
{
|
||||
__instance.playerStates.First(x => x.TargetPlayerId == playerId).SetVote(suspectIdx);
|
||||
}
|
||||
}
|
||||
|
||||
if (PlayerControl.LocalPlayer.PlayerId != playerId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SoundManager.Instance.PlaySound(__instance.VoteLockinSound, false);
|
||||
|
||||
foreach (var playerVoteArea in __instance.playerStates)
|
||||
{
|
||||
playerVoteArea.ClearButtons();
|
||||
}
|
||||
|
||||
__instance.SkipVoteButton.ClearButtons();
|
||||
|
||||
var localVoteData = PlayerControl.LocalPlayer.GetVoteData();
|
||||
if (localVoteData == null || localVoteData.VotesRemaining != 0) return;
|
||||
|
||||
__instance.SkipVoteButton.voteComplete = true;
|
||||
__instance.SkipVoteButton.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(MeetingHud.CheckForEndVoting))]
|
||||
public static bool EndCheck(MeetingHud __instance)
|
||||
@@ -202,21 +226,12 @@ internal static class MeetingHudPatches
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: figure out a way to do host-authorization since right now any player can send RpcCastVote
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(MeetingHud.CmdCastVote))]
|
||||
// Although this method is inlined in MeetingHud.Confirm, the next patch fixes that.
|
||||
public static bool CmdCastVoteOverridePatch(MeetingHud __instance, byte playerId, byte suspectIdx)
|
||||
{
|
||||
VotingUtils.RpcCastVote(PlayerControl.LocalPlayer, playerId, suspectIdx);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(MeetingHud.Confirm))]
|
||||
public static bool ConfirmPatch(MeetingHud __instance, [HarmonyArgument(0)] byte suspect)
|
||||
{
|
||||
__instance.CmdCastVote(PlayerControl.LocalPlayer.PlayerId, suspect);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -2,19 +2,8 @@
|
||||
|
||||
namespace MiraAPI.PluginLoading;
|
||||
|
||||
/// <summary>
|
||||
/// The interface that all Mira plugins must implement.
|
||||
/// </summary>
|
||||
public interface IMiraPlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name to display on the options menu.
|
||||
/// </summary>
|
||||
string OptionsTitleText { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the BepInEx configuration file for the plugin.
|
||||
/// </summary>
|
||||
/// <returns>The BepInEx configuration file for the plugin.</returns>
|
||||
public ConfigFile GetConfigFile();
|
||||
}
|
||||
@@ -6,9 +6,7 @@ using MiraAPI.GameModes;
|
||||
using MiraAPI.GameOptions;
|
||||
using MiraAPI.Hud;
|
||||
using MiraAPI.Modifiers;
|
||||
using MiraAPI.Presets;
|
||||
|
||||
// ReSharper disable UnusedAutoPropertyAccessor.Global
|
||||
namespace MiraAPI.PluginLoading;
|
||||
|
||||
/// <summary>
|
||||
@@ -25,58 +23,61 @@ public class MiraPluginInfo
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read only collection of this plugin's modifiers. This is not safe for modifiers with constructors.
|
||||
/// Get a read only collection of this plugin's modifiers. This is probably not safe because they are uninitialized.
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<BaseModifier> Modifiers { get; private set; } = null!;
|
||||
/// <returns>Readonly collection of modifiers.</returns>
|
||||
public IReadOnlyCollection<BaseModifier> GetModifiers()
|
||||
{
|
||||
return Modifiers.AsReadOnly();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read only collection of this plugin's Option Groups.
|
||||
/// Get a read only collection of this plugin's Option Groups.
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<AbstractOptionGroup> OptionGroups { get; private set; } = null!;
|
||||
/// <returns>Readonly collection of option groups.</returns>
|
||||
public IReadOnlyCollection<AbstractOptionGroup> GetOptionGroups()
|
||||
{
|
||||
return OptionGroups.AsReadOnly();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read only collection of this plugin's options.
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<IModdedOption> Options { get; private set; } = null!;
|
||||
/// <returns>Read only collection of options.</returns>
|
||||
public IReadOnlyCollection<IModdedOption> GetOptions()
|
||||
{
|
||||
return Options.AsReadOnly();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read only dictionary of Role IDs and the RoleBehaviour object they are associated with.
|
||||
/// </summary>
|
||||
public ReadOnlyDictionary<ushort, RoleBehaviour> Roles { get; private set; } = null!;
|
||||
/// <returns>Read only dictionary of IDs and Roles.</returns>
|
||||
public ReadOnlyDictionary<ushort, RoleBehaviour> GetRoles()
|
||||
{
|
||||
return new ReadOnlyDictionary<ushort, RoleBehaviour>(CustomRoles);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read only collection of this plugin's custom buttons.
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<CustomActionButton> Buttons { get; private set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read only dictionary of this plugin's custom game modes.
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<OptionPreset> Presets { get; internal set; } = null!;
|
||||
|
||||
internal void SavePublicCollections()
|
||||
/// <returns>Read only collection of buttons.</returns>
|
||||
public IReadOnlyCollection<CustomActionButton> GetButtons()
|
||||
{
|
||||
Presets = [..InternalPresets];
|
||||
Modifiers = [..InternalModifiers];
|
||||
OptionGroups = [..InternalOptionGroups];
|
||||
Options = [..InternalOptions];
|
||||
Roles = new ReadOnlyDictionary<ushort, RoleBehaviour>(InternalRoles);
|
||||
Buttons = [..InternalButtons];
|
||||
return Buttons.AsReadOnly();
|
||||
}
|
||||
|
||||
internal List<OptionPreset> InternalPresets { get; } = [];
|
||||
internal List<AbstractOptionGroup> OptionGroups { get; } = [];
|
||||
|
||||
internal List<AbstractOptionGroup> InternalOptionGroups { get; } = [];
|
||||
internal List<IModdedOption> Options { get; } = [];
|
||||
|
||||
internal List<IModdedOption> InternalOptions { get; } = [];
|
||||
internal List<BaseModifier> Modifiers { get; } = [];
|
||||
|
||||
internal List<BaseModifier> InternalModifiers { get; } = [];
|
||||
internal Dictionary<ushort, RoleBehaviour> CustomRoles { get; } = [];
|
||||
|
||||
internal Dictionary<ushort, RoleBehaviour> InternalRoles { get; } = [];
|
||||
internal Dictionary<int, CustomGameMode> GameModes { get; } = [];
|
||||
|
||||
internal Dictionary<int, CustomGameMode> InternalGameModes { get; } = [];
|
||||
|
||||
internal List<CustomActionButton> InternalButtons { get; } = [];
|
||||
internal List<CustomActionButton> Buttons { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin's ID, as defined in the plugin's BepInEx metadata.
|
||||
|
||||
@@ -12,7 +12,6 @@ using MiraAPI.GameOptions;
|
||||
using MiraAPI.GameOptions.Attributes;
|
||||
using MiraAPI.Hud;
|
||||
using MiraAPI.Modifiers;
|
||||
using MiraAPI.Presets;
|
||||
using MiraAPI.Roles;
|
||||
using MiraAPI.Utilities;
|
||||
using Reactor.Networking;
|
||||
@@ -27,9 +26,8 @@ public sealed class MiraPluginManager
|
||||
{
|
||||
private readonly Dictionary<Assembly, MiraPluginInfo> _registeredPlugins = [];
|
||||
|
||||
internal MiraPluginInfo[] RegisteredPlugins { get; private set; } = null!;
|
||||
|
||||
internal Dictionary<MiraPluginInfo, List<Type>> QueuedRoleRegistrations { get; } = [];
|
||||
internal MiraPluginInfo[] RegisteredPlugins() => [.. _registeredPlugins.Values];
|
||||
internal Dictionary<MiraPluginInfo, List<Type>> QueuedRoleRegistrations { get; } = new();
|
||||
internal static MiraPluginManager Instance { get; private set; } = new();
|
||||
|
||||
internal void Initialize()
|
||||
@@ -45,9 +43,6 @@ public sealed class MiraPluginManager
|
||||
var info = new MiraPluginInfo(miraPlugin, pluginInfo);
|
||||
var roles = new List<Type>();
|
||||
|
||||
var oldConfigSetting = info.PluginConfig.SaveOnConfigSet;
|
||||
info.PluginConfig.SaveOnConfigSet = false;
|
||||
|
||||
foreach (var type in assembly.GetTypes())
|
||||
{
|
||||
if (type.GetCustomAttribute<MiraIgnoreAttribute>() != null)
|
||||
@@ -55,29 +50,21 @@ public sealed class MiraPluginManager
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var method in type.GetMethods())
|
||||
foreach (var method in type.GetMethods(BindingFlags.Static | BindingFlags.Public))
|
||||
{
|
||||
var eventAttribute = method.GetCustomAttribute<RegisterEventAttribute>();
|
||||
if (eventAttribute == null)
|
||||
if (eventAttribute != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var parameters = method.GetParameters();
|
||||
if (parameters.Length != 1 || !parameters[0].ParameterType.IsSubclassOf(typeof(MiraEvent)))
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Invalid event registration method {method.Name} in {type.Name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!method.IsStatic)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Event method {method.Name} in {type.Name} must be static.");
|
||||
continue;
|
||||
var paramType = parameters[0].ParameterType;
|
||||
MiraEventManager.RegisterEventHandler(paramType, method, eventAttribute.Priority);
|
||||
}
|
||||
|
||||
var parameters = method.GetParameters();
|
||||
if (parameters.Length != 1 || !parameters[0].ParameterType.IsSubclassOf(typeof(MiraEvent)))
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Invalid event registration method {method.Name} in {type.Name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
var paramType = parameters[0].ParameterType;
|
||||
MiraEventManager.RegisterEventHandler(paramType, method, eventAttribute.Priority);
|
||||
}
|
||||
|
||||
if (RegisterModifier(type, info))
|
||||
@@ -90,13 +77,13 @@ public sealed class MiraPluginManager
|
||||
continue;
|
||||
}
|
||||
|
||||
if (RegisterRole(type, info, out var role))
|
||||
if (RegisterRoleAttribute(type, info, out var role))
|
||||
{
|
||||
roles.Add(role);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (RegisterButton(type, info))
|
||||
if (RegisterButtonAttribute(type, info))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -109,27 +96,17 @@ public sealed class MiraPluginManager
|
||||
RegisterColorClasses(type);
|
||||
}
|
||||
|
||||
info.PluginConfig.Save();
|
||||
info.PluginConfig.SaveOnConfigSet = oldConfigSetting;
|
||||
|
||||
info.InternalOptionGroups.Sort((x, y) => x.GroupPriority.CompareTo(y.GroupPriority));
|
||||
info.OptionGroups.Sort((x, y) => x.GroupPriority.CompareTo(y.GroupPriority));
|
||||
QueuedRoleRegistrations.Add(info, roles);
|
||||
|
||||
_registeredPlugins.Add(assembly, info);
|
||||
|
||||
info.SavePublicCollections();
|
||||
PresetManager.CreateDefaultPreset(info);
|
||||
PresetManager.LoadPresets(info);
|
||||
Logger<MiraApiPlugin>.Info($"Registering mod {pluginInfo.Metadata.GUID} with Mira API.");
|
||||
};
|
||||
IL2CPPChainloader.Instance.Finished += PaletteManager.RegisterAllColors;
|
||||
IL2CPPChainloader.Instance.Finished += MiraEventManager.SortAllHandlers;
|
||||
IL2CPPChainloader.Instance.Finished += () =>
|
||||
{
|
||||
// Save all buttons into a read-only collection for easy access
|
||||
CustomButtonManager.Buttons = new ReadOnlyCollection<CustomActionButton>(CustomButtonManager.CustomButtons);
|
||||
|
||||
// Cache all the registered plugins into an array for easy access
|
||||
RegisteredPlugins = [.._registeredPlugins.Values];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -172,12 +149,6 @@ public sealed class MiraPluginManager
|
||||
|
||||
foreach (var property in type.GetProperties())
|
||||
{
|
||||
if (property.GetMethod?.IsStatic == true)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Option property {property.Name} in {type.Name} must not be static.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (property.PropertyType.IsAssignableTo(typeof(IModdedOption)))
|
||||
{
|
||||
ModdedOptionsManager.RegisterPropertyOption(type, property, pluginInfo);
|
||||
@@ -193,21 +164,16 @@ public sealed class MiraPluginManager
|
||||
ModdedOptionsManager.RegisterAttributeOption(type, attribute, property, pluginInfo);
|
||||
}
|
||||
|
||||
foreach (var field in type.GetFields().Where(f => f.FieldType.IsAssignableTo(typeof(IModdedOption))))
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"{field.Name} is a field, not a property. Use properties for options.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Failed to register options for {type.Name}: {e.ToString()}");
|
||||
Logger<MiraApiPlugin>.Error($"Failed to register options for {type.Name}: {e}");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool RegisterRole(Type type, MiraPluginInfo pluginInfo, [NotNullWhen(true)] out Type? role)
|
||||
private static bool RegisterRoleAttribute(Type type, MiraPluginInfo pluginInfo, [NotNullWhen(true)] out Type? role)
|
||||
{
|
||||
role = null;
|
||||
try
|
||||
@@ -263,11 +229,6 @@ public sealed class MiraPluginManager
|
||||
|
||||
PaletteManager.CustomColors.Add(color);
|
||||
}
|
||||
|
||||
foreach (var field in type.GetFields().Where(f => f.FieldType.IsAssignableTo(typeof(CustomColor))))
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"{field.Name} is a field, not a property. Use properties for colors.");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -288,7 +249,7 @@ public sealed class MiraPluginManager
|
||||
}
|
||||
}
|
||||
|
||||
private static bool RegisterButton(Type type, MiraPluginInfo pluginInfo)
|
||||
private static bool RegisterButtonAttribute(Type type, MiraPluginInfo pluginInfo)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
using System.Linq;
|
||||
using BepInEx.Configuration;
|
||||
using MiraAPI.GameOptions;
|
||||
using MiraAPI.PluginLoading;
|
||||
using MiraAPI.Roles;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Presets;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a preset of game options that can be applied to the game.
|
||||
/// </summary>
|
||||
public class OptionPreset
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the preset.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin associated with the preset.
|
||||
/// </summary>
|
||||
public MiraPluginInfo Plugin { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the configuration file of the plugin associated with the preset.
|
||||
/// </summary>
|
||||
public ConfigFile PluginConfig { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the configuration file for the preset.
|
||||
/// </summary>
|
||||
public ConfigFile PresetConfig { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the button associated with the preset in the UI.
|
||||
/// </summary>
|
||||
public GameObject? PresetButton { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OptionPreset"/> class with the specified name and configuration file.
|
||||
/// </summary>
|
||||
/// <param name="name">>The name of the preset.</param>
|
||||
/// <param name="plugin">The plugin associated with the preset.</param>
|
||||
/// <param name="presetConfig">>The configuration file for the preset.</param>
|
||||
public OptionPreset(string name, MiraPluginInfo plugin, ConfigFile presetConfig)
|
||||
{
|
||||
Name = name;
|
||||
Plugin = plugin;
|
||||
PluginConfig = plugin.PluginConfig;
|
||||
PresetConfig = presetConfig;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the preset by applying the values from the preset configuration to the plugin configuration.
|
||||
/// </summary>
|
||||
public void LoadPreset()
|
||||
{
|
||||
PresetConfig.Reload();
|
||||
foreach (var option in Plugin.InternalOptions)
|
||||
{
|
||||
option.LoadFromPreset(PresetConfig);
|
||||
}
|
||||
|
||||
ModdedOptionsManager.SyncAllOptions();
|
||||
|
||||
foreach (var role in Plugin.InternalRoles.Values.OfType<ICustomRole>())
|
||||
{
|
||||
role.LoadFromPreset(PresetConfig);
|
||||
}
|
||||
|
||||
CustomRoleManager.SyncAllRoleSettings();
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BepInEx.Configuration;
|
||||
using MiraAPI.PluginLoading;
|
||||
using MiraAPI.Roles;
|
||||
using Reactor.Utilities;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Presets;
|
||||
|
||||
/// <summary>
|
||||
/// Provides functionality to manage and load game option presets for plugins.
|
||||
/// </summary>
|
||||
public static class PresetManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the directory where the presets are stored.
|
||||
/// </summary>
|
||||
public static string PresetDirectory { get; } = Path.GetFullPath("mira_presets", Application.persistentDataPath);
|
||||
|
||||
internal static void CreateDefaultPreset(MiraPluginInfo plugin)
|
||||
{
|
||||
var presetPath = Path.Combine(PresetDirectory, plugin.PluginId);
|
||||
if (!Directory.Exists(presetPath))
|
||||
{
|
||||
Directory.CreateDirectory(presetPath);
|
||||
}
|
||||
|
||||
var presetConfig = new ConfigFile(Path.Combine(presetPath, "Default.cfg"), false);
|
||||
|
||||
foreach (var option in plugin.InternalOptions.Where(x => x.IncludeInPreset))
|
||||
{
|
||||
option.SaveToPreset(presetConfig, true);
|
||||
}
|
||||
|
||||
foreach (var role in plugin.InternalRoles.Values.OfType<ICustomRole>())
|
||||
{
|
||||
role.SaveToPreset(presetConfig);
|
||||
}
|
||||
|
||||
presetConfig.Save();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the presets for the specified plugin by reading the configuration files from the preset directory.
|
||||
/// </summary>
|
||||
/// <param name="plugin">>The plugin for which the presets should be loaded.</param>
|
||||
public static void LoadPresets(MiraPluginInfo plugin)
|
||||
{
|
||||
foreach (var btn in plugin.InternalPresets.Select(x=>x.PresetButton))
|
||||
{
|
||||
if (btn != null)
|
||||
{
|
||||
Object.DestroyImmediate(btn);
|
||||
}
|
||||
}
|
||||
|
||||
plugin.InternalPresets.Clear();
|
||||
if (!Directory.Exists(PresetDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(PresetDirectory);
|
||||
}
|
||||
|
||||
var pluginPresetPath = Path.Combine(PresetDirectory, plugin.PluginId);
|
||||
if (!Directory.Exists(pluginPresetPath))
|
||||
{
|
||||
Directory.CreateDirectory(pluginPresetPath);
|
||||
}
|
||||
|
||||
foreach (var file in Directory.GetFiles(pluginPresetPath, "*.cfg"))
|
||||
{
|
||||
Logger<MiraApiPlugin>.Info($"Loading preset file {file}");
|
||||
var presetName = Path.GetFileNameWithoutExtension(file);
|
||||
var presetConfig = new ConfigFile(file, false)
|
||||
{
|
||||
SaveOnConfigSet = false,
|
||||
};
|
||||
|
||||
foreach (var option in plugin.InternalOptions.Where(x=>x.IncludeInPreset))
|
||||
{
|
||||
option.Bind(presetConfig);
|
||||
}
|
||||
|
||||
foreach (var role in plugin.InternalRoles.Values.OfType<ICustomRole>().Where(x=>!x.Configuration.HideSettings))
|
||||
{
|
||||
role.BindConfig(presetConfig);
|
||||
}
|
||||
|
||||
presetConfig.Save();
|
||||
|
||||
plugin.InternalPresets.Add(new OptionPreset(presetName, plugin, presetConfig));
|
||||
}
|
||||
plugin.Presets = [.. plugin.InternalPresets];
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
using System;
|
||||
using Il2CppInterop.Runtime.InteropTypes.Fields;
|
||||
using MiraAPI.Modifiers.ModifierDisplay;
|
||||
using MiraAPI.Utilities;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using Reactor.Utilities.Attributes;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.UI;
|
||||
|
||||
[RegisterInIl2Cpp]
|
||||
public class SavePresetPopup(nint ptr) : Minigame(ptr)
|
||||
{
|
||||
private PassiveButton saveButton;
|
||||
private PassiveButton closeButton;
|
||||
private TextMeshPro textBoxText;
|
||||
private TextBoxTMP textBox;
|
||||
|
||||
private Action<string> onSave;
|
||||
|
||||
// Cleanup holder object
|
||||
public void OnDestroy()
|
||||
{
|
||||
transform.parent.gameObject.Destroy();
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
var textboxHolder = transform.GetChild(1).GetChild(1);
|
||||
saveButton = transform.FindChild("SaveButton").GetComponent<PassiveButton>();
|
||||
closeButton = transform.FindChild("CloseButton").GetComponent<PassiveButton>();
|
||||
textBox = textboxHolder.GetChild(0).GetComponent<TextBoxTMP>();
|
||||
textBoxText = textBox.transform.GetChild(0).GetComponent<TextMeshPro>();
|
||||
|
||||
saveButton.OnClick = new Button.ButtonClickedEvent();
|
||||
saveButton.OnClick.AddListener((UnityAction)(() =>
|
||||
{
|
||||
onSave.Invoke(textBoxText.text);
|
||||
this.BaseClose();
|
||||
}));
|
||||
|
||||
closeButton.OnClick = new Button.ButtonClickedEvent();
|
||||
closeButton.OnClick.AddListener((UnityAction)(() =>
|
||||
{
|
||||
this.BaseClose();
|
||||
}));
|
||||
|
||||
textBox.OnChange = new Button.ButtonClickedEvent();
|
||||
textBox.OnChange.AddListener((UnityAction)(() =>
|
||||
{
|
||||
textBoxText.text = textBoxText.text.Replace(" ", string.Empty);
|
||||
}));
|
||||
|
||||
textBox.GetComponent<PassiveButton>().OnClick.AddListener((UnityAction)(() =>
|
||||
{
|
||||
textBox.GiveFocus();
|
||||
}));
|
||||
|
||||
foreach (var tmp in gameObject.GetComponentsInChildren<TextMeshPro>())
|
||||
{
|
||||
if (tmp.gameObject.transform.parent.name == "Textbox") continue;
|
||||
var text = tmp.text;
|
||||
tmp.text = $"<font=\"LiberationSans SDF\" material=\"LiberationSans SDF RadialMenu Material\">{text}</font>";
|
||||
}
|
||||
}
|
||||
|
||||
public static void CreatePopup(Action<string> saveAction)
|
||||
{
|
||||
// Because innerscuff doesnt account for Z value in Close animation
|
||||
var holder = new GameObject("PopupHolder")
|
||||
{
|
||||
transform =
|
||||
{
|
||||
parent = Camera.main!.transform,
|
||||
},
|
||||
};
|
||||
holder.transform.localPosition = new Vector3(0, -1f, -500);
|
||||
|
||||
var gameObject = Instantiate(MiraAssets.PresetSavePopup.LoadAsset(), holder.transform);
|
||||
var popup = gameObject.GetComponent<SavePresetPopup>();
|
||||
popup.onSave = saveAction;
|
||||
popup.Begin(null);
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.0 KiB |
Binary file not shown.
@@ -1,6 +1,8 @@
|
||||
using AmongUs.GameOptions;
|
||||
using Il2CppSystem.Web.Util;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using UnityEngine;
|
||||
using Helpers = MiraAPI.Utilities.Helpers;
|
||||
|
||||
namespace MiraAPI.Roles;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ using Reactor.Utilities;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TextCore;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace MiraAPI.Roles;
|
||||
@@ -59,9 +60,6 @@ public static class CustomRoleManager
|
||||
{
|
||||
roles.ForEach(x => RoleIds.Add(x, GetNextRoleId()));
|
||||
|
||||
var oldConfigSetting = pluginInfo.PluginConfig.SaveOnConfigSet;
|
||||
pluginInfo.PluginConfig.SaveOnConfigSet = false;
|
||||
|
||||
foreach (var roleType in roles)
|
||||
{
|
||||
ClassInjector.RegisterTypeInIl2Cpp(roleType);
|
||||
@@ -72,11 +70,8 @@ public static class CustomRoleManager
|
||||
continue;
|
||||
}
|
||||
|
||||
pluginInfo.InternalRoles.Add((ushort)role.Role, role);
|
||||
pluginInfo.CustomRoles.Add((ushort)role.Role, role);
|
||||
}
|
||||
|
||||
pluginInfo.PluginConfig.Save();
|
||||
pluginInfo.PluginConfig.SaveOnConfigSet = oldConfigSetting;
|
||||
}
|
||||
|
||||
private static RoleBehaviour? RegisterRole(Type roleType, MiraPluginInfo parentMod)
|
||||
@@ -161,7 +156,7 @@ public static class CustomRoleManager
|
||||
/// <returns>A MiraPluginInfo object representing the parent mod of the role.</returns>
|
||||
public static MiraPluginInfo FindParentMod(ICustomRole role)
|
||||
{
|
||||
return MiraPluginManager.Instance.RegisteredPlugins.First(plugin => plugin.InternalRoles.ContainsValue(role as RoleBehaviour ?? throw new InvalidOperationException()));
|
||||
return MiraPluginManager.Instance.RegisteredPlugins().First(plugin => plugin.CustomRoles.ContainsValue(role as RoleBehaviour ?? throw new InvalidOperationException()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -236,7 +231,7 @@ public static class CustomRoleManager
|
||||
// we dont know how other plugins handle their configs
|
||||
// this way, all the options are saved at once, instead of one by one
|
||||
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);
|
||||
plugin.PluginConfig.SaveOnConfigSet = false;
|
||||
@@ -271,7 +266,7 @@ public static class CustomRoleManager
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins)
|
||||
foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins())
|
||||
{
|
||||
plugin.PluginConfig.Save();
|
||||
plugin.PluginConfig.SaveOnConfigSet = oldConfigSetting[plugin];
|
||||
|
||||
Loaded 100 of 121 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user