Table of Contents

Class AIController

Namespace
FishMMO.Shared
Assembly
FishMMO.Shared.dll

Controls AI navigation, state transitions, and behavior for NPCs using NavMeshAgent. Handles movement, enemy detection, leash logic, waypoints, state management, and provides a virtual camera for aiming abilities at targets during combat.

[RequireComponent(typeof(NavMeshAgent))]
public class AIController : CharacterBehaviour, IAIController, IAINavigation, IAIStateMachine, IAIWaypoints, ICharacterBehaviour
Inheritance
Object
Component
Behaviour
MonoBehaviour
NetworkBehaviour
AIController
Implements
Inherited Members
NetworkBehaviour.IsSpawned
NetworkBehaviour.ComponentIndex
NetworkBehaviour.NetworkObject
NetworkBehaviour.MAXIMUM_NETWORKBEHAVIOURS
NetworkBehaviour.UNSET_NETWORKBEHAVIOUR_ID
NetworkBehaviour.ToString()
NetworkBehaviour.Reset()
NetworkBehaviour.OnValidate()
NetworkBehaviour.IsBehaviourReconciling
NetworkBehaviour.ClearReplicateCache()
NetworkBehaviour.CreateReconcile()
NetworkBehaviour.Reconcile_Reader<T>(PooledReader, ref T)
NetworkBehaviour.OnStartServerCalled
NetworkBehaviour.OnStartClientCalled
NetworkBehaviour.WritePayload(NetworkConnection, Writer)
NetworkBehaviour.ReadPayload(NetworkConnection, Reader)
NetworkBehaviour.OnStartServer()
NetworkBehaviour.OnStopServer()
NetworkBehaviour.OnOwnershipServer(NetworkConnection)
NetworkBehaviour.OnSpawnServer(NetworkConnection)
NetworkBehaviour.OnDespawnServer(NetworkConnection)
NetworkBehaviour.OnStartClient()
NetworkBehaviour.OnStopClient()
NetworkBehaviour.OnOwnershipClient(NetworkConnection)
NetworkBehaviour.ClearBuffedRpcs()
NetworkBehaviour.ExcludeOwnerFromUnbufferedObserversRpcs
NetworkBehaviour.IsClientOnly
NetworkBehaviour.IsServerOnly
NetworkBehaviour.IsHost
NetworkBehaviour.IsClient
NetworkBehaviour.IsServer
NetworkBehaviour.IsDeinitializing
NetworkBehaviour.NetworkManager
NetworkBehaviour.ServerManager
NetworkBehaviour.ClientManager
NetworkBehaviour.ObserverManager
NetworkBehaviour.TransportManager
NetworkBehaviour.TimeManager
NetworkBehaviour.SceneManager
NetworkBehaviour.PredictionManager
NetworkBehaviour.RollbackManager
NetworkBehaviour.NetworkObserver
NetworkBehaviour.IsClientInitialized
NetworkBehaviour.IsClientStarted
NetworkBehaviour.IsClientOnlyInitialized
NetworkBehaviour.IsClientOnlyStarted
NetworkBehaviour.IsServerInitialized
NetworkBehaviour.IsServerStarted
NetworkBehaviour.IsServerOnlyInitialized
NetworkBehaviour.IsServerOnlyStarted
NetworkBehaviour.IsHostInitialized
NetworkBehaviour.IsHostStarted
NetworkBehaviour.IsOffline
NetworkBehaviour.IsNetworked
NetworkBehaviour.GetIsNetworked()
NetworkBehaviour.IsManagerReconciling
NetworkBehaviour.Observers
NetworkBehaviour.IsOwner
NetworkBehaviour.IsController
NetworkBehaviour.HasAuthority
NetworkBehaviour.Owner
NetworkBehaviour.OwnerId
NetworkBehaviour.ObjectId
NetworkBehaviour.LocalConnection
NetworkBehaviour.OwnerMatches(NetworkConnection)
NetworkBehaviour.Despawn(GameObject, DespawnType?)
NetworkBehaviour.Despawn(NetworkObject, DespawnType?)
NetworkBehaviour.Despawn(DespawnType?)
NetworkBehaviour.Spawn(GameObject, NetworkConnection, Scene)
NetworkBehaviour.Spawn(NetworkObject, NetworkConnection, Scene)
NetworkBehaviour.RemoveOwnership()
NetworkBehaviour.GiveOwnership(NetworkConnection)
NetworkBehaviour.GetInstance<T>()
NetworkBehaviour.TryRegisterInstance<T>(T)
NetworkBehaviour.UnregisterInstance<T>()
NetworkBehaviour.CanLog(LoggingType)
MonoBehaviour.IsInvoking()
MonoBehaviour.CancelInvoke()
MonoBehaviour.StopCoroutine(Coroutine)
MonoBehaviour.StopAllCoroutines()
MonoBehaviour.destroyCancellationToken
MonoBehaviour.useGUILayout
MonoBehaviour.didStart
MonoBehaviour.didAwake
MonoBehaviour.runInEditMode
Behaviour.enabled
Behaviour.isActiveAndEnabled
Component.GetComponent<T>()
Component.TryGetComponent<T>(out T)
Component.GetComponentInChildren<T>()
Component.GetComponentsInChildren<T>()
Component.GetComponentInParent<T>()
Component.GetComponentsInParent<T>()
Component.GetComponents<T>()
Component.GetComponentIndex()
Component.CompareTag(TagHandle)
Component.transform
Component.transformHandle
Component.gameObject
Component.tag
Object.GetEntityId()
Object.GetInstanceID()
Object.GetHashCode()
Object.InstantiateAsync<T>(T)
Object.InstantiateAsync<T>(T, Transform)
Object.InstantiateAsync<T>(T, Vector3, Quaternion)
Object.InstantiateAsync<T>(T, Transform, Vector3, Quaternion)
Object.Instantiate(Object, Vector3, Quaternion)
Object.Instantiate(Object, Vector3, Quaternion, Transform)
Object.Instantiate(Object)
Object.Instantiate(Object, Scene)
Object.Instantiate<T>(T, InstantiateParameters)
Object.Instantiate<T>(T, Vector3, Quaternion, InstantiateParameters)
Object.Instantiate(Object, Transform)
Object.Instantiate<T>(T)
Object.Instantiate<T>(T, Vector3, Quaternion)
Object.Instantiate<T>(T, Vector3, Quaternion, Transform)
Object.Instantiate<T>(T, Transform)
Object.Destroy(Object)
Object.DestroyImmediate(Object)
Object.DontDestroyOnLoad(Object)
Object.DestroyObject(Object)
Object.FindObjectsOfType<T>()
Object.FindObjectsByType<T>(FindObjectsSortMode)
Object.FindObjectsByType<T>(FindObjectsInactive, FindObjectsSortMode)
Object.FindObjectOfType<T>()
Object.FindFirstObjectByType<T>()
Object.FindAnyObjectByType<T>()
Object.FindFirstObjectByType<T>(FindObjectsInactive)
Object.FindAnyObjectByType<T>(FindObjectsInactive)
Object.FindObjectsByType<T>()
Object.FindObjectsByType<T>(FindObjectsInactive)
Object.name
Object.hideFlags

