mirror of
https://github.com/All-Of-Us-Mods/MiraAPI.git
synced 2026-10-20 11:39:45 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19c7e38690 | ||
|
|
f1639a784d | ||
|
|
5f1996b457 | ||
|
|
33d389e107 | ||
|
|
176d472cd7 | ||
|
|
7a3406e0ae | ||
|
|
9f92af8958 | ||
|
|
8d167596d2 | ||
|
|
a98a37bea7 | ||
|
|
4963c80395 | ||
|
|
4bef2f838d | ||
|
|
763017b6e2 | ||
|
|
16fd8e02aa | ||
|
|
f187a15f2e | ||
|
|
e211c4c49c | ||
|
|
7f532cd82f | ||
|
|
7a0396fe5c | ||
|
|
d4955b5183 | ||
|
|
0a5c59d9cd | ||
|
|
774e1a7a2b | ||
|
|
8e6f75cfbd | ||
|
|
b36b434a99 | ||
|
|
858e518a57 | ||
|
|
b9734f5a1c |
No files matched your search
@@ -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>();
|
||||
}
|
||||
}
|
||||
+13
-9
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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>();
|
||||
}
|
||||
})
|
||||
{
|
||||
|
||||
+2
-8
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -129,6 +129,11 @@ public static class ModdedOptionsManager
|
||||
|
||||
option.HandleNetData(netData.Data);
|
||||
}
|
||||
|
||||
if (LobbyInfoPane.Instance)
|
||||
{
|
||||
LobbyInfoPane.Instance.RefreshPane();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -71,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)
|
||||
@@ -101,19 +101,19 @@ public abstract class CustomActionButton
|
||||
}
|
||||
|
||||
/// <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>
|
||||
@@ -136,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();
|
||||
|
||||
@@ -154,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() { }
|
||||
|
||||
@@ -239,7 +240,7 @@ public abstract class CustomActionButton
|
||||
{
|
||||
Button?.SetDisabled();
|
||||
}
|
||||
|
||||
|
||||
Button?.SetCoolDown(Timer, EffectActive ? EffectDuration : Cooldown);
|
||||
|
||||
FixedUpdate(playerControl);
|
||||
@@ -256,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>
|
||||
@@ -270,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();
|
||||
@@ -298,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;
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ public static class MeetingHudPatches
|
||||
{
|
||||
foreach (var customActionButton in CustomButtonManager.CustomButtons)
|
||||
{
|
||||
customActionButton.ResetCooldown();
|
||||
customActionButton.ResetCooldownAndOrEffect();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -205,5 +205,10 @@ public static class CustomRoleManager
|
||||
Logger<MiraApiPlugin>.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (LobbyInfoPane.Instance)
|
||||
{
|
||||
LobbyInfoPane.Instance.RefreshPane();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
Reference in new issue
Block a user