Skip to content

Modifiers

XtraCube edited this page Feb 27, 2026 · 1 revision

Modifiers are gameplay effects that can be applied to players on top of their role. They are distinct from roles: a player keeps their role and receives one or more modifiers on top of it. Mira handles registration, assignment, networking, and UI display.

There are three modifier base classes depending on the behavior you need:

Class Use case
BaseModifier Any modifier applied manually at runtime.
GameModifier A modifier the game automatically assigns at round start based on chance and count.
TimedModifier A modifier that has a countdown timer and optionally removes itself when it expires.

Creating a Modifier

Create a class that inherits from BaseModifier, GameModifier, or TimedModifier. Mira automatically discovers and registers any modifier class found in your plugin assembly.

public class CaptainModifier : GameModifier
{
    public override string ModifierName => "Captain";
    public override LoadableAsset<Sprite>? ModifierIcon => ExampleAssets.CaptainIcon;

    public override string GetDescription() => "You can call a meeting from anywhere on the map.";

    public override int GetAssignmentChance() => 50;
    public override int GetAmountPerGame() => 1;

    public override void OnDeath(DeathReason reason)
    {
        Player.RemoveModifier(this);
    }
}

BaseModifier Properties and Methods

Member Type Default Description
ModifierName string (abstract) The display name of the modifier.
ModifierIcon LoadableAsset<Sprite>? null Icon shown on the HUD. Has no effect if HideOnUi is true.
HideOnUi bool true when GetDescription() is empty Whether the modifier is hidden on the HUD.
Unique bool true If true, only one instance of this modifier can be on a player at a time.
ShowInFreeplay bool false Whether this modifier appears in the freeplay modifier selection menu.
FreeplayFileColor Color Color.gray Color of the entry in the freeplay menu.
Player PlayerControl Set by Mira The player this modifier is attached to.
UniqueId Guid Set by Mira A unique instance ID for this specific modifier.
TypeId uint Set by Mira The registered type ID shared by all instances of this modifier class.
ParentMod MiraPluginInfo Resolved automatically The plugin that registered this modifier.
GetDescription() string "" Description shown on the HUD. Must be non-empty for the modifier to be visible on the UI.
OnActivate() void Called when the modifier is first added to the player.
OnDeactivate() void Called when the modifier is removed from the player.
Update() void Called each frame while the modifier is active.
FixedUpdate() void Called each physics tick while the modifier is active.
OnDeath(DeathReason) void Called when the player dies.
OnMeetingStart() void Called when a meeting starts.
CanVent() bool? null Override vent permission. null means no effect.

GameModifier — Automatically Assigned Modifiers

GameModifier extends BaseModifier for modifiers that should be assigned by the game at round start. ShowInFreeplay defaults to true.

Member Type Default Description
GetAssignmentChance() int (abstract) Chance (0–100) of this modifier being assigned per eligible player.
GetAmountPerGame() int (abstract) Maximum number of players that can have this modifier in one game.
Priority() int -1 Assignment priority. Higher values are assigned first.
IsModifierValidOn(RoleBehaviour) bool true Whether this modifier can be assigned to a player with the given role.
DidWin(GameOverReason) bool? null Custom win condition. true = won, false = lost, null = use player's default condition. The highest-priority modifier's result takes precedence.
CanSpawnOnCurrentMode() bool true (false in Hide and Seek) Whether this modifier can spawn given the current game mode.

Assignment Configuration

When you use the default GetAssignmentChance() and GetAmountPerGame() values, Mira automatically creates options in the Modifiers settings screen so the host can configure them. If you want to read those values from an options group instead (as is recommended), return the values from your options group directly:

public override int GetAssignmentChance() =>
    (int)OptionGroupSingleton<CaptainModifierSettings>.Instance.Chance;

public override int GetAmountPerGame() =>
    (int)OptionGroupSingleton<CaptainModifierSettings>.Instance.Amount;

TimedModifier — Duration-Based Modifiers

TimedModifier extends BaseModifier for modifiers that count down and optionally remove themselves when the timer completes.

Member Type Default Description
Duration float (abstract) How long the modifier lasts in seconds.
AutoStart bool true Whether the timer starts automatically when the modifier is added.
RemoveOnComplete bool true Whether the modifier removes itself when the timer reaches zero.
TimerActive bool Whether the timer is currently running.
TimeRemaining float Seconds remaining on the timer.
HideOnUi bool !TimerActive Hides the HUD entry while the timer is not running.
GetDescription() string "Remaining Time: Xs/Ys" Automatically shows remaining/total time.
StartTimer() void Starts the countdown.
StopTimer() void Stops the timer and calls OnTimerComplete(). Removes the modifier if RemoveOnComplete is true.
ResetTimer() void Resets TimeRemaining to Duration without calling OnTimerComplete().
OnTimerComplete() void Called when the timer reaches zero.
public class FreezeModifier : TimedModifier
{
    public override string ModifierName => "Frozen";
    public override float Duration => 5f;