Remarks

Every AI state previously drove the NavMeshAgent with the same three-line pattern: NavMesh.SamplePosition, SetDestination, and then !pathPending && remainingDistance < 1 to decide it had arrived. All three steps have failure modes that the pattern silently swallows:

  • SamplePosition returns false when the sampled point has no NavMesh near it, and the caller then simply did not move — with no retry and no fallback.
  • An unreachable destination does not fail. Unity returns a partial path to the closest reachable point, so the agent walks to the near side of the obstacle and stops.
  • remainingDistance is 0 when there is no path at all, and pathPending is false at the same moment — so "no path" and "arrived" are indistinguishable. A patrol whose first waypoint failed to sample cycled through every waypoint once per tick without walking anywhere.

These helpers collapse that pattern into calls that report what actually happened.

Fields

ARRIVAL_TOLERANCE

Distance from the destination at which an NPC counts as arrived, in world units.

public const float ARRIVAL_TOLERANCE = 1

Field Value

float

AbilityRotation

[Header("Ability Rotation")]
[Tooltip("Optional ability rotation for condition/sequence-based ability selection.")]
public AIAbilityRotation AbilityRotation

Field Value

AIAbilityRotation

AggressionDamageWeight

[Header("Aggression / Threat")]
[Tooltip("Aggression points per 1 damage taken.")]
public float AggressionDamageWeight

