Compare commits

..
10 Commits
Author SHA1 Message Date
XtraCube 575f7a78a1 contexts 2025-07-24 21:35:41 -04:00
XtraCube bf7edb30ab fix warnings 2025-07-20 07:30:38 -04:00
XtraCube b4527014b7 Merge pull request #44 from AtonyGit/dev
Allow modifiers to have freeplay colors
2025-07-16 14:41:01 -04:00
Atony 277391ef1c Add freeplay modifier colors 2025-07-11 22:30:09 -05:00
XtraCube 6b3038f397 shuffle modifiers before assigning 2025-07-07 18:47:12 -04:00
XtraCube 4e1db039a0 Merge pull request #42 from AtonyGit/dev
Fix Regions Dropdown in "Find Game" & Add Tab Count for Cosmetics Menu
2025-07-05 15:36:08 -04:00
Atony 62bee24789 Fix Server Dropdown for Game Finder 2025-07-04 22:43:13 -05:00
Atony 2156c80467 Cosmetic Tab Count Improvements 2025-07-04 22:42:47 -05:00
XtraCube 0ff60071c2 fix canuse event 2025-06-30 20:47:17 -04:00
XtraCube de2f3f0d95 update assetbundle 2025-06-29 22:31:21 -04:00
32 changed files with 214 additions and 258 deletions

No files matched your search

