Compare commits

...
27 Commits
Author SHA1 Message Date
XtraCube 19c7e38690 what the freak 2024-08-27 21:56:33 -04:00
XtraCube f1639a784d Merge branch 'refs/heads/dev' 2024-08-27 21:52:41 -04:00
XtraCube 5f1996b457 precautions 2024-08-27 21:49:53 -04:00
XtraCube 33d389e107 fix modifiers logic 2024-08-27 21:46:45 -04:00
XtraCube 176d472cd7 nullable 2024-08-27 21:46:36 -04:00
XtraCube 7a3406e0ae XD 2024-08-27 21:46:26 -04:00
XtraCube 9f92af8958 only show role setting button if there is settings for it 2024-08-27 21:46:21 -04:00
Ang 8d167596d2 Update README.md 2024-08-27 21:29:29 -04:00
XtraCube a98a37bea7 fix modifier manager logic? 2024-08-27 21:14:53 -04:00
ang-xd 4963c80395 Fixes and new stuff 2024-08-27 20:56:53 -04:00
XtraCube 4bef2f838d can use fix 2024-08-27 20:46:15 -04:00
XtraCube 763017b6e2 make GetTarget abstract instead of virtual 2024-08-27 20:44:37 -04:00
XtraCube 16fd8e02aa fix bug with advanced roles settings 2024-08-27 20:44:25 -04:00
XtraCube f187a15f2e more documentaion stuff 2024-08-27 20:23:53 -04:00
XtraCube e211c4c49c rename ResetCooldown to ResetCooldownAndOrEffect, add warning in doc 2024-08-27 20:21:06 -04:00
XtraCube 7f532cd82f Merge remote-tracking branch 'origin/master' 2024-08-27 20:11:43 -04:00
XtraCube 7a0396fe5c Make lobby view pane sync on options change 2024-08-27 20:11:36 -04:00
ang-xd d4955b5183 Merge branch 'master' into dev 2024-08-27 20:10:37 -04:00
ang-xd 0a5c59d9cd somewhat fix notifications on game start 2024-08-27 20:10:01 -04:00
ang-xd 774e1a7a2b Modifiers bug fixed 2024-08-27 19:54:33 -04:00
ang-xd 8e6f75cfbd WIP 2024-08-27 19:43:38 -04:00
XtraCube b36b434a99 oversight in the code 2024-08-27 19:39:36 -04:00
XtraCube 858e518a57 for sake of versioning ig 2024-08-27 19:34:31 -04:00
XtraCube b9734f5a1c forgot to bump version what an idiot 2024-08-27 19:31:42 -04:00
XtraCube 1cb11be9b0 Merge pull request #7 from All-Of-Us-Mods/dev
Button fix, MiraNumberSuffixes, checkmark fix,
2024-08-27 19:24:36 -04:00
XtraCube 6bc6ab7143 fix button issue 2024-08-27 19:22:48 -04:00
ang-xd 05fdf5313d MiraNumberSuffixes, and checkmark fix 2024-08-27 19:04:32 -04:00
36 changed files with 465 additions and 204 deletions

No files matched your search