Field Value

float

AggressionDecayRate

Points per second that each entry decays when no new events occur.

[Tooltip("Aggression decay per second.")]
public float AggressionDecayRate

Field Value

float

AggressionHealingWeight

Points awarded per 1 point of healing an enemy of the NPC witnesses.

[Tooltip("Aggression points per 1 healing witnessed on a combat participant.")]
public float AggressionHealingWeight

Field Value

float

AggressionHitBonus

Flat points added per hit, regardless of damage amount.

[Tooltip("Flat aggression per hit.")]
public float AggressionHitBonus

Field Value

float

AggressionStaleTimeout

Seconds after last event before an entry is removed entirely.

[Tooltip("Seconds before a stale aggression entry is pruned.")]
public float AggressionStaleTimeout

Field Value

float

AggressionVarietyChance

Chance (0-1) that target selection ignores the top-threat target and picks a secondary one.

[Range(0, 1)]
[Tooltip("Chance to pick a non-top-threat target for variety.")]
public float AggressionVarietyChance

Field Value

float

AiTickRate

How many times per second this NPC's brain runs, in hertz.

[Header("Tick Rate")]
[Tooltip("Brain updates per second. 5-10 is the useful band. Rounded to a divisor of the network tick rate.")]
[Range(1, 30)]
public float AiTickRate

Field Value

float

Remarks

Rounded to the nearest whole divisor of the FishNet tick rate, so the brain always lands on network ticks and never drifts against them. At the project's 30 Hz network tick, 8 Hz resolves to every 4th tick — 7.5 Hz exactly, forever, on any hardware. EffectiveAiTickRate reports what a requested rate actually resolved to.

5-10 Hz is the useful band for an MMO brain. Decisions below about 5 Hz start to read as sluggish reaction time to a player; above about 10 Hz the NPC is re-deciding faster than its own pathing and animation can respond, so the extra ticks buy nothing but CPU.

AllyScanTimer

Seconds until a healer archetype rescans for wounded allies.

[NonSerialized]
public float AllyScanTimer

Field Value

float

Archetype

[Header("Archetype")]
[Tooltip("Optional archetype asset that fills in the state and tuning slots below.")]
public AIArchetypeTemplate Archetype

Field Value

AIArchetypeTemplate

AttackCooldownTimer

Seconds remaining before this NPC may activate another ability.

[NonSerialized]
public float AttackCooldownTimer

Field Value

float

Remarks

Lives on the controller, not on the attacking state, because the state is a ScriptableObject shared by every NPC of that archetype — a timer stored there would be one global pacing clock for the whole population.

AttackingState

Reference to the attacking state for combat behavior.

public BaseAIState AttackingState

Field Value

BaseAIState

AvoidancePriority

The avoidance priority for this agent (affects how strongly it avoids other agents).

public AgentAvoidancePriority AvoidancePriority

Field Value

AgentAvoidancePriority

BehaviorTree

[Header("Behavior Tree")]
[Tooltip("Optional behavior tree for high-level decision making.")]
public AIBehaviorTree BehaviorTree

Field Value

AIBehaviorTree

BossScript

[Header("Boss Script")]
[Tooltip("Optional boss script for phased encounters.")]
public BossScript BossScript

Field Value

BossScript

CachedHealTarget

The ally a healer archetype last chose to heal, re-validated cheaply between scans.

[NonSerialized]
public ICharacter CachedHealTarget

Field Value

ICharacter

DeadState

Reference to the dead state for death logic.

public BaseAIState DeadState

Field Value

BaseAIState

EnemySweepRate

How often (in seconds) to sweep for nearby enemies.

public float EnemySweepRate

Field Value

float

FlankTimer

Seconds of manoeuvring budget remaining for RogueAttackingState's flanking attempt. Per-NPC for the same reason as AttackCooldownTimer.

[NonSerialized]
public float FlankTimer

Field Value

float

Group

The NPC group this controller belongs to. Set by NPCGroup.

[NonSerialized]
public NPCGroup Group

Field Value

NPCGroup

GroupRole

