Merge pull request #16 from All-Of-Us-Mods/modifier-assignment-priority-2

Modifier assignment priority 2
This commit is contained in:
XtraCube authored and GitHub committed 2024-12-31 14:36:49 -05:00
commit 204d1fe123
7 files changed
+119 -50

No files matched your search

@@ -0,0 +1,24 @@
using MiraAPI.Modifiers;
using MiraAPI.Modifiers.Types;
namespace MiraAPI.Example.Modifiers;
[RegisterModifier]
public class HighPriorityModifier : GameModifier
{
public override string ModifierName => "High Priority";
public override int GetAssignmentChance()
{
return 100;
}
public override int GetAmountPerGame()
{
return 1;
}
public override int Priority()
{
return 100;
}
}
+1 -1
View File
@@ -284,7 +284,7 @@ public abstract class CustomActionButton
/// This method takes into account cooldowns, effects, and uses, before calling the <see cref="OnClick"/> method.
/// It can be overridden for custom behavior.
/// </summary>
protected virtual void ClickHandler()
public virtual void ClickHandler()
{
if (!CanUse())
{
+2
View File
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using AmongUs.GameOptions;
using Il2CppInterop.Runtime.Attributes;
using MiraAPI.Patches.Stubs;
using Reactor.Utilities.Attributes;
using Reactor.Utilities.Extensions;
@@ -84,6 +85,7 @@ public class CustomPlayerMenu(IntPtr il2CppPtr) : Minigame(il2CppPtr)
/// </summary>
/// <param name="playerMatch">Function to determine if player should show in the custom menu.</param>
/// <param name="onClick">Onclick action for player.</param>
[HideFromIl2Cpp]
public void Begin(Func<PlayerControl, bool> playerMatch, Action<PlayerControl> onClick)
{
MinigameStubs.Begin(this, null);
+20
View File
@@ -255,4 +255,24 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
{
return AddModifier(typeof(T)) as T;
}
/// <summary>
/// Checks if a player has an active or queued modifier by its ID.
/// </summary>
/// <param name="id">The Modifier ID.</param>
/// <returns>True if the Modifier is present, false otherwise.</returns>
public bool HasModifier(uint id)
{
return ActiveModifiers.Exists(x => x.ModifierId == id) || _toAdd.Exists(x => x.ModifierId == id);
}
/// <summary>
/// Checks if a player has an active or queued modifier by its type.
/// </summary>
/// <typeparam name="T">The Type of the Modifier.</typeparam>
/// <returns>True if the Modifier is present, false otherwise.</returns>
public bool HasModifier<T>()
{
return ActiveModifiers.Exists(x => x is T) || _toAdd.Exists(x => x is T);
}
}
+63 -47
View File
@@ -19,6 +19,7 @@ public static class ModifierManager
{
private static readonly Dictionary<uint, Type> IdToTypeModifierMap = [];
private static readonly Dictionary<Type, uint> TypeToIdModifierMap = [];
private static readonly Dictionary<int, List<uint>> PrioritiesToIdsMap = [];
private static uint _nextId;
@@ -57,75 +58,90 @@ public static class ModifierManager
IdToTypeModifierMap.Add(GetNextId(), modifierType);
TypeToIdModifierMap.Add(modifierType, _nextId);
if (!typeof(GameModifier).IsAssignableFrom(modifierType))
{
return;
}
var mod = Activator.CreateInstance(modifierType) as GameModifier;
var priority = mod!.Priority();
if (!PrioritiesToIdsMap.TryGetValue(priority, out var list))
{
PrioritiesToIdsMap[priority] = list = [];
}
list.Add(_nextId);
}
internal static void AssignModifiers(List<PlayerControl> plrs)
{
var rand = new Random();
List<uint> filteredModifiers = [];
foreach (var modifier in IdToTypeModifierMap.Where(pair => pair.Value.IsAssignableTo(typeof(GameModifier))))
foreach (var priority in PrioritiesToIdsMap.Keys.OrderByDescending(x => x))
{
if (Activator.CreateInstance(modifier.Value) is not GameModifier mod)
var filteredModifiers = new List<uint>();
foreach (var id in PrioritiesToIdsMap[priority])
{
Logger<MiraApiPlugin>.Error($"Failed to create instance of {modifier.Value.Name}");
continue;
}
var mod = Activator.CreateInstance(IdToTypeModifierMap[id]) as GameModifier;
var chance = Math.Clamp(mod!.GetAssignmentChance(), 0, 100);
var count = mod.GetAmountPerGame();
if (!plrs.Exists(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);
if (randomNum < Math.Clamp(chance, 0, 100))
if (chance == 0 || count == 0)
{
filteredModifiers.Add(TypeToIdModifierMap[modifier.Value]);
continue;
}
var maxCount = plrs.Count(x => IsGameModifierValid(x, mod, id));
if (maxCount == 0)
{
Logger<MiraApiPlugin>.Warning($"No players are valid for {mod.ModifierName}");
continue;
}
var num = Math.Clamp(count, 0, maxCount);
for (var i = 0; i < num; i++)
{
var randomNum = rand.Next(100);
if (randomNum < chance)
{
filteredModifiers.Add(id);
}
}
}
}
var shuffledModifiers = filteredModifiers.Randomize();
if (shuffledModifiers.Count > plrs.Count)
{
shuffledModifiers = shuffledModifiers.GetRange(0, plrs.Count);
}
while (shuffledModifiers.Count > 0)
{
var id = shuffledModifiers[0];
if (Activator.CreateInstance(IdToTypeModifierMap[id]) is not GameModifier mod)
if (filteredModifiers.Count == 0)
{
Logger<MiraApiPlugin>.Error($"Failed to create instance of {IdToTypeModifierMap[id].Name}");
Logger<MiraApiPlugin>.Warning($"No filtered modifiers for priority {priority}");
continue;
}
if (!plrs.Exists(x => IsGameModifierValid(x, mod, id)))
var shuffledList = filteredModifiers.Randomize();
if (shuffledList.Count > plrs.Count)
{
shuffledModifiers.RemoveAt(0);
continue;
shuffledList = shuffledList.GetRange(0, plrs.Count);
}
var plr = plrs.Random();
if (plr == null || !IsGameModifierValid(plr, mod, id))
foreach (var id in shuffledList)
{
continue;
}
var mod = Activator.CreateInstance(IdToTypeModifierMap[id]) as GameModifier;
var plr = plrs.Where(x => IsGameModifierValid(x, mod!, id)).Random();
shuffledModifiers.RemoveAt(0);
plr.RpcAddModifier(id);
if (plr == null)
{
Logger<MiraApiPlugin>.Warning($"Somehow valid players for modifier {mod!.ModifierName} disappeared");
}
else
{
plr.RpcAddModifier(id);
}
}
}
}
+7
View File
@@ -17,6 +17,12 @@ public abstract class GameModifier : BaseModifier
/// <returns>An int value greater than or equal to zero.</returns>
public abstract int GetAmountPerGame();
/// <summary>
/// Gets the priority at which the modifier will spawn. The higher the value, the higher up on the assignment list.
/// </summary>
/// <returns>An int value greater than or equal to -1.</returns>
public virtual int Priority() => -1;
/// <summary>
/// Determines whether the modifier is valid on a role.
/// </summary>
@@ -27,6 +33,7 @@ public abstract class GameModifier : BaseModifier
/// <summary>
/// Determines whether the player won the game with this modifier.
/// </summary>
/// <param name="reason">The reason why the game ended.</param>
/// <returns>True if the player won, false if they lost. Return null to use the player's default win condition.</returns>
public virtual bool? DidWin(GameOverReason reason) => null;
}
+2 -2
View File
@@ -194,7 +194,7 @@ public static class Extensions
public static bool HasModifier<T>(this PlayerControl? player) where T : BaseModifier
{
return player?.GetModifierComponent() != null &&
player.GetModifierComponent()!.ActiveModifiers.Exists(x => x is T);
player.GetModifierComponent()!.HasModifier<T>();
}
/// <summary>
@@ -206,7 +206,7 @@ public static class Extensions
public static bool HasModifier(this PlayerControl? player, uint id)
{
return player?.GetModifierComponent() != null &&
player.GetModifierComponent()!.ActiveModifiers.Exists(x => x.ModifierId == id);
player.GetModifierComponent()!.HasModifier(id);
}
/// <summary>