-29
View File
@@ -1,29 +0,0 @@
using MiraAPI.Example.Modifiers;
using MiraAPI.Hud;
using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets;
using Reactor.Utilities;
using UnityEngine;
namespace MiraAPI.Example.Buttons;
[RegisterButton]
public class ExampleButton : CustomActionButton
{
public override string Name => "Example Button";
public override float Cooldown => 5f;
public override float EffectDuration => 0f;
public override int MaxUses => 5;
public override LoadableAsset<Sprite> Sprite => ExampleAssets.ExampleButton;
public override ButtonLocation Location => ButtonLocation.BottomRight;
protected override void OnClick()
{
Logger<ExamplePlugin>.Info("Example button clicked!");
}
public override bool Enabled(RoleBehaviour role)
{
return PlayerControl.LocalPlayer.HasModifier<GameModifierExample>();
}
}
@@ -1,20 +1,24 @@
using MiraAPI.Hud;
using MiraAPI.Example.Modifiers.Freezer;
using MiraAPI.Example.Roles;
using MiraAPI.Hud;
using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets;
using UnityEngine;
namespace MiraAPI.Example.Buttons;
namespace MiraAPI.Example.Buttons.Freezer;
public class ExampleTargetButton : CustomActionButton<PlayerControl>
[RegisterButton]
public class FreezeButton : CustomActionButton<PlayerControl>
{
public override string Name => "Shoot";
public override float Cooldown => 0f;
public override string Name => "Freeze";
public override float Cooldown => 5f;
public override float EffectDuration => 0f;
public override int MaxUses => 0;
public override LoadableAsset<Sprite> Sprite => ExampleAssets.ExampleButton;
protected override void OnClick()
{
PlayerControl.LocalPlayer.CmdCheckMurder(Target);
Target.AddModifier<FreezeModifier>();
}
public override PlayerControl GetTarget()
@@ -24,17 +28,17 @@ public class ExampleTargetButton : CustomActionButton<PlayerControl>
public override void SetOutline(bool active)
{
Target?.cosmetics.SetOutline(active, new Il2CppSystem.Nullable<Color>(Color.red));
Target?.cosmetics.SetOutline(active, new Il2CppSystem.Nullable<Color>(Palette.Blue));
}
public override bool IsTargetValid(PlayerControl target)
{
return PlayerControl.LocalPlayer.Data.Role.IsValidTarget(target.Data);
return true;
}
public override bool Enabled(RoleBehaviour role)
{
return role.IsImpostor;
return role is FreezerRole;
}
}
+47
View File
@@ -0,0 +1,47 @@
using MiraAPI.Example;
using MiraAPI.Example.Modifiers;
using MiraAPI.Hud;
using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets;
using UnityEngine;
[RegisterButton]
public class MeetingButton : CustomActionButton
{
public override string Name => "Call Meeting";
public override float Cooldown => 15;
public override float EffectDuration => 0;
public override int MaxUses => 3;
public override LoadableAsset<Sprite> Sprite => ExampleAssets.ExampleButton;
public override bool Enabled(RoleBehaviour role)
{
return PlayerControl.LocalPlayer.HasModifier<CaptainModifier>();
}
protected override void OnClick()
{
var bt = ShipStatus.Instance.EmergencyButton;
PlayerControl.LocalPlayer.NetTransform.Halt();
var minigame = Object.Instantiate(bt.MinigamePrefab, Camera.main!.transform, false);
var taskAdderGame = minigame as TaskAdderGame;
if (taskAdderGame != null)
{
taskAdderGame.SafePositionWorld = bt.SafePositionLocal + (Vector2)bt.transform.position;
}
minigame.transform.localPosition = new Vector3(0f, 0f, -50f);
minigame.Begin(null);
if (UsesLeft == 0 && PlayerControl.LocalPlayer.HasModifier<CaptainModifier>())
{
PlayerControl.LocalPlayer.RemoveModifier<CaptainModifier>();
}
}
}
@@ -0,0 +1,86 @@
using MiraAPI.Example.Options.Roles;
using MiraAPI.Example.Roles;
using MiraAPI.GameOptions;
using MiraAPI.Hud;
using MiraAPI.Utilities.Assets;
using Reactor.Utilities;
using System.Collections;
using UnityEngine;
namespace MiraAPI.Example.Buttons.Teleporter;
[RegisterButton]
public class TeleportButton : CustomActionButton
{
public override string Name => "Teleport";
public override float Cooldown => OptionGroupSingleton<TeleporterOptions>.Instance.TeleportCooldown;
public override float EffectDuration => OptionGroupSingleton<TeleporterOptions>.Instance.TeleportDuration;
public override int MaxUses => 0;
public override LoadableAsset<Sprite> Sprite => ExampleAssets.ExampleButton;
public static bool IsZoom { get; private set; }
public override bool Enabled(RoleBehaviour role)
{
return role is TeleporterRole;
}
protected override void OnClick()
{
Coroutines.Start(ZoomOutCoroutine());
}
public override void OnEffectEnd()
{
Coroutines.Start(ZoomInCoroutine());
}
protected override void FixedUpdate(PlayerControl playerControl)
{
base.FixedUpdate(playerControl);
if (!EffectActive) return;
if (Input.GetKey(KeyCode.Mouse0))
{
playerControl.NetTransform.RpcSnapTo(Camera.main.ScreenToWorldPoint(Input.mousePosition));
ResetCooldownAndOrEffect();
}
}
private static IEnumerator ZoomOutCoroutine()
{
HudManager.Instance.ShadowQuad.gameObject.SetActive(false);
IsZoom = true;
var zoomDistance = OptionGroupSingleton<TeleporterOptions>.Instance.ZoomDistance;
for (var ft = Camera.main!.orthographicSize; ft < zoomDistance; ft += 0.3f)
{
Camera.main.orthographicSize = MeetingHud.Instance ? 3f : ft;
ResolutionManager.ResolutionChanged.Invoke((float)Screen.width / Screen.height, Screen.width, Screen.height, Screen.fullScreen);
foreach (var cam in Camera.allCameras) cam.orthographicSize = Camera.main.orthographicSize;
yield return null;
}
foreach (var cam in Camera.allCameras) cam.orthographicSize = zoomDistance;
ResolutionManager.ResolutionChanged.Invoke((float)Screen.width / Screen.height, Screen.width, Screen.height, Screen.fullScreen);
}
private static IEnumerator ZoomInCoroutine()
{
for (var ft = Camera.main!.orthographicSize; ft > 3f; ft -= 0.3f)
{
Camera.main.orthographicSize = MeetingHud.Instance ? 3f : ft;
ResolutionManager.ResolutionChanged.Invoke((float)Screen.width / Screen.height, Screen.width, Screen.height, Screen.fullScreen);
foreach (var cam in Camera.allCameras) cam.orthographicSize = Camera.main.orthographicSize;
yield return null;
}
foreach (var cam in Camera.allCameras) cam.orthographicSize = 3f;
HudManager.Instance.ShadowQuad.gameObject.SetActive(true);
IsZoom = false;
ResolutionManager.ResolutionChanged.Invoke((float)Screen.width / Screen.height, Screen.width, Screen.height, Screen.fullScreen);
}
}
+4 -4
View File
@@ -1,8 +1,8 @@
using MiraAPI.Utilities;
using MiraAPI.Example.Modifiers;
using MiraAPI.Utilities;
using Reactor.Utilities.Attributes;
using Reactor.Utilities.ImGui;
using System;
using MiraAPI.Example.Modifiers;
using UnityEngine;
namespace MiraAPI.Example;
@@ -13,11 +13,11 @@ public class MiraDebugWindow(IntPtr ptr) : MonoBehaviour(ptr)
{
if (GUILayout.Button("Test modifier"))
{
PlayerControl.LocalPlayer.AddModifier<ModifierTimerExample>();
//PlayerControl.LocalPlayer.AddModifier<ModifierTimerExample>();
}
if (GUILayout.Button("Remove modifier"))
{
PlayerControl.LocalPlayer.RemoveModifier<ModifierTimerExample>();
//PlayerControl.LocalPlayer.RemoveModifier<ModifierTimerExample>();
}
})
{
@@ -4,15 +4,9 @@ using MiraAPI.Modifiers.Types;
namespace MiraAPI.Example.Modifiers;
[RegisterModifier]
public class GameModifierExample : GameModifier
public class CaptainModifier : GameModifier
{
public override string ModifierName => "Example Game Modifier";
public override bool CanVent()
{
return true;
}
public override string ModifierName => "Captain";
public override int GetAmountPerGame()
{
return 1;
@@ -0,0 +1,30 @@
using MiraAPI.Modifiers;
using MiraAPI.Modifiers.Types;
namespace MiraAPI.Example.Modifiers.Freezer;
[RegisterModifier]
public class FreezeModifier : TimedModifier
{
public override string ModifierName => "Freezed";
public override bool HideOnUi => false;
public override float Duration => 15f;
public override bool AutoStart => true;
public override bool RemoveOnComplete => true;
public override void OnActivate()
{
if (Player.AmOwner)
{
Player.moveable = false;
}
}
public override void OnTimerComplete()
{
if (Player.AmOwner)
{
Player.moveable = true;
}
}
}
@@ -1,21 +0,0 @@
using MiraAPI.Modifiers;
using MiraAPI.Modifiers.Types;
namespace MiraAPI.Example.Modifiers;
[RegisterModifier]
public class ModifierTimerExample : TimedModifier
{
public override string ModifierName => "Color Changer";
public override bool HideOnUi => true;
public override float Duration => 10f;
public override bool AutoStart => true;
private readonly System.Random _rand = new();
public override void OnTimerComplete()
{
Player.SetColor((byte)_rand.Next(0, Palette.PlayerColors.Count));
StartTimer();
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
using MiraAPI.GameOptions;
using MiraAPI.GameOptions.Attributes;
using MiraAPI.Utilities;
using UnityEngine;
namespace MiraAPI.Example.Options;
@@ -15,7 +16,7 @@ public class ExampleOptions : AbstractOptionGroup
[ModdedToggleOption("Toggle Opt 2")]
public bool ToggleOpt2 { get; set; } = true;
[ModdedNumberOption("Number Opt", min: 0, max: 10)]
[ModdedNumberOption("Number Opt", min: 0, max: 10, suffixType: MiraNumberSuffixes.Percent)]
public float NumberOpt { get; set; } = 4f;
[ModdedStringOption("String Opt", ["Hello", "Hey", "Bye"])]
@@ -9,7 +9,7 @@ public class CustomRole2Settings : AbstractOptionGroup
{
public override string GroupName => "Custom Role";
public override Type AdvancedRole => typeof(CustomRole2);
public override Type AdvancedRole => typeof(FreezerRole);
[ModdedToggleOption("Teleportation ability")] public bool Teleport { get; set; } = true;
@@ -1,16 +0,0 @@
using MiraAPI.Example.Roles;
using MiraAPI.GameOptions;
using MiraAPI.GameOptions.Attributes;
using System;
namespace MiraAPI.Example.Options.Roles;
public class CustomRoleSettings : AbstractOptionGroup
{
public override string GroupName => "Custom Role";
public override Type AdvancedRole => typeof(CustomRole);
[ModdedNumberOption("Number Opt", min: 3, max: 9)] public float NumberOpt { get; set; } = 4;
}
@@ -0,0 +1,23 @@
using MiraAPI.Example.Roles;
using MiraAPI.GameOptions;
using MiraAPI.GameOptions.Attributes;
using MiraAPI.Utilities;
using System;
namespace MiraAPI.Example.Options.Roles;
public class TeleporterOptions : AbstractOptionGroup
{
public override string GroupName => "Teleporter";
public override Type AdvancedRole => typeof(TeleporterRole);
[ModdedNumberOption("Teleport Cooldown", 5, 60, 2.5f, MiraNumberSuffixes.Seconds)]
public float TeleportCooldown { get; set; } = 5;
[ModdedNumberOption("Teleport Duration", 5, 25, 1, MiraNumberSuffixes.Seconds)]
public float TeleportDuration { get; set; } = 10;
[ModdedNumberOption("Zoom Distance", 4, 15)]
public float ZoomDistance { get; set; } = 6;
}
@@ -5,12 +5,12 @@ using UnityEngine;
namespace MiraAPI.Example.Roles;
[RegisterCustomRole]
public class CustomRole : ImpostorRole, ICustomRole
public class FreezerRole : ImpostorRole, ICustomRole
{
public string RoleName => "Custom Role";
public string RoleLongDescription => "Eliminate everyone with your special ability!";
public string RoleName => "Freezer";
public string RoleLongDescription => "Freeze another player for a duration of time.";
public string RoleDescription => RoleLongDescription;
public Color RoleColor => Palette.Black;
public Color RoleColor => Palette.Blue;
public ModdedRoleTeams Team => ModdedRoleTeams.Impostor;
public LoadableAsset<Sprite> OptionsScreenshot => ExampleAssets.Banner;
@@ -5,12 +5,12 @@ using UnityEngine;
namespace MiraAPI.Example.Roles;
[RegisterCustomRole]
public class CustomRole2 : CrewmateRole, ICustomRole
public class TeleporterRole : CrewmateRole, ICustomRole
{
public string RoleName => "Custom Role 2";
public string RoleLongDescription => "Save everyone from dying!";
public string RoleName => "Teleporter";
public string RoleLongDescription => "Zoom out and teleport across the map!";
public string RoleDescription => RoleLongDescription;
public Color RoleColor => Palette.AcceptedGreen;
public Color RoleColor => new Color32(221, 176, 152, 255);
public ModdedRoleTeams Team => ModdedRoleTeams.Crewmate;
public LoadableAsset<Sprite> OptionsScreenshot => ExampleAssets.Banner;
@@ -1,4 +1,5 @@
using MiraAPI.GameOptions.OptionTypes;
using MiraAPI.Utilities;
using System;
using System.Reflection;
@@ -9,7 +10,7 @@ public class ModdedNumberOptionAttribute(
float min,
float max,
float increment = 1,
NumberSuffixes suffixType = NumberSuffixes.None,
MiraNumberSuffixes suffixType = MiraNumberSuffixes.None,
bool zeroInfinity = false,
Type roleType = null)
: ModdedOptionAttribute(title, roleType)
@@ -129,6 +129,11 @@ public static class ModdedOptionsManager
option.HandleNetData(netData.Data);
}
if (LobbyInfoPane.Instance)
{
LobbyInfoPane.Instance.RefreshPane();
}
}
@@ -1,7 +1,7 @@
using System;
using AmongUs.GameOptions;
using AmongUs.GameOptions;
using MiraAPI.Networking;
using MiraAPI.Utilities;
using System;
using UnityEngine;
using Object = UnityEngine.Object;
@@ -9,17 +9,17 @@ namespace MiraAPI.GameOptions.OptionTypes;
public class ModdedNumberOption : ModdedOption<float>
{
public float Min { get;}
public float Min { get; }
public float Max { get; }
public float Increment { get; }
public NumberSuffixes SuffixType { get; }
public MiraNumberSuffixes SuffixType { get; }
public bool ZeroInfinity { get; }
public ModdedNumberOption(string title, float defaultValue, float min, float max, float increment, NumberSuffixes suffixType, bool zeroInfinity=false, Type roleType=null) : base(title, defaultValue, roleType)
public ModdedNumberOption(string title, float defaultValue, float min, float max, float increment, MiraNumberSuffixes suffixType, bool zeroInfinity = false, Type roleType = null) : base(title, defaultValue, roleType)
{
Min = min;
Max = max;
@@ -28,18 +28,18 @@ public class ModdedNumberOption : ModdedOption<float>
ZeroInfinity = zeroInfinity;
Value = Mathf.Clamp(defaultValue, min, max);
Data = ScriptableObject.CreateInstance<FloatGameSetting>();
var data = (FloatGameSetting)Data;
data.Type = global::OptionTypes.Float;
data.Title = StringName;
data.Value = Value;
data.Increment = Increment;
data.ValidRange = new FloatRange(Min, Max);
data.FormatString = Increment % 1 == 0 && Value % 1 == 0 && Min % 1 == 0 && Max % 1 == 0 ? "0" : "0.0";
data.FormatString = Increment % 1 == 0 && Value % 1 == 0 && Min % 1 == 0 && Max % 1 == 0 ? "0" : "0.0";
data.ZeroIsInfinity = ZeroInfinity;
data.SuffixType = SuffixType;
data.SuffixType = (NumberSuffixes)SuffixType;
data.OptionName = FloatOptionNames.Invalid;
}
@@ -49,17 +49,17 @@ public class ModdedNumberOption : ModdedOption<float>
numberOption.SetUpFromData(Data, 20);
numberOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged;
numberOption.Title = StringName;
numberOption.Value = Value;
numberOption.Increment = Increment;
numberOption.ValidRange = new FloatRange(Min, Max);
numberOption.FormatString = ((FloatGameSetting)Data).FormatString;
numberOption.ZeroIsInfinity = ZeroInfinity;
numberOption.SuffixType = SuffixType;
numberOption.SuffixType = (NumberSuffixes)SuffixType;
numberOption.floatOptionName = FloatOptionNames.Invalid;
OptionBehaviour = numberOption;
return numberOption;
+21 -22
View File
@@ -3,6 +3,7 @@ using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
namespace MiraAPI.Hud;
@@ -70,7 +71,7 @@ public abstract class CustomActionButton
/// The button object in game. This is created by Mira API automatically.
/// </summary>
protected ActionButton? Button { get; private set; }
internal void CreateButton(Transform parent)
{
if (Button)
@@ -95,24 +96,24 @@ public abstract class CustomActionButton
}
var pb = Button.GetComponent<PassiveButton>();
pb.OnClick.RemoveAllListeners();
pb.OnClick = new Button.ButtonClickedEvent();
pb.OnClick.AddListener((UnityAction)ClickHandler);
}
/// <summary>
/// A utility function to reset the cooldown of the button.
/// A utility function to reset the cooldown and/or effect of the button.
/// </summary>
public void ResetCooldown()
public void ResetCooldownAndOrEffect()
{
Timer = Cooldown;
if (EffectActive)
{
OnEffectEnd();
}
EffectActive = false;
}
/// <summary>
/// A utility function to override the sprite of the button.
/// </summary>
@@ -135,13 +136,13 @@ public abstract class CustomActionButton
}
/// <summary>
/// A utility function that runs with the local PlayerControl's FixedUpdate.
/// A utility function that runs with the local PlayerControl's FixedUpdate if the button is enabled.
/// </summary>
/// <param name="playerControl">the local PlayerControl</param>
protected virtual void FixedUpdate(PlayerControl playerControl) { }
/// <summary>
/// Runs when the button is clicked.
/// Callback method for the button click event.
/// </summary>
protected abstract void OnClick();
@@ -153,7 +154,8 @@ public abstract class CustomActionButton
public abstract bool Enabled(RoleBehaviour? role);
/// <summary>
/// Given that there is an effect, this method that runs when the effect ends.
/// Given that there is an effect, this method runs when the effect ends.
/// <br /> <br /> THIS IS A CALLBACK METHOD! Use <see cref="ResetCooldownAndOrEffect" /> if you want to end the effect.
/// </summary>
public virtual void OnEffectEnd() { }
@@ -238,7 +240,7 @@ public abstract class CustomActionButton
{
Button?.SetDisabled();
}
Button?.SetCoolDown(Timer, EffectActive ? EffectDuration : Cooldown);
FixedUpdate(playerControl);
@@ -255,12 +257,12 @@ public abstract class CustomActionButton<T> : CustomActionButton where T : MonoB
/// The target object of the button.
/// </summary>
public T? Target { get; private set; }
/// <summary>
/// The distance the player must be from the target object to use the button.
/// </summary>
public virtual float Distance => PlayerControl.LocalPlayer.Data.Role.GetAbilityDistance();
/// <summary>
/// An optional collider tag to filter the target object by.
/// </summary>
@@ -269,26 +271,23 @@ public abstract class CustomActionButton<T> : CustomActionButton where T : MonoB
/// <summary>
/// Determines if the target object is valid.
/// </summary>
public virtual bool IsTargetValid(T target)
public virtual bool IsTargetValid(T? target)
{
return target;
}
/// <summary>
/// The method used to get the target object.
/// </summary>
public virtual T? GetTarget()
{
return PlayerControl.LocalPlayer.GetNearestObjectOfType<T>(Distance, ColliderTag, IsTargetValid);
}
public abstract T? GetTarget();
/// <summary>
/// Sets the outline of the target object.
/// </summary>
/// <param name="active">Should the outline be active</param>
public abstract void SetOutline(bool active);
public override bool CanUse()
{
var newTarget = GetTarget();
@@ -297,9 +296,9 @@ public abstract class CustomActionButton<T> : CustomActionButton where T : MonoB
SetOutline(false);
}
Target = newTarget;
Target = IsTargetValid(newTarget) ? newTarget : null;
SetOutline(true);
return base.CanUse() && Target;
}
}
+1 -1
View File
@@ -4,7 +4,7 @@
<LangVersion>latest</LangVersion>
<DebugType>embedded</DebugType>
<PackageId>AllOfUs.MiraAPI</PackageId>
<Version>0.1.0</Version>
<Version>0.1.2</Version>
<Description>Simple and easy to use Among Us modding API.</Description>
<Authors>Angxl, XtraCube</Authors>
<Company>All Of Us</Company>
+1 -1
View File
@@ -26,7 +26,7 @@ public partial class MiraApiPlugin : BasePlugin
{
Harmony.PatchAll();
ReactorCredits.Register("Mira API", Version.Truncate(11, "") ?? Version, true, ReactorCredits.AlwaysShow);
ReactorCredits.Register("Mira API", Version, true, ReactorCredits.AlwaysShow);
PluginManager = new MiraPluginManager();
PluginManager.Initialize();
+1 -1
View File
@@ -84,7 +84,7 @@ public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr)
var modifierComponent = target.GetModifierComponent();
var modifier = modifierComponent.ActiveModifiers.Find(x => x.GetType() == type);
var modifier = modifierComponent?.ActiveModifiers.Find(x => x.GetType() == type);
if (modifier is null)
{
+45 -26
View File
@@ -6,6 +6,8 @@ using Reactor.Networking.Rpc;
using Reactor.Utilities.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using Reactor.Utilities;
using Random = System.Random;
namespace MiraAPI.Modifiers;
@@ -40,12 +42,27 @@ public static class ModifierManager
List<uint> filteredModifiers = [];
foreach (var modifier in IdToTypeModifiers)
foreach (var modifier in IdToTypeModifiers.Where(pair => pair.Value.IsAssignableTo(typeof(GameModifier))))
{
var mod = (GameModifier)Activator.CreateInstance(modifier.Value);
var num = mod.GetAmountPerGame();
var chance = mod.GetAssignmentChance();
if (mod is null)
{
Logger<MiraApiPlugin>.Error($"Failed to create instance of {modifier.Value.Name}");
continue;
}
if (!plrs.Any(x=>IsGameModifierValid(x, mod, modifier.Key)))
{
Logger<MiraApiPlugin>.Warning("No players are valid for modifier: " + mod.ModifierName);
continue;
}
var maxCount = plrs.Count(x=>IsGameModifierValid(x, mod, modifier.Key));
var num = Math.Clamp(mod.GetAmountPerGame(), 0, maxCount);
var chance = mod.GetAssignmentChance();
for (var i = 0; i < num; i++)
{
var randomNum = rand.Next(100);
@@ -62,23 +79,28 @@ public static class ModifierManager
{
shuffledModifiers = shuffledModifiers.GetRange(0, plrs.Count);
}
while (shuffledModifiers.Count > 0)
{
var id = shuffledModifiers[0];
var mod = (GameModifier)Activator.CreateInstance(IdToTypeModifiers[id]);
if (mod is null)
{
Logger<MiraApiPlugin>.Error($"Failed to create instance of {IdToTypeModifiers[id].Name}");
continue;
}
if (!plrs.Any(x=>IsGameModifierValid(x, mod, id)))
{
shuffledModifiers.RemoveAt(0);
continue;
}
var plr = plrs.Random();
if (plr.Data.Role is ICustomRole modRole)
{
if (!modRole.IsModifierApplicable(mod))
{
continue;
}
}
if (plr.HasModifier(id) || mod.IsModifierValidOn(plr.Data.Role))
if (!IsGameModifierValid(plr, mod, id))
{
continue;
}
@@ -88,26 +110,23 @@ public static class ModifierManager
}
}
private static bool IsGameModifierValid(PlayerControl player, GameModifier modifier, uint modifierId)
{
return (player.Data.Role is not ICustomRole role || role.IsModifierApplicable(modifier)) &&
modifier.IsModifierValidOn(player.Data.Role) &&
!player.HasModifier(modifierId);
}
internal static void SyncAllModifiers(int targetId = -1)
{
var data = new List<NetData>();
if (targetId == -1)
foreach (var player in GameData.Instance.AllPlayers)
{
foreach (var player in GameData.Instance.AllPlayers)
{
data.Add(GetPlayerModifiers(player.Object));
}
}
else
{
var player = GameData.Instance.GetPlayerById((byte)targetId).Object;
data.Add(GetPlayerModifiers(player));
data.Add(GetPlayerModifiers(player.Object));
}
Rpc<SyncModifiersRpc>.Instance.Send(PlayerControl.LocalPlayer, data.ToArray(), true);
Rpc<SyncModifiersRpc>.Instance.SendTo(PlayerControl.LocalPlayer, targetId, data.ToArray());
}
internal static void HandleSyncModifiers(NetData[] data)
+6
View File
@@ -6,6 +6,7 @@ public abstract class TimedModifier : BaseModifier
{
public abstract float Duration { get; }
public virtual bool AutoStart => false;
public virtual bool RemoveOnComplete => true;
public abstract void OnTimerComplete();
public bool TimerActive { get; protected set; }
@@ -28,6 +29,11 @@ public abstract class TimedModifier : BaseModifier
TimerActive = false;
TimeRemaining = Duration;
OnTimerComplete();
if (RemoveOnComplete)
{
ModifierComponent.RpcRemoveModifier(Player, ModifierId);
}
}
}
+1 -1
View File
@@ -12,7 +12,7 @@ public static class MeetingHudPatches
{
foreach (var customActionButton in CustomButtonManager.CustomButtons)
{
customActionButton.ResetCooldown();
customActionButton.ResetCooldownAndOrEffect();
}
}
}
@@ -3,7 +3,6 @@ using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets;
using Reactor.Localization.Utilities;
using Reactor.Utilities.Extensions;
using System.Collections.Generic;
using System.Linq;
using TMPro;
using UnityEngine;
@@ -102,7 +101,7 @@ public static class GameOptionsMenuPatch
var options = group.Options.Select(opt => opt.CreateOption(__instance.checkboxOrigin,
__instance.numberOptionOrigin, __instance.stringOptionOrigin, __instance.settingsContainer));
foreach (var newOpt in options)
{
newOpt.SetClickMask(__instance.ButtonClickMask);
@@ -137,10 +136,10 @@ public static class GameOptionsMenuPatch
if (newOpt is ToggleOption toggle)
{
toggle.CheckMark.sprite = MiraAssets.Checkmark.LoadAsset();
toggle.CheckMark.color = group.GroupColor != Color.clear ? group.GroupColor : MiraAssets.AcceptedTeal;
var rend = toggle.CheckMark.transform.parent.FindChild("ActiveSprite").GetComponent<SpriteRenderer>();
rend.sprite = MiraAssets.CheckmarkBox.LoadAsset();
rend.color = group.GroupColor;
rend.color = group.GroupColor != Color.clear ? group.GroupColor : MiraAssets.AcceptedTeal;
}
__instance.Children.Add(newOpt);
@@ -1,4 +1,5 @@
using HarmonyLib;
#nullable enable
using HarmonyLib;
using MiraAPI.PluginLoading;
using MiraAPI.Utilities.Assets;
using Reactor.Utilities.Extensions;
@@ -17,9 +18,9 @@ public static class GameSettingMenuPatches
{
public static int CurrentSelectedMod { get; private set; }
public static MiraPluginInfo SelectedMod { get; private set; }
public static MiraPluginInfo? SelectedMod { get; private set; }
private static TextMeshPro _text;
private static TextMeshPro? _text;
private static Vector3 _roleBtnOgPos;
@@ -80,12 +81,12 @@ public static class GameSettingMenuPatches
private static void UpdateText(GameSettingMenu menu, GameOptionsMenu settings, RolesSettingsMenu roles)
{
if (CurrentSelectedMod == 0)
if (_text is not null && CurrentSelectedMod == 0)
{
_text.text = "Default";
_text.fontSizeMax = 3.2f;
}
else
else if (_text is not null)
{
_text.fontSizeMax = 2.3f;
SelectedMod = MiraPluginManager.Instance.RegisteredPlugins.ElementAt(CurrentSelectedMod - 1).Value;
@@ -153,7 +154,7 @@ public static class GameSettingMenuPatches
roles.scrollBar.ScrollToTop();
}
if (settings?.Children != null && SelectedMod?.OptionGroups.Count != 0)
if (settings.Children != null && SelectedMod?.OptionGroups.Count != 0)
{
foreach (var child in settings.Children)
{
@@ -0,0 +1,39 @@
using HarmonyLib;
using Il2CppSystem;
using MiraAPI.Roles;
using System.Linq;
namespace MiraAPI.Patches.Options;
[HarmonyPatch(typeof(NotificationPopper))]
public class NotificationPopperPatch
{
[HarmonyPrefix]
[HarmonyPatch(nameof(NotificationPopper.AddRoleSettingsChangeMessage))]
public static bool RoleChangeMsgPatch(NotificationPopper __instance,
[HarmonyArgument(0)] StringNames key, [HarmonyArgument(1)] int roleCount, [HarmonyArgument(2)] int roleChance, [HarmonyArgument(3)] RoleTeamTypes teamType,
[HarmonyArgument(4)] bool playSound)
{
if (CustomRoleManager.CustomRoles.Values.Any(role => role.StringName == key))
{
string item = string.Empty;
string text = (teamType == RoleTeamTypes.Crewmate) ? Palette.CrewmateSettingChangeText.ToTextColor() : Palette.ImpostorRed.ToTextColor();
item = DestroyableSingleton<TranslationController>.Instance.GetString(StringNames.LobbyChangeSettingNotificationRole, new Object[]
{
string.Concat(
[
"<font=\"Barlow-Black SDF\" material=\"Barlow-Black Outline\">",
text,
DestroyableSingleton<TranslationController>.Instance.GetString(key, Array.Empty<Object>()),
"</color></font>"
]),
"<font=\"Barlow-Black SDF\" material=\"Barlow-Black Outline\">" + roleCount.ToString() + "</font>",
"<font=\"Barlow-Black SDF\" material=\"Barlow-Black Outline\">" + roleChance.ToString() + "%"
});
__instance.SettingsChangeMessageLogic(key, item, playSound);
return false;
}
return true;
}
}
@@ -224,11 +224,20 @@ public static class RoleSettingMenuPatches
__instance.roleScreenshot.drawMode = SpriteDrawMode.Sliced;
__instance.roleHeaderSprite.color = customRole.RoleColor;
__instance.roleHeaderText.color = customRole.RoleColor.GetAlternateColor();
var bg = __instance.AdvancedRolesSettings.transform.Find("Background");
bg.localPosition = new Vector3(1.4041f, -7.08f, 0);
bg.GetComponent<SpriteRenderer>().size = new Vector2(89.4628f, 100);
CreateAdvancedSettings(__instance, role);
foreach (var optionBehaviour in __instance.advancedSettingChildren)
{
if (optionBehaviour.IsCustom())
{
continue;
}
optionBehaviour.OnValueChanged = new Action<OptionBehaviour>(__instance.ValueChanged);
if (AmongUsClient.Instance && !AmongUsClient.Instance.AmHost)
{
@@ -254,27 +263,28 @@ public static class RoleSettingMenuPatches
roleOptionSetting.titleText.transform.localPosition = new Vector3(-0.5376f, -0.2923f, 0f);
roleOptionSetting.titleText.color = customRole.RoleColor.GetAlternateColor();
roleOptionSetting.titleText.horizontalAlignment = HorizontalAlignmentOptions.Left;
var newButton = Object.Instantiate(roleOptionSetting.buttons[0], roleOptionSetting.transform);
newButton.name = "ConfigButton";
newButton.transform.localPosition = new Vector3(0.4473f, -0.3f, -2f);
newButton.transform.FindChild("Text_TMP").gameObject.DestroyImmediate();
newButton.activeSprites.Destroy();
var btnRend = newButton.transform.FindChild("ButtonSprite").GetComponent<SpriteRenderer>();
btnRend.sprite = MiraAssets.Cog.LoadAsset();
var passiveButton = newButton.GetComponent<GameOptionButton>();
passiveButton.OnClick = new ButtonClickedEvent();
passiveButton.interactableColor = btnRend.color = customRole.RoleColor.GetAlternateColor();
passiveButton.interactableHoveredColor = Color.white;
passiveButton.OnClick.AddListener((UnityAction)(() =>
if (GameSettingMenuPatches.SelectedMod is null ||
GameSettingMenuPatches.SelectedMod.Options.Any(x => x.AdvancedRole != null && x.AdvancedRole.IsInstanceOfType(role)))
{
ChangeTab(role, __instance);
}));
var newButton = Object.Instantiate(roleOptionSetting.buttons[0], roleOptionSetting.transform);
newButton.name = "ConfigButton";
newButton.transform.localPosition = new Vector3(0.4473f, -0.3f, -2f);
newButton.transform.FindChild("Text_TMP").gameObject.DestroyImmediate();
newButton.activeSprites.Destroy();
if (index < GameSettingMenuPatches.SelectedMod.CustomRoles.Count - 1)
var btnRend = newButton.transform.FindChild("ButtonSprite").GetComponent<SpriteRenderer>();
btnRend.sprite = MiraAssets.Cog.LoadAsset();
var passiveButton = newButton.GetComponent<GameOptionButton>();
passiveButton.OnClick = new ButtonClickedEvent();
passiveButton.interactableColor = btnRend.color = customRole.RoleColor.GetAlternateColor();
passiveButton.interactableHoveredColor = Color.white;
passiveButton.OnClick.AddListener((UnityAction)(() => { ChangeTab(role, __instance); }));
}
if (index < GameSettingMenuPatches.SelectedMod?.CustomRoles.Count - 1)
{
yPos += -0.43f;
}
+42
View File
@@ -0,0 +1,42 @@
using HarmonyLib;
using MiraAPI.Utilities;
using UnityEngine;
namespace MiraAPI.Patches.Options;
[HarmonyPatch]
public class SettingPatches
{
[HarmonyPrefix]
[HarmonyPatch(typeof(FloatGameSetting), nameof(FloatGameSetting.GetValueString))]
public static bool ValueStringPatch(FloatGameSetting __instance, ref string __result, [HarmonyArgument(0)] float value)
{
string result = string.Empty;
MiraNumberSuffixes suffix = (MiraNumberSuffixes)__instance.SuffixType;
if (__instance.ZeroIsInfinity && Mathf.Abs(value) < 0.0001f)
{
result = "<b>∞</b>";
}
else if (suffix == MiraNumberSuffixes.None)
{
result = value.ToString(__instance.FormatString);
}
else if (suffix == MiraNumberSuffixes.Multiplier)
{
result = value.ToString(__instance.FormatString) + "x";
}
else if (suffix == MiraNumberSuffixes.Percent)
{
result = value.ToString(__instance.FormatString) + "%";
}
else
{
result = DestroyableSingleton<TranslationController>.Instance.GetString(StringNames.GameSecondsAbbrev, (Il2CppSystem.Object[])(
[
value.ToString(__instance.FormatString)
]));
}
__result = result;
return false;
}
}
+2 -2
View File
@@ -37,9 +37,9 @@ public static class PlayerControlPatches
foreach (var button in CustomButtonManager.CustomButtons)
{
if (__instance.Data?.Role != null)
if (__instance.Data?.Role == null)
{
return;
continue;
}
if (!button.Enabled(__instance.Data?.Role))
Binary file not shown.

Before

Width:  |  Height:  |  Size: 370 B

After

Width:  |  Height:  |  Size: 1.0 KiB

+5
View File
@@ -205,5 +205,10 @@ public static class CustomRoleManager
Logger<MiraApiPlugin>.Error(e);
}
}
if (LobbyInfoPane.Instance)
{
LobbyInfoPane.Instance.RefreshPane();
}
}
}
+6 -1
View File
@@ -1,4 +1,6 @@
namespace MiraAPI.Utilities.Assets;
using UnityEngine;
namespace MiraAPI.Utilities.Assets;
public static class MiraAssets
{
@@ -9,4 +11,7 @@ public static class MiraAssets
public static readonly LoadableResourceAsset Cog = new("MiraAPI.Resources.Cog.png");
public static readonly LoadableResourceAsset Checkmark = new("MiraAPI.Resources.Checkmark.png");
public static readonly LoadableResourceAsset CheckmarkBox = new("MiraAPI.Resources.CheckMarkBox.png");
public static readonly Color32 AcceptedTeal = new Color32(43, 233, 198, 255);
}
+5 -4
View File
@@ -46,13 +46,14 @@ TextAlignmentOptions textAlignment = TextAlignmentOptions.Center)
return Object.FindObjectsOfType<DeadBody>().FirstOrDefault(body => body.ParentId == id);
}
public static string GetSuffix(NumberSuffixes suffix)
public static string GetSuffix(MiraNumberSuffixes suffix)
{
return suffix switch
{
NumberSuffixes.None => string.Empty,
NumberSuffixes.Multiplier => "x",
NumberSuffixes.Seconds => "s",
MiraNumberSuffixes.None => string.Empty,
MiraNumberSuffixes.Multiplier => "x",
MiraNumberSuffixes.Seconds => "s",
MiraNumberSuffixes.Percent => "%",
_ => string.Empty
};
}
+8
View File
@@ -0,0 +1,8 @@
namespace MiraAPI.Utilities;
public enum MiraNumberSuffixes
{
None,
Multiplier,
Seconds,
Percent
}
+2
View File
@@ -57,6 +57,8 @@ Roles are very simple in Mira API. There are 3 things you need to do to create a
2. Implement the `ICustomRole` interface from Mira API.
3. Add the `[RegisterCustomRole]` attribute to the class.
**Disclaimer: Make sure your plugin class has the following attribute `[ReactorModFlags(ModFlags.RequireOnAllClients)]` or else your roles will not register correctly.**
Note: For step 1, if you are making neutral roles, choose either `CrewmateRole` or `ImpostorRole` as the base depending on if it can kill or not!
Mira API handles everything else, from adding the proper options to the settings menu, to managing the role assignment at the start of the game. There are no extra steps on the developer's part.