This NPC's role within its group. Set by NPCGroup.

[NonSerialized]
public NPCGroupRole GroupRole

Field Value

NPCGroupRole

IdleState

Reference to the idle state for passive behavior.

public BaseAIState IdleState

Field Value

BaseAIState

InitialState

[Header("States")]
public BaseAIState InitialState

Field Value

BaseAIState

LodSettings

[Header("AI LOD")]
[Tooltip("Optional LOD settings for distance-based AI throttling.")]
public AILodSettings LodSettings

Field Value

AILodSettings

LookTarget

The current look target for the AI (used for facing/rotation).

public Transform LookTarget

Field Value

Transform

OrbitAngle

Per-NPC orbit angle (radians) used by OrbitState. Stored here instead of on the ScriptableObject to avoid the shared-instance mutable state problem.

[NonSerialized]
public float OrbitAngle

Field Value

float

PatrolState

Reference to the patrol state for waypoint movement.

public BaseAIState PatrolState

Field Value

BaseAIState

Personality

[Header("Combat Personality")]
[Tooltip("Optional combat personality for data-driven ability preference.")]
public AICombatPersonality Personality

Field Value

AICombatPersonality

PetStuckTimer

Seconds a pet has spent unable to reach its owner. Drives the follow state's teleport escape hatch. Per-NPC, for the same reason as the other timers here.

[NonSerialized]
public float PetStuckTimer

Field Value

float

RandomizeState

If true, the AI will randomize its movement state.

public bool RandomizeState

Field Value

bool

RepathInterval

[Header("Pathfinding")]
[Tooltip("Minimum seconds between NavMeshAgent.SetDestination calls via SetThrottledDestination.")]
public float RepathInterval

Field Value

float

RetreatState

Reference to the retreat state for fleeing behavior.

public BaseAIState RetreatState

Field Value

BaseAIState

ReturnHomeState

Reference to the return home state for leash logic.

public BaseAIState ReturnHomeState

Field Value

BaseAIState

RotationIndex

Per-NPC rotation index used by AIAbilityRotation in Sequence mode. Tracks which entry in the rotation to try next.

[NonSerialized]
public int RotationIndex

Field Value

int

StuckTimeout

Seconds of no progress before the NPC is considered stuck.

[Header("Navigation Recovery")]
[Tooltip("Seconds of no movement, while trying to move, before the NPC is considered stuck.")]
public float StuckTimeout

Field Value

float

StuckWarpTimeout

Seconds of continued no progress after the first recovery attempt before the NPC is teleported to its destination. 0 disables teleport recovery.

[Tooltip("Seconds stuck before the NPC is warped free. 0 = never warp.")]
public float StuckWarpTimeout

Field Value

float

SubStateTimer

Countdown used by bounded combat sub-states such as OrbitState to know when their manoeuvre is finished. Per-NPC, for the same reason as the other timers here.

[NonSerialized]
public float SubStateTimer

Field Value

float

SweepHits

Buffer for storing colliders hit during enemy sweep. Grown on demand — see SweepForEnemies(AIController, List<ICharacter>).

public Collider[] SweepHits

Field Value

Collider[]

Remarks

Not a fixed 20. A non-allocating overlap returns at most buffer.Length results and says nothing about how many it discarded, and the ones it discarded were chosen by the physics broadphase — so an NPC in a fight larger than its buffer detected an arbitrary, run-varying subset of its attackers and ignored the rest. The sweep re-queries into a larger buffer through TargetOrdering.TryGrowQueryBuffer until it stops coming back full, which is the same treatment every other spatial query in the project gets.

TurnRate

How quickly the NPC turns to face its look target, in radians-ish per second.

[Header("Facing")]
[Tooltip("How quickly the NPC turns to face its target. Higher is snappier.")]
public float TurnRate

Field Value

float

Remarks

Feeds an exponential smoothing factor, so the value is a rate rather than a hard angular speed: higher snaps faster, and the result is identical at any frame rate.

UnreachableTargetTimer

Seconds the NPC has spent unable to reach its combat target.

[NonSerialized]
public float UnreachableTargetTimer

Field Value

float

WanderState

Reference to the wander state for random movement.

public BaseAIState WanderState

Field Value

BaseAIState

