Skip to main content

NPC and Boss Patrols

EliteMobs can make configured NPCs and Custom Bosses follow authored routes using native Minecraft navigation. The same route system also gives Lua scripts controlled walk_to, hold, and teleport operations.

Quick authoring workflow​

  1. Spawn or place the NPC or Custom Boss you want to edit.
  2. Look directly at it from within 12 blocks and run /em patrol edit.
  3. Stand at each destination and run /em patrol add. New routes start with node 0 at the actor's authored spawn location.
  4. Use /em patrol mode LOOP to connect the last node back to the first, or /em patrol mode REVERSE to walk back through the list.
  5. Run /em patrol save to write the route and refresh that actor.

The editor shows node connections and the resolved walk preview. /em patrol remove removes the last node, /em patrol undo restores a previous edit, and /em patrol status reports the editing driver. /em patrol cancel ends the session without saving route changes. All patrol authoring commands are player-only and require elitemobs.patrol.admin. Waypoints must be in the authored world; the editor rejects a waypoint in a WorldGuard elitemob-spawning deny region.

New routes use relative coordinates. When editing an existing route, the editor preserves its patrol.relative setting and saves added waypoints in that format. Set the format in the YAML configuration.

If an NPC or regional boss file has several spawnLocations, saving a route for one placed actor creates a separate leaf file for that location and removes that location from the original file. This keeps the edit specific to that actor for both relative and absolute routes. The leaf inherits the original through extends. A boss leaf also copies the original's leashRadius, powers and eliteScript, which extends does not inherit.

Configuration​

Patrol nodes are x,y,z coordinates. With relative: true, the default, they are offsets from the actor's authored spawn location. With relative: false, they are exact coordinates in the actor's world, including its active dungeon instance world. Absolute nodes do not include a world name.

patrol:
enabled: true
relative: true
mode: LOOP
speed: 1.0
virtualSpeed: 0.2
nodes:
- 0,0,0
- 1000,0,0
- 1000,0,250
KeyValuesDefaultDescription
patrol.enabledtrue / falsetrue when nodes existEnables this route.
patrol.relativetrue / falsetrueUses offsets from the authored spawn when true, or exact coordinates in the actor's world when false. Applies to every node in the route.
patrol.modeLOOP / REVERSELOOPLOOP connects the final node to node 0; REVERSE walks back through the list.
patrol.speedpositive finite number1.0Native navigation speed multiplier.
patrol.virtualSpeedpositive finite numbermovement-speed attribute × patrol speedBlocks advanced per tick along the cached resolved path while a persistent actor has no loaded body. Usually leave this unset.
patrol.startNodeWaitSecondsfinite number, 0 or more0Seconds the actor rests at node 0 each time it gets back there: once per lap in LOOP, once per round trip in REVERSE. An actor that spawns at node 0 also rests before its first lap. While resting it faces its spawn heading.
patrol.nodeslist of at least two coordinatesnoneOrdered route destinations, interpreted according to patrol.relative. String entries (x,y,z) and x/y/z maps are accepted. Coordinates must be finite and consecutive nodes must differ.

For example, this route visits three exact locations before looping back to the first:

patrol:
enabled: true
relative: false
mode: LOOP
nodes:
- 299,91,215
- 286,91,213
- 279,91,230

Existing routes without patrol.relative keep using offsets. Changing the flag changes how every node is interpreted; it does not convert the stored numbers. Convert the nodes as well when changing an existing route's format.

There is no public maximum-leg setting. A 1,000-block leg is valid. The old experimental patrol.maxLegDistance key is obsolete and has no effect.

The editor preserves the route's navigation speed, any explicit virtualSpeed and the start-node rest when saving. A repeated closing copy of the first node is removed automatically when parsing a route with more than two nodes.

NPC proximity pauses​

NPCs additionally support patrol.pauseNearPlayersRadius (default 0, disabled) and patrol.faceNearbyPlayers (default false). Set a positive finite radius to pause near living, non-spectator players; the facing option makes the paused NPC look at the nearest eligible player. Proximity is checked every 20 ticks. Invalid or negative radii disable the proximity pause.