    public override string GetDescription() => $"You are frozen! {Math.Round(TimeRemaining, 0)}s remaining.";

    public override void OnActivate()
    {
        Player.MyPhysics.body.velocity = Vector2.zero;
    }
}

Adding and Removing Modifiers

Modifiers are applied and removed via RPC so all clients stay in sync. All methods are extensions on PlayerControl.

Networked (RPC) Methods

Method Description
player.RpcAddModifier<T>(params object[] args) Adds modifier of type T to the player, synced over RPC.
player.RpcRemoveModifier<T>(Func<BaseModifier, bool>? predicate) Removes a modifier of type T from the player, synced over RPC.
player.RpcRemoveModifier(Guid uniqueId) Removes a specific modifier instance by its unique ID, synced over RPC.
player.RpcRemoveModifier(Type type, Func<BaseModifier, bool>? predicate) Removes a modifier by type, synced over RPC.
player.RpcRemoveModifier(uint typeId, Func<BaseModifier, bool>? predicate) Removes a modifier by type ID, synced over RPC.

Local-Only Methods

These modify the local ModifierComponent directly, without sending an RPC. Use these only when you already know the call is running on all clients, or for local-only state.

Method Description
player.AddModifier<T>(params object[] args) Adds a modifier of type T locally.
player.AddModifier(Type type, params object[] args) Adds a modifier by type locally.
player.AddModifier(uint typeId, params object[] args) Adds a modifier by type ID locally.
player.RemoveModifier<T>(Func<T, bool>? predicate) Removes a modifier of type T locally.
player.RemoveModifier(Type type, Func<BaseModifier, bool>? predicate) Removes a modifier by type locally.
player.RemoveModifier(uint typeId, Func<BaseModifier, bool>? predicate) Removes a modifier by type ID locally.
player.RemoveModifier(Guid uniqueId) Removes a modifier by its unique instance ID locally.
player.ClearModifiers() Removes all modifiers from the player locally.

Querying Modifiers

These extension methods on PlayerControl let you check and retrieve a player's modifiers.

Method Description
player.HasModifier<T>(Func<T, bool>? predicate) Returns true if the player has a modifier of type T.
player.HasModifier(Type type, Func<BaseModifier, bool>? predicate) Returns true if the player has a modifier of the given type.
player.HasModifier(uint typeId, Func<BaseModifier, bool>? predicate) Returns true if the player has a modifier with the given type ID.
player.HasModifier(Guid uniqueId) Returns true if the player has the specific modifier instance.
player.GetModifier<T>(Func<T, bool>? predicate) Returns the first matching modifier of type T, or null.
player.GetModifiers<T>(Func<T, bool>? predicate) Returns all modifiers of type T on the player.
player.GetModifiers(Type type, Func<BaseModifier, bool>? predicate) Returns all modifiers of the given type on the player.
player.GetModifiers(uint typeId, Func<BaseModifier, bool>? predicate) Returns all modifiers with the given type ID on the player.
player.GetModifierComponent() Returns the ModifierComponent attached to the player.

Global Modifier Utilities

ModifierUtils provides static helpers for querying modifiers across all players.

Method Description
ModifierUtils.GetActiveModifiers<T>(Func<T, bool>? predicate) Returns all active modifiers of type T across every player.
ModifierUtils.GetPlayersWithModifier<T>(Func<T, bool>? predicate) Returns all players that currently have a modifier of type T.
// Get all players who are currently frozen
var frozenPlayers = ModifierUtils.GetPlayersWithModifier<FreezeModifier>();

// Get all active freeze modifier instances
var activeFreeze = ModifierUtils.GetActiveModifiers<FreezeModifier>();

ModifierManager

ModifierManager exposes the registered modifier registry.

Member Description
ModifierManager.Modifiers Read-only list of all registered modifier instances (one per type).
ModifierManager.GetModifierType(uint id) Returns the Type for a given modifier type ID.
ModifierManager.GetModifierTypeId(Type type) Returns the type ID (uint?) for a given modifier type.
ModifierManager.MiraAssignsModifiers When set to false, Mira will skip automatic modifier assignment at round start.

Clone this wiki locally