WasAttackingLastTick

True when the previous combat tick resolved to attacking or holding position rather than moving. Feeds the range hysteresis in AICombatDecision.

[NonSerialized]
public bool WasAttackingLastTick

Field Value

bool

Waypoints

The waypoints available to this AI controller.

public Vector3[] Waypoints

Field Value

Vector3[]

Properties

Agent

The NavMeshAgent component used for navigation.

public NavMeshAgent Agent { get; }

Property Value

NavMeshAgent

Aggression

Convenience accessor for the underlying aggression controller.

public AggressionController Aggression { get; }

Property Value

AggressionController

AggressionState

The aggression (threat) state for this NPC. Manages the threat table, event subscriptions, and target re-evaluation timer. One instance per NPC — not shared.

public AggressionState AggressionState { get; }

Property Value

AggressionState

AiTickIndex

Monotonic count of brain updates. Drives the LOD stagger.

public uint AiTickIndex { get; }

Property Value

uint

BossState

Runtime state for the boss script. Null when no BossScript is assigned.

public BossScriptState BossState { get; }

Property Value

BossScriptState

CombatTargetBuffer

Reusable buffer for collecting targets during combat state updates. Used by attacking states to avoid per-frame GC allocations.

public List<ICharacter> CombatTargetBuffer { get; }

Property Value

List<ICharacter>

CurrentLodTier

Current AI LOD tier. Determines how frequently this NPC's brain ticks.

public AILodTier CurrentLodTier { get; }

Property Value

AILodTier

CurrentState

The current AI state.

public BaseAIState CurrentState { get; }

Property Value

BaseAIState

CurrentWaypointIndex

The current waypoint index.

public int CurrentWaypointIndex { get; }

Property Value

int

EffectiveAiTickRate

The brain rate actually achieved, after rounding to a whole number of network ticks.

public float EffectiveAiTickRate { get; }

Property Value

float

EyeTransform

The transform used for vision checks. Defaults to the character's transform if not set.

public Transform EyeTransform { get; }

Property Value

Transform

Home

The anchor this AI leashes and wanders around.

public Vector3 Home { get; set; }

Property Value

Vector3

Remarks

For a normal NPC this is its spawn point. For a pet it is its owner — a pet's home is a moving target, and every leash check, wander radius and return-home destination in the AI reads this property, so anchoring it to the owner here fixes all of them at once. Previously each site that cared had to remember to overwrite the field with the owner's position, and the ones that forgot dragged the pet back toward wherever it happened to be summoned.

A pet ordered to Stay is the exception: it holds the position it was standing in, which is what the setter stores.

LastAiDeltaTime

Seconds elapsed during the AI tick currently executing.

public float LastAiDeltaTime { get; }

Property Value

float

Remarks

Published so helpers reached from deep inside a state's update — which do not receive deltaTime as a parameter — can still advance per-tick timers on the same clock the state machine runs on, rather than sampling UnityEngine.Time.deltaTime and getting one frame instead of one AI tick.

LastPathWasPartial

True when the last accepted destination request produced only a partial path.

public bool LastPathWasPartial { get; }

Property Value

bool

Remarks

Consumed by GetMovementProgress(float, float): an agent that has run out of a partial path has stopped somewhere it was not asked to go, which is a stuck condition rather than an arrival.

NpcRNG

The seeded RNG from the owning NPC. All AI randomisation should use this instead of DeterministicRNG.Shared so that NPC behaviour is fully deterministic given the same seed. Returns null for non-NPC characters.

public DeterministicRNG NpcRNG { get; }

Property Value

DeterministicRNG

OwningPet

The pet this controller drives, or null when it drives a normal NPC.

public Pet OwningPet { get; }

Property Value

Pet

PendingState

The state that is about to become CurrentState, visible to the outgoing state's Exit(AIController).

public BaseAIState PendingState { get; }

Property Value

BaseAIState

Remarks

Exists so an attacking state can tell "combat is over" from "combat is continuing in a sub-state". Exit(AIController) clears the target and interrupts the cast, which is right when the NPC disengages and catastrophic when it does not: the melee archetype's flanking roll called ChangeState(GetBehindState), Exit wiped the target on the way out, and GetBehindState then found no target and dropped the NPC to idle. Every configured orbit / flank / strafe roll silently ended the fight.