These settings are independent of chatter and manual Lua patrol holds. They are NPC options, not extra boss patrol fields.

Start-node rest​

With patrol.startNodeWaitSeconds, an actor stops at node 0 before starting the next lap. The rest counts down while the actor is unloaded too. A proximity or combat pause stops the countdown until it ends. walk_to, hold, teleport and patrol_resume() end the rest immediately. The Guild's combat instructor uses a 60-second rest so players can find him at his chair between laps.

Long-distance pathfinding​

Long legs are solved automatically. EliteMobs asks MagmaCore for a rolling route through the currently loaded terrain, splits it into short native-navigation destinations, and replans as the actor advances.

Terrain access and route search are deliberately separated:

  • Loaded chunk snapshots are captured on the server thread through MagmaCore's shared tick-budget workload system.
  • The bounded A* search runs asynchronously over those immutable snapshots.
  • The final short legs are still validated and walked by Minecraft's native pathfinder.
  • Patrol planning never force-loads chunks. Missing terrain causes the planner to wait and retry. A detached persistent actor can advance only along its cached resolved path, then waits when that path runs out. Its body can return only in an entity-ticking area.

This makes routes across a city practical without scanning the whole city in one tick or pretending a straight line through an unloaded building is safe. Flying and aquatic mobs use volume-aware searches; ground mobs require support and clearance and avoid common hazards.

Actor behavior​

  • NPC patrols automatically enable the NPC body's AI and custom-model movement synchronization. syncMovement still controls non-patrol custom-model behavior, but it is not a second patrol switch.
  • Custom Boss patrols require ai: true, a positive movement speed, and a body that is not riding another entity.
  • A boss pauses its patrol during combat and resumes afterward.
  • Non-instanced NPCs and persistent non-instanced bosses retain their safe route position in patrol-state.yml across chunk detachment and reloads. A saved position is dropped when the route's nodes or relative flag change, and the actor starts over from node 0.
  • An actor only rematerializes inside the entity-ticking area. Detachment may still advance its saved position along a previously resolved path; it does not discover a new route through unknown terrain.
  • A passenger cannot own a patrol; route its mount instead.

Lua control​

These methods are exposed on context.npc and context.boss; their patrol operations require a configured controller. Lua coordinates remain offsets from the actor's authored spawn location regardless of patrol.relative, and each method returns whether the request was accepted. walk_to, hold, and teleport also need a live body. Acceptance of a walk does not prove arrival; combat, proximity pauses, disabled AI or path failure can interrupt it.

MethodBehavior
patrol_pause()Stops the current route without losing its configuration.
patrol_resume()Clears a scripted hold or temporary walk and resumes the configured route.
walk_to(x, y, z)Walks to the offset, then resumes the configured patrol.
hold(x, y, z)Walks to the offset and stays there until patrol_resume() is called.
teleport(x, y, z)Teleports the live body to the offset in the same world when the destination is entity-ticking and Bukkit accepts the teleport. Does not clear an existing scripted pause or temporary target; use patrol_resume() to return to the route.
return {
api_version = 1,
on_npc_interact = function(context)
context.npc:walk_to(40, 0, -12)
end
}

walk_to uses the same long-distance solver as configured patrol legs. It does not require authors to break a distant destination into manual waypoints.

Diagnostics​

Outside an edit session, look at the actor and run /em patrol status. The output includes its runtime state, current and target node, route fraction, logical location, virtual speed, whether a body is present, and any hold reason. start_node_wait marks the start-node rest. terrain_unavailable indicates that detached movement has no remaining resolved path; a loaded actor waiting on planning can instead report yielded. The logical location is route progress and can differ from the saved safe location used to restore the body.

Ordinary route failures are retried once by the patrol controller before it advances to the next node. A failed temporary walk or hold also gets one controller retry; it is not proof that the requested destination was reached. Check the terrain and native navigation when an actor skips a waypoint or remains held.