@@ -1,31 +0,0 @@
using System;
using BepInEx;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using MiraAPI.PluginLoading;
using Reactor;
using Reactor.Networking;
using Reactor.Networking.Attributes;
namespace MiraAPI.Example.Extension;
[BepInAutoPlugin("mira.example.extension", "MiraExampleExtensionMod")]
[BepInProcess("Among Us.exe")]
[BepInDependency(ReactorPlugin.Id)]
[BepInDependency(MiraApiPlugin.Id)]
[BepInDependency(ExamplePlugin.Id)]
[ReactorModFlags(ModFlags.RequireOnAllClients)]
public partial class ExampleExtensionPlugin : BasePlugin, IMiraExtension
{
public Harmony Harmony { get; } = new(Id);
public override void Load()
{
Harmony.PatchAll();
}
public Type GetBasePluginType()
{
return typeof(ExamplePlugin);
}
}
@@ -1,14 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version>1.0.0</Version>
<Description>Example extension for Mira API</Description>
<Authors>Angxl, XtraCube</Authors>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MiraAPI\MiraAPI.csproj" />
<ProjectReference Include="..\MiraAPI.Example\MiraAPI.Example.csproj" />
</ItemGroup>
<Import Project="..\AmongUs.props" />
</Project>
@@ -1,16 +0,0 @@
using MiraAPI.Example.Extension.Options;
using MiraAPI.GameOptions;
using MiraAPI.Modifiers.Types;
namespace MiraAPI.Example.Extension.Modifiers;
public class ExtensionModifier : GameModifier
{
public override string ModifierName => "Extension Modifier";
public override string GetDescription() => "This is from the extension mod!";
public override int GetAssignmentChance() => (int)OptionGroupSingleton<ExtensionModifierOptions>.Instance.ModifierChance;
public override int GetAmountPerGame() => (int)OptionGroupSingleton<ExtensionModifierOptions>.Instance.ModifierLimit;
}
@@ -1,13 +0,0 @@
using MiraAPI.Example.Roles;
using MiraAPI.GameOptions;
using MiraAPI.GameOptions.Attributes;
namespace MiraAPI.Example.Extension.Options;
public class ExtendedFreezerOptions : AbstractOptionGroup<FreezerRole>
{
public override string GroupName => "Extended Freezer Role Options";
[ModdedNumberOption("Freeze Uses", 1, 5)]
public float FreezeUses { get; set; } = 1;
}
@@ -1,17 +0,0 @@
using MiraAPI.Example.Extension.Modifiers;
using MiraAPI.GameOptions;
using MiraAPI.GameOptions.Attributes;
using MiraAPI.Utilities;
namespace MiraAPI.Example.Extension.Options;
public class ExtensionModifierOptions : AbstractOptionGroup<ExtensionModifier>
{
public override string GroupName => "Extension Modifier Options";
[ModdedNumberOption("Modifier Chance", 0, 100, 10, MiraNumberSuffixes.Percent)]
public float ModifierChance { get; set; } = 100f;
[ModdedNumberOption("Modifier Limit", 0, 10)]
public float ModifierLimit { get; set; } = 0;
}
@@ -1,15 +0,0 @@
using HarmonyLib;
using MiraAPI.Example.Buttons.Freezer;
using MiraAPI.Example.Extension.Options;
using MiraAPI.GameOptions;
namespace MiraAPI.Example.Extension.Patches;
[HarmonyPatch(typeof(FreezeButton), nameof(FreezeButton.MaxUses), MethodType.Getter)]
public static class FreezeButtonPatch
{
public static void Postfix(ref int __result)
{
__result = (int)OptionGroupSingleton<ExtendedFreezerOptions>.Instance.FreezeUses;
}
}
@@ -16,7 +16,7 @@ public class FreezeButton : CustomActionButton<PlayerControl>
public override float Cooldown => OptionGroupSingleton<FreezerRoleSettings>.Instance.FreezeDuration;
public override int MaxUses => 1;
public override int MaxUses => (int)OptionGroupSingleton<FreezerRoleSettings>.Instance.FreezeUses;
public override LoadableAsset<Sprite> Sprite => ExampleAssets.ExampleButton;
@@ -1,20 +1,17 @@
using MiraAPI.Example.Extension.GameOver;
using MiraAPI.Example.Extension.Roles;
using MiraAPI.Example.GameOver;
using MiraAPI.Example.Roles;
using MiraAPI.GameEnd;
using MiraAPI.Hud;
using MiraAPI.Utilities.Assets;
using UnityEngine;
namespace MiraAPI.Example.Extension.Buttons;
namespace MiraAPI.Example.Buttons;
public class NeutralKillerButton : CustomActionButton
{
public override string Name => "Win Game";
public override float Cooldown => 0f;
public override LoadableAsset<Sprite> Sprite => ExampleAssets.ExampleButton;
protected override void OnClick()
{
CustomGameOver.Trigger<NeutralKillerGameOver>([PlayerControl.LocalPlayer.Data]);
+1 -1
View File
@@ -51,7 +51,7 @@ public static class ExampleEventHandlers
[RegisterEvent(15)]
public static void HandleVoteEvent(HandleVoteEvent @event)
{
if (@event.VoteData.Owner.Data.Role is not FreezerRole) return;
if (@event.VoteData.Owner.Data.Role is not NeutralKillerRole) return;
@event.VoteData.SetRemainingVotes(0);
@@ -1,9 +1,9 @@
using MiraAPI.Example.Extension.Roles;
using MiraAPI.Example.Roles;
using MiraAPI.GameEnd;
using MiraAPI.Utilities;
using UnityEngine;
namespace MiraAPI.Example.Extension.GameOver;
namespace MiraAPI.Example.GameOver;
public class NeutralKillerGameOver : CustomGameOver
{
@@ -11,4 +11,7 @@ public class FreezerRoleSettings : AbstractOptionGroup<FreezerRole>
[ModdedNumberOption("Freeze Duration", 1, 15, 1, MiraNumberSuffixes.Seconds)]
public float FreezeDuration { get; set; } = 5;
[ModdedNumberOption("Freeze Uses", 1, 5)]
public float FreezeUses { get; set; } = 1;
}
@@ -1,20 +1,16 @@
using MiraAPI.Example.Extension.GameOver;
using MiraAPI.Example.GameOver;
using MiraAPI.GameEnd;
using MiraAPI.Roles;
using UnityEngine;
namespace MiraAPI.Example.Extension.Roles;
namespace MiraAPI.Example.Roles;
public class NeutralKillerRole : ImpostorRole, ICustomRole
{
public string RoleName => "Outcast Killer";
public string RoleDescription => "Outcast who can kill.";
public string RoleLongDescription => this.RoleDescription;
public string RoleLongDescription => RoleDescription;
public Color RoleColor => Color.magenta;
public ModdedRoleTeams Team => ModdedRoleTeams.Custom;
public CustomRoleConfiguration Configuration => new(this)
-6
View File
@@ -7,8 +7,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MiraAPI", "MiraAPI\MiraAPI.
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiraAPI.Example", "MiraAPI.Example\MiraAPI.Example.csproj", "{19B76855-16CC-43CC-A0A6-5A7FFB43F077}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiraAPI.Example.Extension", "MiraAPI.Example.Extension\MiraAPI.Example.Extension.csproj", "{526FF017-80E1-4094-804D-741AF35AA226}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -23,10 +21,6 @@ Global
{19B76855-16CC-43CC-A0A6-5A7FFB43F077}.Debug|Any CPU.Build.0 = Debug|Any CPU
{19B76855-16CC-43CC-A0A6-5A7FFB43F077}.Release|Any CPU.ActiveCfg = Release|Any CPU
{19B76855-16CC-43CC-A0A6-5A7FFB43F077}.Release|Any CPU.Build.0 = Release|Any CPU
{526FF017-80E1-4094-804D-741AF35AA226}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{526FF017-80E1-4094-804D-741AF35AA226}.Debug|Any CPU.Build.0 = Debug|Any CPU
{526FF017-80E1-4094-804D-741AF35AA226}.Release|Any CPU.ActiveCfg = Release|Any CPU
{526FF017-80E1-4094-804D-741AF35AA226}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
+6
View File
@@ -0,0 +1,6 @@
namespace MiraAPI.Game;
public enum PlayerContext
{
}
+5
View File
@@ -64,6 +64,11 @@ public abstract class BaseModifier : IOptionable
/// </summary>
public virtual bool ShowInFreeplay => false;
/// <summary>
/// Gets a value indicating the color that should be used for the modifier within freeplay.
/// </summary>
public virtual Color FreeplayFileColor => Color.gray;
/// <summary>
/// Gets a value indicating whether the modifier is unique. If true, the player can only have one instance of this modifier.
/// </summary>
+6
View File
@@ -0,0 +1,6 @@
namespace MiraAPI.Modifiers;
public enum ModifierContext
{
}
+2
View File
@@ -5,6 +5,7 @@ using System.Runtime.Serialization;
using MiraAPI.Modifiers.Types;
using MiraAPI.PluginLoading;
using MiraAPI.Roles;
using MiraAPI.Utilities;
using Reactor.Utilities;
using Reactor.Utilities.Extensions;
using Random = System.Random;
@@ -125,6 +126,7 @@ public static class ModifierManager
.Select(x => Activator.CreateInstance(x.Value) as GameModifier)
.OfType<GameModifier>()
.Where(x => x.GetAmountPerGame() > 0 && x.GetAssignmentChance() > 0)
.Shuffle()
.OrderByDescending(x => x.Priority())
.ToArray();
+2
View File
@@ -3,10 +3,12 @@ using UnityEngine;
namespace MiraAPI.Patches;
#pragma warning disable SA1629
/// <summary>
/// Allows players to paste text into chat.
/// Source: https://github.com/CallOfCreator/NewMod/blob/main/NewMod/Patches/ClipboardPatch.cs
/// </summary>
#pragma warning restore SA1629
[HarmonyPatch(typeof(ChatController), nameof(ChatController.Update))]
public static class ChatControllerPatch
{
+7 -5
View File
@@ -12,14 +12,16 @@ public static class CanUsePatches
public static IEnumerable<MethodBase> TargetMethods()
{
yield return AccessTools.Method(typeof(Console), nameof(Console.CanUse));
yield return AccessTools.Method(typeof(MapConsole), nameof(MapConsole.CanUse));
yield return AccessTools.Method(typeof(SystemConsole), nameof(SystemConsole.CanUse));
yield return AccessTools.Method(typeof(Ladder), nameof(Ladder.CanUse));
yield return AccessTools.Method(typeof(PlatformConsole), nameof(PlatformConsole.CanUse));
yield return AccessTools.Method(typeof(OpenDoorConsole), nameof(OpenDoorConsole.CanUse));
yield return AccessTools.Method(typeof(DeconControl), nameof(DeconControl.CanUse));
yield return AccessTools.Method(typeof(DoorConsole), nameof(DoorConsole.CanUse));
yield return AccessTools.Method(typeof(Ladder), nameof(Ladder.CanUse));
yield return AccessTools.Method(typeof(MapConsole), nameof(MapConsole.CanUse));
yield return AccessTools.Method(typeof(OpenDoorConsole), nameof(OpenDoorConsole.CanUse));
yield return AccessTools.Method(typeof(OptionsConsole), nameof(OptionsConsole.CanUse));
yield return AccessTools.Method(typeof(PlatformConsole), nameof(PlatformConsole.CanUse));
yield return AccessTools.Method(typeof(SystemConsole), nameof(SystemConsole.CanUse));
yield return AccessTools.Method(typeof(ZiplineConsole), nameof(ZiplineConsole.CanUse));
// vent is not necessary as it has its own patch
}
[HarmonyPriority(Priority.Last)]
@@ -22,15 +22,4 @@ internal static class TaskAddButtonPatches
return true;
}
[HarmonyPostfix]
[HarmonyPatch(nameof(TaskAddButton.Role), MethodType.Setter)]
public static void RoleGetterPatch(TaskAddButton __instance)
{
if (__instance.role is ICustomRole { Team: ModdedRoleTeams.Custom } customRole)
{
__instance.FileImage.color = customRole.IntroConfiguration?.IntroTeamColor ?? Color.gray;
}
__instance.RolloverHandler.OutColor = __instance.FileImage.color;
}
}
+23 -3
View File
@@ -10,6 +10,7 @@ using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
using Object = UnityEngine.Object;
@@ -19,7 +20,6 @@ namespace MiraAPI.Patches.Freeplay;
internal static class TaskAdderPatches
{
private static Scroller _scroller = null!;
[HarmonyPostfix]
[HarmonyPatch(nameof(TaskAdderGame.Begin))]
public static void AddRolesFolder(TaskAdderGame __instance)
@@ -32,6 +32,11 @@ internal static class TaskAdderPatches
_scroller.allowY = true;
_scroller.Inner = inner.transform;
var scrollerHelper = __instance.TaskParent.gameObject.AddComponent<ManualScrollHelper>();
scrollerHelper.scroller = _scroller;
scrollerHelper.verticalAxis = RewiredConstsEnum.Action.TaskRVertical;
scrollerHelper.scrollSpeed = 10f;
GameObject hitbox = new("Hitbox")
{
layer = 5,
@@ -295,7 +300,7 @@ internal static class TaskAdderPatches
if (_scroller)
{
_scroller.CalculateAndSetYBounds(__instance.ActiveItems.Count, 6, 5, 1.4f);
_scroller.CalculateAndSetYBounds(__instance.ActiveItems.Count, 6, 4.5f, 1.65f);
_scroller.SetYBoundsMin(0.0f);
_scroller.ScrollToTop();
}
@@ -332,7 +337,8 @@ internal static class TaskAdderPatches
taskAddButton.SafePositionWorld = instance.SafePositionWorld;
taskAddButton.Text.text = modifier.ModifierName;
taskAddButton.Text.EnableMasking();
taskAddButton.FileImage.color = modifier.FreeplayFileColor;
taskAddButton.RolloverHandler.OutColor = modifier.FreeplayFileColor;
taskAddButton.Button.OnClick = new Button.ButtonClickedEvent();
taskAddButton.Button.OnClick.AddListener((UnityEngine.Events.UnityAction)(() =>
{
@@ -384,6 +390,20 @@ internal static class TaskAdderPatches
instance.AddFileAsChildCustom(taskAddButton2, ref num, ref num2, ref num3);
taskAddButton2.Role = roleBehaviour;
if (roleBehaviour is ICustomRole custom)
{
var customColor = custom.IntroConfiguration?.IntroTeamColor ?? Color.gray;
if (custom.Team is ModdedRoleTeams.Crewmate)
{
customColor = Palette.CrewmateBlue;
}
else if (custom.Team is ModdedRoleTeams.Impostor)
{
customColor = Palette.ImpostorRed;
}
taskAddButton2.FileImage.color = customColor;
taskAddButton2.RolloverHandler.OutColor = customColor;
}
if (taskAddButton2.Button == null)
{
continue;
+1 -1
View File
@@ -84,7 +84,7 @@ public static class HatsTabPatches
text.gameObject.transform.localScale = Vector3.one;
text.GetComponent<TextTranslatorTMP>().Destroy();
text.EnableStencilMasking();
text.text = $"{groupName}\nPress Ctrl or Tab to cycle pages";
text.text = $"{groupName} ({currentPage + 1}/{sortedHats.Count})\nPress Ctrl or Tab to cycle pages";
text.alignment = TextAlignmentOptions.Center;
text.fontSize = 3f;
text.fontSizeMax = 3f;
+1 -1
View File
@@ -73,7 +73,7 @@ public static class NameplatesTabPatches
text.gameObject.transform.localScale = Vector3.one;
text.GetComponent<TextTranslatorTMP>().Destroy();
text.EnableStencilMasking();
text.text = $"{groupName}\nPress Ctrl or Tab to cycle pages";
text.text = $"{groupName} ({currentPage + 1}/{SortedNameplates.Count})\nPress Ctrl or Tab to cycle pages";
text.alignment = TextAlignmentOptions.Center;
text.fontSize = 3f;
text.fontSizeMax = 3f;
+1 -1
View File
@@ -76,7 +76,7 @@ public static class SkinsTabPatches
text.gameObject.transform.localScale = Vector3.one;
text.GetComponent<TextTranslatorTMP>().Destroy();
text.EnableStencilMasking();
text.text = $"{groupName}\nPress Ctrl or Tab to cycle pages";
text.text = $"{groupName} ({currentPage + 1}/{sortedSkins.Count})\nPress Ctrl or Tab to cycle pages";
text.alignment = TextAlignmentOptions.Center;
text.fontSize = 3f;
text.fontSizeMax = 3f;
+1 -1
View File
@@ -72,7 +72,7 @@ public static class VisorsTabPatches
text.gameObject.transform.localScale = Vector3.one;
text.GetComponent<TextTranslatorTMP>().Destroy();
text.EnableStencilMasking();
text.text = $"{groupName}\nPress Ctrl or Tab to cycle pages";
text.text = $"{groupName} ({currentPage + 1}/{SortedVisors.Count})\nPress Ctrl or Tab to cycle pages";
text.alignment = TextAlignmentOptions.Center;
text.fontSize = 3f;
text.fontSizeMax = 3f;
+8 -1
View File
@@ -1,6 +1,7 @@
using HarmonyLib;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
namespace MiraAPI.Patches;
@@ -14,6 +15,8 @@ public static class ServerDropdownPatch
foreach (var regionInfo in DestroyableSingleton<ServerManager>.Instance.AvailableRegions)
{
var findingGame = SceneManager.GetActiveScene().name is "FindAGame";
if (DestroyableSingleton<ServerManager>.Instance.CurrentRegion.Equals(regionInfo))
{
__instance.defaultButtonSelected = __instance.firstOption;
@@ -27,6 +30,10 @@ public static class ServerDropdownPatch
var region = regionInfo;
var serverListButton = __instance.ButtonPool.Get<ServerListButton>();
var x = num % 2 == 0 ? -2 : 2;
if (findingGame)
{
x += 2;
}
var y = -0.55f * (num / 2);
serverListButton.transform.localPosition = new Vector3(x, __instance.y_posButton + y, -1f);
serverListButton.transform.localScale = Vector3.one;
@@ -39,7 +46,7 @@ public static class ServerDropdownPatch
serverListButton.Button.OnClick.AddListener((UnityAction)(() => { __instance.ChooseOption(region); }));
__instance.controllerSelectable.Add(serverListButton.Button);
__instance.background.transform.localPosition = new Vector3(
0f,
findingGame ? 2f : 0f,
__instance.initialYPos + (-0.3f * (num / 2)),
0f);
__instance.background.size = new Vector2(__instance.background.size.x, 1.2f + (0.6f * (num / 2)));
-8
View File
@@ -1,8 +0,0 @@
using System;
namespace MiraAPI.PluginLoading;
public interface IMiraExtension
{
public Type GetBasePluginType();
}
+82 -96
View File
@@ -37,30 +37,100 @@ public sealed class MiraPluginManager
Instance = this;
IL2CPPChainloader.Instance.PluginLoad += (pluginInfo, assembly, plugin) =>
{
if (plugin is IMiraPlugin miraPlugin)
if (plugin is not IMiraPlugin miraPlugin)
{
var miraInfo = new MiraPluginInfo(miraPlugin, pluginInfo);
RegisterPlugin(miraInfo, assembly);
_registeredPlugins.Add(assembly, miraInfo);
return;
}
else if (plugin is IMiraExtension miraExtension)
var info = new MiraPluginInfo(miraPlugin, pluginInfo);
var roles = new List<Type>();
var oldConfigSetting = info.PluginConfig.SaveOnConfigSet;
info.PluginConfig.SaveOnConfigSet = false;
foreach (var type in assembly.GetTypes())
{
var basePlugin = miraExtension.GetBasePluginType();
var basePluginInfo = _registeredPlugins.Values.FirstOrDefault(x => x.MiraPlugin.GetType() == basePlugin);
if (basePluginInfo == null)
if (type.GetCustomAttribute<MiraIgnoreAttribute>() != null)
{
Logger<MiraApiPlugin>.Error($"Base plugin {basePlugin.Name} not found for extension {pluginInfo.Metadata.GUID}.");
return;
continue;
}
RegisterPlugin(basePluginInfo, assembly);
foreach (var method in type.GetMethods())
{
var eventAttribute = method.GetCustomAttribute<RegisterEventAttribute>();
if (eventAttribute == null)
{
continue;
}
if (!method.IsStatic)
{
Logger<MiraApiPlugin>.Error($"Event method {method.Name} in {type.Name} must be static.");
continue;
}
var parameters = method.GetParameters();
if (parameters.Length != 1 || !parameters[0].ParameterType.IsSubclassOf(typeof(MiraEvent)))
{
Logger<MiraApiPlugin>.Error($"Invalid event registration method {method.Name} in {type.Name}");
continue;
}
var paramType = parameters[0].ParameterType;
MiraEventManager.RegisterEventHandler(paramType, method, eventAttribute.Priority);
}
if (RegisterModifier(type, info))
{
continue;
}
if (RegisterOptions(type, info))
{
continue;
}
if (RegisterRole(type, info, out var role))
{
roles.Add(role);
continue;
}
if (RegisterButton(type, info))
{
continue;
}
if (RegisterGameOver(type))
{
continue;
}
RegisterColorClasses(type);
}
info.PluginConfig.Save();
info.PluginConfig.SaveOnConfigSet = oldConfigSetting;
info.InternalOptionGroups.Sort((x, y) => x.GroupPriority.CompareTo(y.GroupPriority));
QueuedRoleRegistrations.Add(info, roles);
_registeredPlugins.Add(assembly, info);
info.SavePublicCollections();
PresetManager.CreateDefaultPreset(info);
PresetManager.LoadPresets(info);
Logger<MiraApiPlugin>.Info($"Registering mod {pluginInfo.Metadata.GUID} with Mira API.");
};
IL2CPPChainloader.Instance.Finished += PaletteManager.RegisterAllColors;
IL2CPPChainloader.Instance.Finished += () =>
{
// Save all buttons into a read-only collection for easy access
CustomButtonManager.Buttons = new ReadOnlyCollection<CustomActionButton>(CustomButtonManager.CustomButtons);
RegisteredPlugins = [.. _registeredPlugins.Values];
// Cache all the registered plugins into an array for easy access
RegisteredPlugins = [.._registeredPlugins.Values];
ModifierManager.Modifiers = new ReadOnlyCollection<BaseModifier>(ModifierManager.InternalModifiers);
};
}
@@ -75,90 +145,6 @@ public sealed class MiraPluginManager
return Instance._registeredPlugins.Values.FirstOrDefault(plugin => plugin.PluginId == pluginId);
}
private void RegisterPlugin(MiraPluginInfo info, Assembly assembly)
{
Logger<MiraApiPlugin>.Info($"Registering plugin {info.PluginId} with Mira API.");
var roles = new List<Type>();
var oldConfigSetting = info.PluginConfig.SaveOnConfigSet;
info.PluginConfig.SaveOnConfigSet = false;
foreach (var type in assembly.GetTypes())
{
if (type.GetCustomAttribute<MiraIgnoreAttribute>() != null)
{
continue;
}
foreach (var method in type.GetMethods())
{
var eventAttribute = method.GetCustomAttribute<RegisterEventAttribute>();
if (eventAttribute == null)
{
continue;
}
if (!method.IsStatic)
{
Logger<MiraApiPlugin>.Error($"Event method {method.Name} in {type.Name} must be static.");
continue;
}
var parameters = method.GetParameters();
if (parameters.Length != 1 || !parameters[0].ParameterType.IsSubclassOf(typeof(MiraEvent)))
{
Logger<MiraApiPlugin>.Error($"Invalid event registration method {method.Name} in {type.Name}");
continue;
}
var paramType = parameters[0].ParameterType;
MiraEventManager.RegisterEventHandler(paramType, method, eventAttribute.Priority);
}
if (RegisterModifier(type, info))
{
continue;
}
if (RegisterOptions(type, info))
{
continue;
}
if (RegisterRole(type, info, out var role))
{
roles.Add(role);
continue;
}
if (RegisterButton(type, info))
{
continue;
}
if (RegisterGameOver(type))
{
continue;
}
RegisterColorClasses(type);
}
info.PluginConfig.Save();
info.PluginConfig.SaveOnConfigSet = oldConfigSetting;
info.InternalOptionGroups.Sort((x, y) => x.GroupPriority.CompareTo(y.GroupPriority));
QueuedRoleRegistrations.TryAdd(info, []);
QueuedRoleRegistrations[info].AddRange(roles);
info.SavePublicCollections();
PresetManager.CreateDefaultPreset(info);
PresetManager.LoadPresets(info);
Logger<MiraApiPlugin>.Info($"{info.PluginId} finished registering.");
}
private static bool RegisterGameOver(Type type)
{
try
Binary file not shown.
+6
View File
@@ -0,0 +1,6 @@
namespace MiraAPI.Roles;
public enum RoleContext
{
}
+1 -1
View File
@@ -49,4 +49,4 @@ public static class SpriteTools
sprite.name = resourcePath;
return sprite;
}
}
}
+49
View File
@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace MiraAPI.Utilities;
/// <summary>
/// Extensions for IEnumerable.
/// </summary>
public static class EnumerableExtensions
{
/// <summary>
/// Shuffle the elements of the source enumerable using a new instance of Random.
/// </summary>
/// <param name="source">The source enumerable.</param>
/// <typeparam name="T">The type of the elements.</typeparam>
/// <returns>>The shuffled enumerable.</returns>
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source)
{
return source.Shuffle(new Random());
}
/// <summary>
/// Shuffle the elements of the source enumerable using the provided Random instance.
/// </summary>
/// <param name="source">The source enumerable.</param>
/// <param name="rng">The Random instance to use for shuffling.</param>
/// <typeparam name="T">The type of the elements.</typeparam>
/// <returns>>The shuffled enumerable.</returns>
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
{
ArgumentNullException.ThrowIfNull(source);
ArgumentNullException.ThrowIfNull(rng);
return source.ShuffleIterator(rng);
}
private static IEnumerable<T> ShuffleIterator<T>(this IEnumerable<T> source, Random rng)
{
var buffer = source.ToList();
for (var i = 0; i < buffer.Count; i++)
{
var j = rng.Next(i, buffer.Count);
yield return buffer[j];
buffer[j] = buffer[i];
}
}
}