OnAIBrainStateSwitched
OnAIBrainStateSwitched is called after an NPC's AI brain switches to a new state, right after the new state's StateEnter has run. It fires from BaseAIBrain.SwitchToState on the server.
void OnAIBrainStateSwitched(BaseAIBrain aIBrain, BaseAIBrain.BasicAIState CurrentState)Parameters
| Parameter | Type | Description |
|---|---|---|
aIBrain | BaseAIBrain | The NPC's BaseAIBrain component that just changed state. Use GetBaseEntity() to reach the owning BaseEntity (usually an NPCPlayer such as a ScientistNPC or an animal). |
CurrentState | BaseAIBrain.BasicAIState | The BaseAIBrain.BasicAIState instance the brain has already entered. Compare it to the brain's state fields or inspect its type to tell what the NPC is now doing. |
Return value
The return value is ignored. The game already set CurrentState, ran the new state's enter logic and registered events before the hook is invoked, and SwitchToState returns true regardless of what your plugin returns.
How it works
This hook fires at the tail of BaseAIBrain.SwitchToState, after CurrentState has been assigned, the new state's StateEnter has executed, and the event container has been registered. Every brain state transition on a server-side NPC goes through this method, so the hook covers routine behaviour changes such as an NPC going from idle to chase, attacking, returning home or eating, as well as scripted state changes triggered by npc spawn managers or other plugins.
The first argument is the brain itself; aIBrain.GetBaseEntity() gives you the NPC entity, which you can null-check and cast to ScientistNPC, BaseAnimalNPC or whatever type your server uses. The second argument is the state instance, already entered. You can compare it against the brain's public state fields to see the previous target of the switch, or check CurrentState.GetType().Name when you only need a coarse label.
Because the hook receives BaseAIBrain rather than a player type, you cannot narrow it to a specific NPC class through parameter typing. Filter inside the body instead. The hook fires frequently during combat, so keep the body cheap and do not print or log on every call.
Example plugin: Announce when a scientist starts chasing a player
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using UnityEngine;
using Oxide.Core;
namespace Oxide.Plugins
{
[Info("Npc State Announcer", "RustPluginGenerator", "1.0.0")]
[Description("Broadcasts a warning when a scientist brain enters a chase state")]
public class NpcStateAnnouncer : RustPlugin
{
private const string PermBypass = "npcstateannouncer.bypass";
private PluginConfig config;
private readonly Dictionary<ulong, float> lastAnnounced = new Dictionary<ulong, float>();
private class PluginConfig
{
[JsonProperty("Announce chase states in chat")]
public bool AnnounceChase { get; set; } = true;
[JsonProperty("Seconds between announcements per NPC")]
public float CooldownSeconds { get; set; } = 20f;
}
protected override void LoadDefaultConfig() => config = new PluginConfig();
protected override void LoadConfig()
{
base.LoadConfig();
try
{
config = Config.ReadObject<PluginConfig>();
if (config == null) throw new Exception();
}
catch
{
PrintWarning("Configuration file is invalid, using defaults");
LoadDefaultConfig();
}
SaveConfig();
}
protected override void SaveConfig() => Config.WriteObject(config);
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["ChaseWarning"] = "{0} scientists are hunting players: {1}"
}, this);
}
private string Msg(string key, string userId = null, params object[] args)
=> string.Format(lang.GetMessage(key, this, userId), args);
private void Init()
{
permission.RegisterPermission(PermBypass, this);
}
private void Unload()
{
lastAnnounced.Clear();
}
private void OnAIBrainStateSwitched(BaseAIBrain aIBrain, BaseAIBrain.BasicAIState CurrentState)
{
if (!config.AnnounceChase || aIBrain == null || CurrentState == null) return;
var npc = aIBrain.GetBaseEntity() as ScientistNPC;
if (npc == null || npc.IsDestroyed) return;
// Only react to the chase state, not every transition.
// Detect the chase state by its type name to stay resilient to assembly changes.
string stateName = CurrentState.GetType().Name;
if (stateName != "ChaseState") return;
ulong npcId = npc.net != null ? npc.net.ID.Value : 0;
if (npcId == 0) return;
float now = Time.realtimeSinceStartup;
float last;
if (lastAnnounced.TryGetValue(npcId, out last) && now - last < config.CooldownSeconds) return;
lastAnnounced[npcId] = now;
BasePlayer target = null;
var targetProp = CurrentState.GetType().GetProperty("currentTarget");
if (targetProp != null)
{
target = targetProp.GetValue(CurrentState, null) as BasePlayer;
}
string targetName = target != null && target.IsConnected ? target.displayName : "someone";
string message = Msg("ChaseWarning", null, 1, targetName);
foreach (var player in BasePlayer.activePlayerList)
{
if (player == null || !player.IsConnected) continue;
if (permission.UserHasPermission(player.UserIDString, PermBypass)) continue;
player.ChatMessage(message);
}
}
}
}The plugin hooks every brain state switch and only reacts to ScientistNPC entities, which it reaches through aIBrain.GetBaseEntity(). When the new state is the chase state, it tries to read the brain's current target player from the state; if a connected player is found it uses their display name, otherwise it uses someone. The message is then sent to every connected player who lacks the npcstateannouncer.bypass permission. A per-NPC cooldown based on Time.realtimeSinceStartup, configured by CooldownSeconds, keeps a scientist that repeatedly re-enters the chase state from spamming the server. The AnnounceChase config option turns the feature off entirely.
Common use cases
Raid warnings
Tell online players when a scientist or other NPC brain enters a chase state near them so they know they are being hunted.
NPC behaviour monitoring
Count how often specific NPC types switch into attack or return-home states to tune spawn density and loot balance on custom PvE servers.
Scripted NPC reactions
Trigger effects, sounds or extra entities when a custom NPC switches state, for example a supply drop marker when a boss enters its enrage state.
Pitfalls
State is already entered
The hook fires after
StateEnterran and after the brain's current state was replaced. You cannot use it to block or delay the transition; react to it instead.Fires very often in combat
Chasing NPCs can cycle through states many times per second. Never call
Puts,PrintWarningor chat broadcasts unconditionally in the hook body.Entity may be missing
aIBrain.GetBaseEntity()can return null if the NPC entity was destroyed or is being torn down. Null-check the brain and entity before touching position, health or target fields.
Build a plugin with OnAIBrainStateSwitched
Describe what should happen and get a compiled plugin that uses this hook.
Edit the prompt on the next screen. 2 free plugins a month, no card.
Last updated Oct 26, 2026. Found a mistake? Email support@rustplugingenerator.com.
Build a plugin with OnAIBrainStateSwitched
Generate