mirror of
https://github.com/All-Of-Us-Mods/MiraAPI.git
synced 2026-10-19 15:51:48 +00:00
Refactor + untargeted meeting abilities
This commit is contained in:
1 parent
4b7f17fa30
commit
7bbbd7597d
15 files changed
+600
-73
No files matched your search
@@ -0,0 +1,33 @@
|
||||
using MiraAPI.Example.Modifiers;
|
||||
using MiraAPI.Example.Roles;
|
||||
using MiraAPI.Hud;
|
||||
using MiraAPI.Keybinds;
|
||||
using MiraAPI.MeetingAbilities;
|
||||
using MiraAPI.Modifiers;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Example.MeetingAbilities;
|
||||
|
||||
public class EndMeetingButton : MeetingActionButton
|
||||
{
|
||||
public override string Name => "End Meeting";
|
||||
|
||||
public override float Cooldown => 15;
|
||||
|
||||
public override float InitialCooldown => 30;
|
||||
|
||||
public override int MaxUses => 3;
|
||||
|
||||
public override LoadableAsset<Sprite> Sprite => ExampleAssets.ExampleButton;
|
||||
|
||||
public override bool Enabled(RoleBehaviour? role)
|
||||
{
|
||||
return PlayerControl.LocalPlayer != null && role is MayorRole;
|
||||
}
|
||||
|
||||
protected override void OnClick()
|
||||
{
|
||||
MeetingHud.Instance.RpcClose();
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ namespace MiraAPI.Hud;
|
||||
/// <see cref="CustomActionButton"/> that has multiple target objects.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the target objects.</typeparam>
|
||||
public abstract class MultiTargetButton<T> : CustomActionButton where T : MonoBehaviour
|
||||
public abstract class MultiTargetMeetingButton<T> : CustomActionButton where T : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the list of targets of the button.
|
||||
@@ -96,7 +96,7 @@ public class MeetingAbilityBehaviour : MonoBehaviour
|
||||
{
|
||||
Renderer.material.SetFloat("_Desat", 0.0f);
|
||||
CooldownText.text = string.Empty;
|
||||
UsesText.text = _button.LimitedUses ? _button.UsesRemaining.ToString(CultureInfo.InvariantCulture) : string.Empty;
|
||||
UsesText.text = _button.LimitedUses ? _button.UsesLeft.ToString(CultureInfo.InvariantCulture) : string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -0,0 +1,321 @@
|
||||
using System;
|
||||
using MiraAPI.Events;
|
||||
using MiraAPI.Events.Mira;
|
||||
using MiraAPI.Hud;
|
||||
using MiraAPI.Utilities;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.UI;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace MiraAPI.MeetingAbilities;
|
||||
|
||||
/// <summary>
|
||||
/// Class for making custom meeting action buttons.
|
||||
/// </summary>
|
||||
public abstract class MeetingActionButton
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name and text of the button.
|
||||
/// </summary>
|
||||
public abstract string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the initial cooldown duration in seconds.
|
||||
/// </summary>
|
||||
public virtual float InitialCooldown => Cooldown;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the button's cooldown duration in seconds.
|
||||
/// </summary>
|
||||
public abstract float Cooldown { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sprite of the button. Use <see cref="LoadableResourceAsset"/> to load a sprite from a resource path. Use <see cref="LoadableBundleAsset{T}"/> to load a sprite from an asset bundle.
|
||||
/// </summary>
|
||||
public abstract LoadableAsset<Sprite> Sprite { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the format string for the cooldown timer.
|
||||
/// </summary>
|
||||
public virtual string CooldownTimerFormatString => "0";
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the button has limited uses.
|
||||
/// </summary>
|
||||
public bool LimitedUses => ZeroIsInfinite ? MaxUses > 0 : MaxUses >= 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum number of uses the button has. If the button has infinite uses, set to 0 or -1 based on what value that ZeroIsInfinite is set to.
|
||||
/// </summary>
|
||||
public virtual int MaxUses => ZeroIsInfinite ? 0 : -1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value indicating uses mode.
|
||||
/// </summary>
|
||||
public virtual MeetingButtonUsesMode UsesMode => MeetingButtonUsesMode.PerGame;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the button's text outline <see cref="Color"/>.
|
||||
/// </summary>
|
||||
public virtual Color TextOutlineColor => Color.clear;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether limited uses are determined via zero or a negative number of uses.
|
||||
/// </summary>
|
||||
public virtual bool ZeroIsInfinite { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the timer is currently active.
|
||||
/// </summary>
|
||||
public bool TimerPaused { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the amount of uses left.
|
||||
/// </summary>
|
||||
public int UsesLeft { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timer variable to measure cooldowns and effects.
|
||||
/// </summary>
|
||||
public float Timer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="ActionButton"/> object in game. This is created by Mira API automatically.
|
||||
/// </summary>
|
||||
public ActionButton? Button { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The method used to create the button.
|
||||
/// </summary>
|
||||
/// <param name="parent">The parent of the button.</param>
|
||||
public virtual void CreateButton(Transform parent)
|
||||
{
|
||||
if (Button)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Timer = AmongUsClient.Instance?.NetworkMode == NetworkModes.FreePlay ? 0 : InitialCooldown;
|
||||
TimerPaused = false;
|
||||
|
||||
Button = Object.Instantiate(MeetingHud.Instance.MeetingAbilityButton, parent);
|
||||
Button.name = Name + "Button";
|
||||
Button.OverrideText(Name.ToUpperInvariant());
|
||||
|
||||
Button.graphic.sprite = Sprite.LoadAsset();
|
||||
|
||||
Button.SetUsesRemaining(UsesLeft);
|
||||
if (MaxUses <= 0)
|
||||
{
|
||||
Button.SetInfiniteUses();
|
||||
}
|
||||
|
||||
if (TextOutlineColor != Color.clear)
|
||||
{
|
||||
SetTextOutline(TextOutlineColor);
|
||||
}
|
||||
|
||||
var pb = Button.GetComponent<PassiveButton>();
|
||||
pb.OnClick = new Button.ButtonClickedEvent();
|
||||
pb.OnClick.AddListener((UnityAction)(() =>
|
||||
{
|
||||
ClickHandler();
|
||||
}));
|
||||
Button.gameObject.SetActive(Enabled(PlayerControl.LocalPlayer.Data.Role));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A utility function to change the outline <see cref="Color"/> of the button's text.
|
||||
/// </summary>
|
||||
/// <param name="color">The new <see cref="Color"/>.</param>
|
||||
public virtual void SetTextOutline(Color color)
|
||||
{
|
||||
Button?.buttonLabelText.SetOutlineColor(color);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A utility function to override the <see cref="UnityEngine.Sprite"/> of the button.
|
||||
/// </summary>
|
||||
/// <param name="sprite">The new <see cref="UnityEngine.Sprite"/> to override with.</param>
|
||||
public virtual void OverrideSprite(Sprite sprite)
|
||||
{
|
||||
if (Button != null)
|
||||
{
|
||||
Button.graphic.sprite = sprite;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A utility function to override the name of the button.
|
||||
/// </summary>
|
||||
/// <param name="name">The new name to override with.</param>
|
||||
public virtual void OverrideName(string name)
|
||||
{
|
||||
Button?.OverrideText(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the button's timer.
|
||||
/// </summary>
|
||||
/// <param name="time">The time you want to set to.</param>
|
||||
public virtual void SetTimer(float time)
|
||||
{
|
||||
Timer = Mathf.Clamp(time, -1, float.MaxValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increase the button's timer.
|
||||
/// </summary>
|
||||
/// <param name="amount">The amount you want to increase by.</param>
|
||||
public virtual void IncreaseTimer(float amount)
|
||||
{
|
||||
SetTimer(Timer + amount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrease the button's timer.
|
||||
/// </summary>
|
||||
/// <param name="amount">The amount you want to decrease by.</param>
|
||||
public virtual void DecreaseTimer(float amount)
|
||||
{
|
||||
SetTimer(Timer - amount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets whether the timer is paused or not.
|
||||
/// </summary>
|
||||
/// <param name="val">Whether you want to pause/resume the timer.</param>
|
||||
public virtual void SetTimerPaused(bool val)
|
||||
{
|
||||
TimerPaused = val;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the amount of uses this button has left.
|
||||
/// </summary>
|
||||
/// <param name="amount">The amount you want to set to.</param>
|
||||
public virtual void SetUses(int amount)
|
||||
{
|
||||
UsesLeft = Mathf.Clamp(amount, 0, int.MaxValue);
|
||||
Button?.SetUsesRemaining(UsesLeft);
|
||||
|
||||
if (Button != null)
|
||||
{
|
||||
Button.usesRemainingSprite.color = UsesLeft == 0 ? Color.red : Color.white;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increase the amount of uses this button has left.
|
||||
/// </summary>
|
||||
/// <param name="amount">The amount you want to increase by. Default: 1.</param>
|
||||
public virtual void IncreaseUses(int amount = 1)
|
||||
{
|
||||
SetUses(UsesLeft + amount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrease the amount of uses this button has left.
|
||||
/// </summary>
|
||||
/// <param name="amount">The amount you want to decrease by. Default: 1.</param>
|
||||
public virtual void DecreaseUses(int amount = 1)
|
||||
{
|
||||
SetUses(UsesLeft - amount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A utility function that runs with the local player's <see cref="MeetingHud.Update"/> if the button is enabled.
|
||||
/// </summary>
|
||||
/// <param name="hud">the <see cref="MeetingHud"/> instance.</param>
|
||||
protected virtual void FixedUpdate(MeetingHud hud)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback method for the button click event.
|
||||
/// </summary>
|
||||
protected abstract void OnClick();
|
||||
|
||||
/// <summary>
|
||||
/// This method determines if the button should be active or not.
|
||||
/// <see langword="true"/> means the button is active, <see langword="false"/> means the button is disabled.
|
||||
/// </summary>
|
||||
/// <param name="role">The <see cref="RoleBehaviour"/> of the local player.</param>
|
||||
/// <returns><see langword="true"/> if the button is enabled, <see langword="false"/> otherwise.</returns>
|
||||
public abstract bool Enabled(RoleBehaviour? role);
|
||||
|
||||
/// <summary>
|
||||
/// When the button is usable, this method is called to determine if the button can be clicked.
|
||||
/// By default, it takes into account the timer, effect, and uses.
|
||||
/// You can override it to change the behavior.
|
||||
/// </summary>
|
||||
/// <returns>A value that represents whether the button can be clicked or not.</returns>
|
||||
public virtual bool CanClick()
|
||||
{
|
||||
return Timer <= 0 && CanUse();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the button should light up or not. This is also the base for CanClick.
|
||||
/// You can override it to change the behaviour. Do not include timer in here, that is for CanClick.
|
||||
/// </summary>
|
||||
/// <returns>A value that represents whether the button should light up or not.</returns>
|
||||
public virtual bool CanUse()
|
||||
{
|
||||
return !LimitedUses || UsesLeft > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method handles the button click event. It is a wrapper for the <see cref="OnClick"/> method.
|
||||
/// This method takes into account cooldowns, effects, and uses, before calling the <see cref="OnClick"/> method.
|
||||
/// It can be overridden for custom behavior.
|
||||
/// </summary>
|
||||
public virtual void ClickHandler()
|
||||
{
|
||||
if (!CanClick())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (LimitedUses)
|
||||
{
|
||||
UsesLeft--;
|
||||
Button?.SetUsesRemaining(UsesLeft);
|
||||
}
|
||||
|
||||
Timer = Cooldown;
|
||||
OnClick();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is called on the <see cref="MeetingHud.Update"/> method. It is a wrapper for the <see cref="FixedUpdate"/> method.
|
||||
/// By default, it handles the cooldown and effect timers, and sets the button to enabled or disabled.
|
||||
/// It can be overridden for custom behavior.
|
||||
/// </summary>
|
||||
/// <param name="hud">The <see cref="MeetingHud"/> instance.</param>
|
||||
public virtual void UpdateHandler(MeetingHud hud)
|
||||
{
|
||||
if (Timer >= 0 && !TimerPaused)
|
||||
{
|
||||
Timer -= Time.deltaTime;
|
||||
}
|
||||
|
||||
if (Button)
|
||||
{
|
||||
if (CanUse())
|
||||
{
|
||||
Button!.SetEnabled();
|
||||
}
|
||||
else
|
||||
{
|
||||
Button!.SetDisabled();
|
||||
}
|
||||
Button.SetCooldownFormat(Timer, Cooldown, CooldownTimerFormatString);
|
||||
Button.SetCoolDown(Timer, Cooldown);
|
||||
}
|
||||
|
||||
FixedUpdate(hud);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MiraAPI.Modifiers;
|
||||
using MiraAPI.Modifiers.Types;
|
||||
using MiraAPI.PluginLoading;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using MiraAPI.Utilities.Components;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.MeetingAbilities;
|
||||
|
||||
/// <summary>
|
||||
/// Class for managing all registered meeting abilities.
|
||||
/// </summary>
|
||||
public static class MeetingButtonManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a list if==of all registered <see cref="TargetedMeetingButton"/>.
|
||||
/// </summary>
|
||||
public static List<TargetedMeetingButton> TargetedButtons { get; internal set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list if==of all registered <see cref="MeetingActionButton"/>.
|
||||
/// </summary>
|
||||
public static List<MeetingActionButton> UntargetedButtons { get; internal set; } = new();
|
||||
|
||||
internal static bool RegisterTargetedMeetingButton(Type type, MiraPluginInfo info)
|
||||
{
|
||||
if (!typeof(TargetedMeetingButton).IsAssignableFrom(type))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var button = Activator.CreateInstance(type) as TargetedMeetingButton;
|
||||
info.InternalTargetedMeetingButtons.Add(button);
|
||||
TargetedButtons.Add(button);
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static bool RegisterMeetingButton(Type type, MiraPluginInfo info)
|
||||
{
|
||||
if (!typeof(MeetingActionButton).IsAssignableFrom(type))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var button = Activator.CreateInstance(type) as MeetingActionButton;
|
||||
info.InternalMeetingButtons.Add(button);
|
||||
UntargetedButtons.Add(button);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static CenteredGridArrange? actionButtonsParent = null!;
|
||||
|
||||
public static void OnGameStart()
|
||||
{
|
||||
foreach (var ability in TargetedButtons.Where(x => x.UsesMode == MeetingButtonUsesMode.PerGame))
|
||||
{
|
||||
ability.UsesLeft = ability.MaxUses;
|
||||
}
|
||||
foreach (var ability in UntargetedButtons.Where(x => x.UsesMode == MeetingButtonUsesMode.PerGame))
|
||||
{
|
||||
ability.UsesLeft = ability.MaxUses;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called on <see cref="MeetingHud.Start"/>. Handles button creation, and ability uses.
|
||||
/// </summary>
|
||||
/// <param name="meetingHud">The <see cref="MeetingHud"/> instance.</param>
|
||||
public static void OnMeetingStart(MeetingHud meetingHud)
|
||||
{
|
||||
// Untargeted buttons parent.
|
||||
actionButtonsParent = new GameObject("ActionsButtonParent").AddComponent<CenteredGridArrange>();
|
||||
actionButtonsParent.transform.SetParent(meetingHud.MeetingAbilityButton.transform.parent, meetingHud.MeetingAbilityButton.transform.parent);
|
||||
actionButtonsParent.CellSize = new Vector2(0.7f, 0.7f);
|
||||
actionButtonsParent.transform.localPosition = new Vector3(0, -2.1f, -10);
|
||||
meetingHud.MeetingAbilityButton.transform.SetParent(actionButtonsParent.transform);
|
||||
|
||||
// Untargeted buttons creation.
|
||||
foreach (var ability in UntargetedButtons)
|
||||
{
|
||||
try
|
||||
{
|
||||
ability.CreateButton(actionButtonsParent.transform);
|
||||
if (ability.UsesMode == MeetingButtonUsesMode.PerMeeting) ability.UsesLeft = ability.MaxUses;
|
||||
ability.Timer = ability.InitialCooldown;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Error($"Failed to create meeting button {ability.Name}: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// Targeted buttons creation.
|
||||
foreach (var ability in TargetedButtons)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var playerVoteArea in meetingHud.playerStates)
|
||||
{
|
||||
var btn = ability.CreateButton(playerVoteArea);
|
||||
btn.transform.SetParent(playerVoteArea.Buttons.transform);
|
||||
playerVoteArea.CancelButton.transform.SetAsFirstSibling();
|
||||
}
|
||||
|
||||
if (ability.UsesMode == MeetingButtonUsesMode.PerMeeting) ability.UsesLeft = ability.MaxUses;
|
||||
if (ability is MultiTargetMeetingButton multiAbility) multiAbility.Targets = new();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Error($"Failed to create targeted meeting button {ability.Name}: {e.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ public abstract class MultiTargetMeetingButton : TargetedMeetingButton
|
||||
/// <inheritdoc />
|
||||
public override void ClickHandler(MeetingAbilityBehaviour button, PlayerVoteArea playerVoteArea)
|
||||
{
|
||||
if (UsesRemaining <= 0 && LimitedUses) return;
|
||||
if (UsesLeft <= 0 && LimitedUses) return;
|
||||
if (Timer > 0) return;
|
||||
var toggle = !Targets.Remove(playerVoteArea);
|
||||
if (toggle) Targets.Add(playerVoteArea, button);
|
||||
@@ -55,7 +55,7 @@ public abstract class MultiTargetMeetingButton : TargetedMeetingButton
|
||||
button.Renderer.sprite = Sprite.LoadAsset();
|
||||
}
|
||||
Timer = Cooldown;
|
||||
UsesRemaining -= 1;
|
||||
UsesLeft -= 1;
|
||||
OnFinish();
|
||||
Targets = new();
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ public abstract class TargetedMeetingButton
|
||||
/// <summary>
|
||||
/// Gets or sets the amount of uses left for this button.
|
||||
/// </summary>
|
||||
public int UsesRemaining { get; set; }
|
||||
public int UsesLeft { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the <see cref="TargetedMeetingButton"/> has limited uses.
|
||||
@@ -65,7 +65,7 @@ public abstract class TargetedMeetingButton
|
||||
/// <summary>
|
||||
/// Gets the <see cref="MeetingButtonUsesMode"/> for this button.
|
||||
/// </summary>
|
||||
public virtual MeetingButtonUsesMode ButtonUsesMode { get; } = MeetingButtonUsesMode.PerGame;
|
||||
public virtual MeetingButtonUsesMode UsesMode { get; } = MeetingButtonUsesMode.PerGame;
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the button is enabled.
|
||||
@@ -135,10 +135,10 @@ public abstract class TargetedMeetingButton
|
||||
/// <param name="playerVoteArea">The target <see cref="PlayerVoteArea"/>.</param>
|
||||
public virtual void ClickHandler(MeetingAbilityBehaviour button, PlayerVoteArea playerVoteArea)
|
||||
{
|
||||
if (UsesRemaining <= 0 && LimitedUses) return;
|
||||
if (UsesLeft <= 0 && LimitedUses) return;
|
||||
if (Timer > 0) return;
|
||||
Timer = Cooldown;
|
||||
UsesRemaining--;
|
||||
UsesLeft--;
|
||||
playerVoteArea.Cancel();
|
||||
OnClick(playerVoteArea);
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MiraAPI.Modifiers;
|
||||
using MiraAPI.Modifiers.Types;
|
||||
using MiraAPI.PluginLoading;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.MeetingAbilities;
|
||||
|
||||
/// <summary>
|
||||
/// Class for managing all registered meeting abilities.
|
||||
/// </summary>
|
||||
public static class TargetedMeetingButtonManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a list if==of all registered <see cref="TargetedMeetingButton"/>.
|
||||
/// </summary>
|
||||
public static List<TargetedMeetingButton> Buttons { get; internal set; } = new();
|
||||
|
||||
internal static bool RegisterMeetingAbility(Type type, MiraPluginInfo info)
|
||||
{
|
||||
if (!typeof(TargetedMeetingButton).IsAssignableFrom(type))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var button = Activator.CreateInstance(type) as TargetedMeetingButton;
|
||||
info.InternalMeetingAbilities.Add(button);
|
||||
Buttons.Add(button);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -15,5 +15,5 @@ public static class TargetedMeetingButtonSingleton<T> where T : TargetedMeetingB
|
||||
/// Gets the instance of the <typeparamref name="T"/> 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 targeted meeting button.")]
|
||||
public static T Instance => _instance ??= TargetedMeetingButtonManager.Buttons.OfType<T>().Single();
|
||||
public static T Instance => _instance ??= MeetingButtonManager.TargetedButtons.OfType<T>().Single();
|
||||
}
|
||||
@@ -20,10 +20,7 @@ public static class FreeplayRoundStartPatch
|
||||
{
|
||||
if (!__result)
|
||||
{
|
||||
foreach (var ability in TargetedMeetingButtonManager.Buttons.Where(x => x.ButtonUsesMode == MeetingButtonUsesMode.PerGame))
|
||||
{
|
||||
ability.UsesRemaining = ability.MaxUses;
|
||||
}
|
||||
MeetingButtonManager.OnGameStart();
|
||||
MiraEventManager.InvokeEvent(new RoundStartEvent(true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,16 +125,12 @@ public static class IntroCutscenePatches
|
||||
|
||||
Info("IntroCutscene ended");
|
||||
|
||||
MeetingButtonManager.OnGameStart();
|
||||
MiraEventManager.InvokeEvent(new IntroEndEvent(introCutscene));
|
||||
|
||||
var @event = new BeforeRoundStartEvent(true);
|
||||
MiraEventManager.InvokeEvent(@event);
|
||||
|
||||
foreach (var ability in TargetedMeetingButtonManager.Buttons.Where(x => x.ButtonUsesMode == MeetingButtonUsesMode.PerGame))
|
||||
{
|
||||
ability.UsesRemaining = ability.MaxUses;
|
||||
}
|
||||
|
||||
if (@event.IsCancelled) return;
|
||||
MiraEventManager.InvokeEvent(new RoundStartEvent(true));
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ using MiraAPI.Events.Vanilla.Meeting.Voting;
|
||||
using MiraAPI.MeetingAbilities;
|
||||
using MiraAPI.Modifiers;
|
||||
using MiraAPI.Utilities;
|
||||
using MiraAPI.Utilities.Components;
|
||||
using MiraAPI.Voting;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -37,19 +38,6 @@ internal static class MeetingHudPatches
|
||||
return true;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(PlayerVoteArea), nameof(PlayerVoteArea.Start))]
|
||||
public static void VoteAreaStartPostfix(PlayerVoteArea __instance)
|
||||
{
|
||||
foreach (var targetedMeetingAbility in TargetedMeetingButtonManager.Buttons)
|
||||
{
|
||||
var btn = targetedMeetingAbility.CreateButton(__instance);
|
||||
btn.transform.SetParent(__instance.Buttons.transform);
|
||||
btn.gameObject.SetActive(targetedMeetingAbility.Enabled(PlayerControl.LocalPlayer.Data.Role) && targetedMeetingAbility.IsTargetValid(__instance));
|
||||
}
|
||||
__instance.CancelButton.transform.SetAsFirstSibling();
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(PlayerVoteArea), nameof(PlayerVoteArea.Select))]
|
||||
public static bool VoteAreaSelectPatch(PlayerVoteArea __instance)
|
||||
@@ -89,6 +77,7 @@ internal static class MeetingHudPatches
|
||||
[HarmonyPatch(nameof(MeetingHud.Start))]
|
||||
public static void MeetingHudStartPatch(MeetingHud __instance)
|
||||
{
|
||||
MeetingButtonManager.OnMeetingStart(__instance);
|
||||
foreach (var plr in PlayerControl.AllPlayerControls)
|
||||
{
|
||||
var voteData = plr.GetVoteData();
|
||||
@@ -106,11 +95,6 @@ internal static class MeetingHudPatches
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var ability in TargetedMeetingButtonManager.Buttons)
|
||||
{
|
||||
if (ability.ButtonUsesMode == MeetingButtonUsesMode.PerMeeting) ability.UsesRemaining = ability.MaxUses;
|
||||
if (ability is MultiTargetMeetingButton multiAbility) multiAbility.Targets = new();
|
||||
}
|
||||
var @event = new StartMeetingEvent(__instance);
|
||||
MiraEventManager.InvokeEvent(@event);
|
||||
}
|
||||
@@ -134,11 +118,16 @@ internal static class MeetingHudPatches
|
||||
[HarmonyPatch(nameof(MeetingHud.Update))]
|
||||
public static void ForceSkipPatch(MeetingHud __instance)
|
||||
{
|
||||
foreach (var targetedMeetingAbility in TargetedMeetingButtonManager.Buttons.Where(x => x.Enabled(PlayerControl.LocalPlayer.Data.Role)))
|
||||
foreach (var targetedMeetingAbility in MeetingButtonManager.TargetedButtons.Where(x => x.Enabled(PlayerControl.LocalPlayer.Data.Role)))
|
||||
{
|
||||
targetedMeetingAbility.UpdateHandler();
|
||||
}
|
||||
|
||||
foreach (var meetingAbility in MeetingButtonManager.UntargetedButtons.Where(x => x.Enabled(PlayerControl.LocalPlayer.Data.Role)))
|
||||
{
|
||||
meetingAbility.UpdateHandler(__instance);
|
||||
}
|
||||
|
||||
if (__instance.state is not (MeetingHud.MeetingStates.NotVoted or MeetingHud.MeetingStates.Voted))
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -50,6 +50,16 @@ public class MiraPluginInfo
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<CustomActionButton> Buttons { get; private set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="IReadOnlyCollection{T}"/> of this <see cref="IMiraPlugin"/>'s <see cref="TargetedMeetingButton"/>.
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<TargetedMeetingButton> TargetedMeetingButtons { get; private set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="IReadOnlyCollection{T}"/> of this <see cref="IMiraPlugin"/>'s <see cref="MeetingActionButton"/>.
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<MeetingActionButton> MeetingButtons { get; private set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="ReadOnlyDictionary{TKey, TValue}"/> of this <see cref="IMiraPlugin"/>'s <see cref="OptionPreset"/>s.
|
||||
/// </summary>
|
||||
@@ -63,6 +73,8 @@ public class MiraPluginInfo
|
||||
Options = [..InternalOptions];
|
||||
Roles = new ReadOnlyDictionary<ushort, RoleBehaviour>(InternalRoles);
|
||||
Buttons = [..InternalButtons];
|
||||
MeetingButtons = [..InternalMeetingButtons];
|
||||
TargetedMeetingButtons = [..InternalTargetedMeetingButtons];
|
||||
}
|
||||
|
||||
internal List<OptionPreset> InternalPresets { get; } = [];
|
||||
@@ -79,7 +91,9 @@ public class MiraPluginInfo
|
||||
|
||||
internal List<CustomActionButton> InternalButtons { get; } = [];
|
||||
|
||||
internal List<TargetedMeetingButton?> InternalMeetingAbilities { get; } = [];
|
||||
internal List<TargetedMeetingButton> InternalTargetedMeetingButtons { get; } = [];
|
||||
|
||||
internal List<MeetingActionButton> InternalMeetingButtons { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin's ID, as defined in the plugin's BepInEx metadata.
|
||||
|
||||
@@ -118,12 +118,17 @@ public sealed class MiraPluginManager
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (RegisterMeetingAbility(type, info))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (RegisterTargetedMeetingAbility(type, info))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
RegisterColorClasses(type);
|
||||
RegisterKeybinds(type, plugin);
|
||||
}
|
||||
@@ -454,7 +459,7 @@ public sealed class MiraPluginManager
|
||||
{
|
||||
try
|
||||
{
|
||||
return TargetedMeetingButtonManager.RegisterMeetingAbility(type, info);
|
||||
return MeetingButtonManager.RegisterMeetingButton(type, info);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -462,4 +467,16 @@ public sealed class MiraPluginManager
|
||||
return false;
|
||||
}
|
||||
}
|
||||
private static bool RegisterTargetedMeetingAbility(Type type, MiraPluginInfo info)
|
||||
{
|
||||
try
|
||||
{
|
||||
return MeetingButtonManager.RegisterTargetedMeetingButton(type, info);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Error($"Failed to register targeted meeting ability {type.Name}: {e}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Reactor.Utilities.Attributes;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Utilities.Components;
|
||||
|
||||
[RegisterInIl2Cpp]
|
||||
public class CenteredGridArrange(IntPtr ptr) : MonoBehaviour(ptr)
|
||||
{
|
||||
public Vector2 CellSize;
|
||||
public int MaxColumns = 3;
|
||||
|
||||
private List<Transform> cells;
|
||||
private static List<Transform> currentChildren = new List<Transform>();
|
||||
|
||||
private void Start()
|
||||
{
|
||||
cells = new List<Transform>();
|
||||
GetChildsActive();
|
||||
CheckCurrentChildren();
|
||||
}
|
||||
|
||||
private void FixedUpdate() => CheckCurrentChildren();
|
||||
|
||||
private void CheckCurrentChildren()
|
||||
{
|
||||
GetChildsActive();
|
||||
if (cells.SequenceEqual(currentChildren))
|
||||
return;
|
||||
cells.Clear();
|
||||
foreach (Transform currentChild in currentChildren)
|
||||
cells.Add(currentChild);
|
||||
ArrangeChilds();
|
||||
}
|
||||
|
||||
private void GetChildsActive()
|
||||
{
|
||||
currentChildren.Clear();
|
||||
foreach (var obj in transform)
|
||||
{
|
||||
var child = obj.TryCast<Transform>();
|
||||
if (child == null) continue;
|
||||
if (child.gameObject.activeSelf)
|
||||
currentChildren.Add(child);
|
||||
}
|
||||
}
|
||||
|
||||
private void ArrangeChilds()
|
||||
{
|
||||
if (cells.Count == 0)
|
||||
return;
|
||||
|
||||
int totalRows = Mathf.CeilToInt((float)cells.Count / MaxColumns);
|
||||
float totalHeight = (totalRows - 1) * CellSize.y;
|
||||
float startY = transform.position.y + totalHeight * 0.5f;
|
||||
|
||||
for (int index = 0; index < cells.Count; ++index)
|
||||
{
|
||||
int row = index / MaxColumns;
|
||||
int rowStartIndex = row * MaxColumns;
|
||||
int itemsInRow = Mathf.Min(MaxColumns, cells.Count - rowStartIndex);
|
||||
int col = index - rowStartIndex;
|
||||
|
||||
float rowWidth = (itemsInRow - 1) * CellSize.x;
|
||||
float startX = transform.position.x - rowWidth * 0.5f;
|
||||
|
||||
float x = startX + col * CellSize.x;
|
||||
float y = startY - row * CellSize.y;
|
||||
|
||||
Transform cell = cells[index];
|
||||
cell.position = new Vector3(x, y, cell.position.z);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user