Null outside of a transition.

PhysicsScene

The physics scene associated with this AI controller.

public PhysicsScene PhysicsScene { get; }

Property Value

PhysicsScene

RequestedDestination

The destination most recently requested through TryMoveTo(Vector3, bool, float).

public Vector3 RequestedDestination { get; }

Property Value

Vector3

Target

The current target for the AI (e.g., enemy, destination). Setting this property updates the agent's destination.

public Transform Target { get; set; }

Property Value

Transform

TargetCharacter

The current combat target as an ICharacter, or null.

public ICharacter TargetCharacter { get; }

Property Value

ICharacter

Remarks

Prefer this over calling Target.GetComponent<ICharacter>(). The result is cached against the transform, so it costs a field read rather than an interface component lookup.

TargetReevaluationTimer

Per-NPC timer for mid-combat target re-evaluation. Delegates to TargetReevaluationTimer.

public float TargetReevaluationTimer { get; set; }

Property Value

float

VirtualCameraPosition

Virtual camera position used by the ability system to aim projectiles. Computed from the eye transform, aimed toward the current target's center. Mirrors the role of KCCController.VirtualCameraPosition for player characters.

public Vector3 VirtualCameraPosition { get; }

Property Value

Vector3

VirtualCameraRotation

Virtual camera rotation used by the ability system to aim projectiles. Points from the eye transform toward the current target's center. Mirrors the role of KCCController.VirtualCameraRotation for player characters.

public Quaternion VirtualCameraRotation { get; }

Property Value

Quaternion

Methods

AgentIsUsable()

True when the NavMeshAgent can accept movement commands.

public bool AgentIsUsable()

Returns

bool

True if the agent is enabled and on a NavMesh.

Remarks

Unity rejects isStopped and SetDestination with an error for an agent that is disabled or not on a NavMesh. Spawn paths legitimately touch the brain around the moment an object is activated and placed, so guard rather than log a wall of errors.

ChangeState(BaseAIState, List<ICharacter>)

Changes the AI state, optionally providing targets for attacking states. Handles speed and state transitions.

public void ChangeState(BaseAIState newState, List<ICharacter> targets = null)

Parameters

newState BaseAIState

The new state to transition to.

targets List<ICharacter>

Optional list of targets for attacking states.

ClearPath()

Clears the agent's path without moving it.

public void ClearPath()

FaceLookTarget(float)

Rotates the character to face the current look target smoothly.

public void FaceLookTarget(float deltaTime)

Parameters

deltaTime float

ForceTarget(ICharacter)

Forces this NPC onto a specific character immediately, entering combat if it is not already fighting.

public bool ForceTarget(ICharacter character)

Parameters

character ICharacter

The character to attack. Ignored when null or dead.

Returns

bool

True if the NPC took the new target.

Remarks

The scripted-aggro entry point, used by ApplyTauntAction. Distinct from setting Target directly, which changes who the NPC is fighting without putting it into a state that fights.

GetHealthPercent()

Returns this NPC's health as a fraction (0-1) of its maximum, or 1 when it has no health resource.

public float GetHealthPercent()

Returns

float

GetMovementProgress(float, float)

Classifies how the agent is doing against its destination, and accumulates the stuck timer.

public AIMovementProgress GetMovementProgress(float deltaTime, float tolerance = 1)

Parameters

deltaTime float

Seconds since the previous AI tick.

tolerance float

Arrival tolerance.

Returns

AIMovementProgress

The current progress classification.

Remarks

Call once per AI tick from a state that is trying to move. States that are deliberately standing still must not call it, or a stationary NPC would be reported stuck.

GetSqrDistanceToTarget()

Returns the squared distance from this NPC to its current target. Returns float.MaxValue if there is no target.

public float GetSqrDistanceToTarget()

Returns

float

HasAbilityInRange(float)

Returns true if the NPC has at least one ability with range >= the given distance that is off cooldown and meets activation conditions.

public bool HasAbilityInRange(float minRange)

Parameters

minRange float

Minimum ability range required.

Returns

bool

HasArrived(float)

True when the agent has arrived at its destination.

public bool HasArrived(float tolerance = 1)

Parameters

tolerance float

