Mob AI & NPC Behavior
The mob AI system implements OSRS-accurate behavior using a state machine pattern. Mobs have distinct behavioral states and transition based on game events like player proximity, combat, and leash distance.AI code lives in:
packages/shared/src/entities/managers/AIStateMachine.ts- State machinepackages/shared/src/systems/shared/combat/AggroSystem.ts- Aggression detectionpackages/shared/src/entities/npc/MobEntity.ts- Mob entity class
AI State Machine
Mobs use a clean state machine with 5 core states:State Behavior Details
IDLE State
- Mob stands still watching for players
- Random idle duration: 3-8 seconds
- Checks for nearby players each frame
- Transitions to WANDER after idle time expires (if movement type allows)
- Transitions to CHASE if player detected within aggro range
WANDER State
- Generate random target within wander radius
- Move toward target tile-by-tile
- Uses tile-based distance checks to prevent infinite loops
- Transitions to CHASE if player detected
- Transitions to IDLE when target reached
CHASE State
- Mob pursues its current target
- Transitions to ATTACK when in melee range
- Transitions to RETURN if target escapes or mob exceeds leash distance
- Handles same-tile step-out (OSRS-accurate)
ATTACK State
- Mob performs attacks on tick intervals
- Uses
canAttack(currentTick)for OSRS-accurate timing - Transitions to CHASE if target moves out of range
- Transitions to RETURN if target dies or escapes
- Attack type (melee/ranged/magic) determined by mob configuration
MeleeAttackHandler, RangedAttackHandler, or MagicAttackHandler) based on attackType.
RETURN State
- Mob walks back to spawn point
- Uses tile-based pathfinding
- Clears combat state and target
- Transitions to IDLE when at spawn
Aggression System
TheAggroSystem handles mob aggression detection following OSRS rules.
Level-Based Aggression
OSRS rule: Aggressive mobs only attack players whose combat level is less than double the mob’s level + 1.- Level 2 Goblin → Ignores players level 5+
- Level 14 Dark Wizard → Ignores players level 29+
- Level 89 Abyssal Demon → Ignores players level 179+ (never ignores)
Tolerance Timer (10-Minute Immunity)
In OSRS, players become immune to aggression after staying in a 21×21 tile region for 10 minutes:Detection Ranges
Leash System
Mobs have a maximum chase distance from their spawn point:- Combat state is cleared
- Target is cleared
- Mob transitions to RETURN state
- Health regenerates while returning (optional)
Movement Types
Mobs have configurable movement behaviors defined in their manifest:| Type | Behavior |
|---|---|
stationary | Never moves from spawn, only rotates to face targets |
wander | Random movement within wander radius |
patrol | Walks between defined patrol points |
Same-Tile Step-Out
OSRS-accurate behavior when mob is on the same tile as its target:“In RS, they pick a random cardinal direction and try to move the NPC towards that by 1 tile, if it can. If not, the NPC does nothing that cycle.”
Mob Entity Configuration
Attack Type Behavior
Melee Mobs (default):- Use
attackandstrengthstats - Attack at close range (1-2 tiles)
- Play
COMBATorSWORD_SWINGanimation - No projectiles
- Require
attackType: "ranged"andarrowId - Use
rangedstat for damage calculation - Attack at distance (typically 7-10 tiles)
- Emit arrow projectiles
- Play
RANGEanimation - Infinite arrows (no consumption)
- Require
attackType: "magic"andspellId - Use
magicstat for damage calculation - Attack at distance (typically 10 tiles)
- Emit spell projectiles with element-based visuals
- Play
SPELL_CASTanimation - Infinite runes (no consumption)
Combat Range by Type
combatRange from the NPC manifest, with fallbacks to these defaults if not specified.
Held Weapon Visuals
Mobs can display held weapons (bows, staves, swords) using theheldWeaponModel field:
- Weapons loaded as GLB models and attached to VRM hand bones
- Uses same Asset Forge attachment metadata as player equipment
- Supports both V1 (direct) and V2 (pre-baked matrix) attachment formats
- Static weapon cache shares loaded models across mob instances
clone(true)creates per-mob instances with shared geometry/materials- Weapons removed from parent on mob destroy (geometry shared, not disposed)
- Cache cleared on world teardown to free GPU resources
- First mob of a type loads the weapon GLB from network
- Subsequent mobs clone from cached scene (no network request)
- Deduplicates concurrent fetches via
_pendingLoadspromise cache - Prevents duplicate geometry and GPU memory waste
- Bows (shortbow, oak shortbow, willow shortbow, etc.)
- Staves (staff of air, staff of fire, etc.)
- Swords (bronze sword, iron sword, etc.)
- Any GLB model with Asset Forge attachment metadata
AI Events
| Event | Data | Description |
|---|---|---|
MOB_NPC_SPAWNED | mobId, mobType, position | New mob created |
MOB_NPC_DESPAWN | mobId | Mob removed from world |
MOB_NPC_POSITION_UPDATED | mobId, position | Mob moved |
MOB_AI_STATE_CHANGED | mobId, oldState, newState | AI state transition |
COMBAT_STARTED | attackerId, targetId | Combat initiated |