Skip to main content

NPC Data Structure

NPCs (Non-Player Characters) and mobs are defined in JSON manifests and loaded at runtime. This data-driven approach allows content to be modified without code changes.
NPC data is managed in packages/shared/src/data/npcs.ts and loaded from world/assets/manifests/npcs.json.

Data Loading

NPCs are NOT hardcoded. The ALL_NPCS map is populated at runtime:

NPC Data Schema

Each NPC has the following structure:

NPC Categories


Aggression Types

NPCs have different aggression behaviors:

Aggro Rules


Drop Tables

Each NPC has a DropTable defining loot:

Drop Calculation


Available 3D Models

NPCs use rigged GLB models from /assets/world/forge/:

Helper Functions

Get NPC by ID

Get NPCs by Category

Get NPCs by Biome

Get NPCs by Level Range

Check if NPC Can Drop Item


Combat Level Calculation

NPC combat level is calculated from stats:

Spawn Constants

Global spawn settings:

Example NPC Definitions

Melee Mob (Default)

Magic Mob

Magic Mobs: The magic stat determines damage output. The spellId must reference a valid spell from the spell manifest. Mobs have infinite runes and don’t consume resources when casting.

Ranged Mob

Ranged Mobs: The ranged stat determines damage output. The arrowId must reference a valid arrow from the ammunition manifest. Mobs have infinite arrows and don’t consume ammunition when firing.

Combat Type Configuration

Attack Type Fields

For Melee Mobs (default):
  • No special fields required
  • Uses attack and strength stats
  • combatRange defaults to 1 tile
  • Plays COMBAT or SWORD_SWING animation
  • Immediate hit (0 tick delay)
For Ranged Mobs:
  • attackType: "ranged" (required)
  • arrowId: Required (e.g., "bronze_arrow", "iron_arrow", "steel_arrow")
  • ranged: Required stat for damage calculation
  • combatRange: Typically 7-10 tiles
  • attackSpeedTicks: Typically 4 ticks (2.4 seconds)
  • heldWeaponModel: Optional bow GLB for visuals (e.g., "asset://weapons/shortbow.glb")
  • Plays RANGE animation
  • Arrow launch delay: 400ms
  • Hit delay: 1 + floor((3 + distance) / 6) ticks
For Magic Mobs:
  • attackType: "magic" (required)
  • spellId: Required (e.g., "wind_strike", "fire_bolt", "water_strike")
  • magic: Required stat for damage calculation
  • combatRange: Typically 10 tiles
  • attackSpeedTicks: Typically 5 ticks (3.0 seconds)
  • heldWeaponModel: Optional staff GLB for visuals (e.g., "asset://weapons/staff.glb")
  • Plays SPELL_CAST animation
  • Spell launch delay: 600ms
  • Hit delay: 1 + floor((1 + distance) / 3) ticks
Missing Configuration: If a mob has attackType: "magic" but no spellId, or attackType: "ranged" but no arrowId, the attack will be skipped with a console warning. Always configure both the attack type and the corresponding resource ID.

Held Weapon Models

The heldWeaponModel field attaches a 3D weapon to the mob’s hand bone:
Supported Formats:
  • Uses Asset Forge attachment metadata (same as player equipment)
  • Supports V1 (direct attachment) and V2 (pre-baked matrix) formats
  • Weapons are cached and shared across mobs of the same type
  • Automatically attaches to VRM rightHand bone (or custom bone from metadata)
Weapon Cache System:
  • Static _weaponCache in MobVisualManager shares loaded GLB scenes
  • _pendingLoads map deduplicates concurrent fetches for the same URL
  • First mob to load a weapon caches the scene, subsequent mobs clone from cache
  • Eliminates duplicate network requests when multiple mobs of the same type spawn
  • Cache cleared on world teardown via MobNPCSpawnerSystem.destroy()
  • Weapons use clone(true) to share geometry/material buffers (GPU efficient)
Available Weapon Models:
  • asset://weapons/shortbow.glb - Shortbow (for ranged mobs)
  • asset://weapons/staff.glb - Magic staff (for magic mobs)
  • asset://weapons/sword.glb - Sword (for melee mobs)
  • Custom weapons from Asset Forge
Attachment Metadata: Weapons exported from Asset Forge include attachment metadata:
  • vrmBoneName: Target bone name (default: "rightHand")
  • version: Metadata format version (1 or 2)
  • relativeMatrix: Pre-baked 4×4 transform matrix (V2 format only)
Cleanup: Weapons are properly cleaned up when mobs are destroyed:
  • removeFromParent() detaches weapon from bone
  • Geometry and materials are NOT disposed (shared across mob instances)
  • JavaScript garbage collection handles the rest

Adding New NPCs

1

Add to JSON Manifest

Add entry to world/assets/manifests/npcs.json
2

Choose or Create Model

Use existing model or generate new one in 3D Asset Forge
3

Configure Combat Type

Set attackType, spellId/arrowId, and heldWeaponModel if using ranged/magic
4

Restart Server

Server must restart to reload manifests
DO NOT add NPC data directly to npcs.ts. Keep all content in JSON manifests for data-driven design.