Distance from the destination that counts as arrived.

Returns

bool

True if arrived.

Remarks

Requires an actual path to exist. Without that check, an agent whose destination never took reports remainingDistance == 0 and pathPending == false — which the naive test reads as "arrived" on the very first tick.

Initialize(Vector3, Vector3[])

Initializes the controller with a home position and waypoints. Sets agent dimensions and initial state.

public void Initialize(Vector3 home, Vector3[] waypoints = null)

Parameters

home Vector3

The home position for the AI.

waypoints Vector3[]

Optional waypoints for patrol.

InitializeOnce()

Initializes the controller and NavMeshAgent. Sets avoidance priority, speed, and movement states.

public override void InitializeOnce()

OnDestroying()

Unsubscribes from global events on destroy to prevent memory leaks.

public override void OnDestroying()

OnStartNetwork()

Called when the network starts. Disables the controller if not running on the server.

public override void OnStartNetwork()

OnStopNetwork()

Releases the tick subscription.

public override void OnStopNetwork()

OnThreatReceived(ICharacter)

Event-driven combat entry. Called by AggressionState when the NPC receives its first threat event (damage from a player/NPC). Immediately transitions to combat without waiting for the next SweepForEnemies(float) physics poll.

This eliminates the biggest polling cost for non-Active NPCs: thousands of per-NPC physics OverlapSphere calls every EnemySweepRate seconds. Nearby/Far tier NPCs rely entirely on this event to detect combat. Active tier NPCs still run SweepForEnemies for proactive (hostile faction) detection.

public void OnThreatReceived(ICharacter attacker)

Parameters

attacker ICharacter

The character that generated the first threat event.

PetStanceAllowsAutoEngage(bool)

Returns whether this NPC's pet stance permits engaging on its own. Always true for anything that is not a pet.

public bool PetStanceAllowsAutoEngage(bool requiresAggressive)

Parameters

requiresAggressive bool

True to require the Aggressive stance (hunting for a fight); false to accept anything except Passive (fighting back).

Returns

bool

True if the NPC may engage.

PickBestAbility(float)

Selects the best ability to use against the current target from the NPC's known abilities.

When an AbilityRotation is assigned, it is evaluated first. If it returns an ability, that ability is used. If no rotation entry matches and FallbackToDefault is true, the default scoring-based picker runs as a fallback.

Prefers abilities whose range covers the current distance. Among those, picks one at random weighted toward longer-cooldown (typically stronger) abilities. Returns null if no ability is usable (all on cooldown, out of resources, or no abilities known).

public Ability PickBestAbility(float preferredMaxRange = 3.4028235E+38)

Parameters

preferredMaxRange float

Maximum desired range. Abilities with range beyond this are still considered but deprioritized.

Returns

Ability

The chosen ability, or null if nothing is available.

PickBestAbility(float, Func<Ability, bool>)

Selects the best ability to use against the current target, optionally restricted to abilities matching a predicate.

public Ability PickBestAbility(float preferredMaxRange, Func<Ability, bool> filter)

Parameters

preferredMaxRange float

Maximum desired range.

filter Func<Ability, bool>

Optional predicate an ability must satisfy to be considered. Used by HealerAttackingState to keep heals out of the damage rotation.

Returns

Ability

The chosen ability, or null if nothing is available.

PickNearestWaypoint()

Picks the nearest waypoint to the current position and sets it as the destination.

public bool PickNearestWaypoint()

Returns

bool

True if a waypoint destination was set.

PickScoredAbility(float, Func<Ability, bool>, float)

Scores every usable known ability against a subject at the given squared distance and returns the highest scorer.

public Ability PickScoredAbility(float sqrDistanceToSubject, Func<Ability, bool> filter, float jitter)

Parameters

sqrDistanceToSubject float

Squared distance to whatever the ability will be aimed at.

filter Func<Ability, bool>

Optional predicate an ability must satisfy. Null accepts all.

jitter float

Maximum random score jitter, for variety.

Returns

Ability

The best-scoring usable ability, or null.

Remarks

Shared by the default enemy picker and by HealerAttackingState's heal picker, which scores against an ally's distance rather than the target's. Both previously carried their own near-identical copy of this loop.

ResetState(bool)

Resets the controller's state, clearing home, target, look target, and virtual camera.

public override void ResetState(bool asServer)

Parameters

asServer bool

Whether the reset is performed on the server.

Resume()

Resumes the agent's movement.

public void Resume()

SetRandomDestination(float)

Sets a random destination within a radius around the current position.

public bool SetRandomDestination(float radius = 5)

Parameters

radius float

Radius to randomize destination.

Returns

bool

SetRandomHomeDestination(float)

Sets a random destination within a radius around the home position.

public bool SetRandomHomeDestination(float radius = 5)

Parameters

radius float

Radius to randomize destination.

Returns

bool

SetThrottledDestination(Vector3)

Throttled destination setter. Only calls UnityEngine.AI.NavMeshAgent.SetDestination(UnityEngine.Vector3) if enough time has elapsed since the last repath (controlled by RepathInterval). Use this for ongoing movement toward a moving target (chase, orbit, retreat) to prevent path recalculation spam. For one-time destinations (waypoint arrival, warp), use UnityEngine.AI.NavMeshAgent.SetDestination(UnityEngine.Vector3) directly.

public bool SetThrottledDestination(Vector3 position)

Parameters

position Vector3

The world position to navigate toward.

Returns

bool

True if the destination was updated, false if throttled.

Stop()

Stops the agent's movement.

public void Stop()

TransitionToIdleState()

Transitions to the idle state.

public void TransitionToIdleState()

TransitionToNextWaypoint()

Transitions to the next waypoint in the waypoint array.

public bool TransitionToNextWaypoint()

Returns

bool

TransitionToRandomMovementState()

Transitions to a random movement state from the available movement states.

public virtual void TransitionToRandomMovementState()

TryMoveTo(Vector3, bool, float)

Asks the agent to move to a world position, reporting whether the path actually reaches it.

public AIMovementResult TryMoveTo(Vector3 destination, bool throttle = true, float sampleRadius = 2)

Parameters

destination Vector3

Desired world position.

throttle bool

True to respect RepathInterval. Use for ongoing movement toward something that moves; pass false for one-shot destinations such as a waypoint or a spawn point, where a silently dropped request means the NPC never sets off at all.

sampleRadius float

Initial NavMesh sample radius.

Returns

AIMovementResult

What happened.

TryRecoverFromStuck(Vector3)

Attempts to free a stuck NPC, escalating with each call within the same episode.

public bool TryRecoverFromStuck(Vector3 fallback)

Parameters

fallback Vector3

Where to go if the original destination cannot be recovered — a pet's owner, an NPC's home. Pass the NPC's own position to mean "just get unstuck where you are".

Returns

bool

True if a recovery action was taken.

Remarks

First a nudge: re-sample the destination from a wider radius and repath, which clears the common case of two agents wedged against each other or a destination that sampled onto the wrong side of a wall. Then, once StuckWarpTimeout has elapsed, a warp — the only remedy for genuinely unreachable geometry.

Warping is a last resort rather than the first move on purpose: it is visible to players, so an NPC should be seen to try to walk before it blinks.

TrySampleNavMesh(Vector3, out Vector3, float)

Places a world position onto the NavMesh, widening the search until it lands or the attempts run out.

public static bool TrySampleNavMesh(Vector3 position, out Vector3 result, float initialRadius = 2)

Parameters

position Vector3

The desired world position.

result Vector3

The nearest position actually on the NavMesh.

initialRadius float

Radius for the first attempt.

Returns

bool

True if a NavMesh position was found.

Remarks

A single fixed-radius sample is the difference between "the NPC walks to a slightly different spot" and "the NPC does not move at all", and the old call sites all took the second outcome silently.

WarpTo(Vector3)

Places the agent at a world position, using UnityEngine.AI.NavMeshAgent.Warp(UnityEngine.Vector3) so the agent's internal NavMesh position is updated rather than only its transform.

public bool WarpTo(Vector3 position)

Parameters

position Vector3

The world position to place the agent at.

Returns

bool

True if the agent was placed on the NavMesh.

Remarks

Required for object pooling. A recycled NPC is reactivated at a new position, and assigning transform.position alone leaves the agent believing it is still where the previous occupant died — it then either refuses to path or walks back toward the old location.