NPCs & Path System
NPCs & Path System
Section titled “NPCs & Path System”Journey extends Cobblemon’s NPC system with powerful MoLang scripting functions and an automated path walking system. Create dynamic NPCs with animations, particles, messages, and automated patrol routes.
Table of Contents
Section titled “Table of Contents”- Overview
- NPC MoLang Functions
- Path Walking System
- Path Configuration
- NPC Path Assignments
- Path Visualization
- Complete Examples
Overview
Section titled “Overview”What Journey Provides
Section titled “What Journey Provides”NPC Scripting Extensions:
- Animation control via MoLang
- Particle effects (Cobblemon Snowstorm)
- Chat messages and dialogue
- Distance calculations
- Path walking control functions
Path Walking System:
- Waypoint-based automated movement
- Script execution at each node
- Looping and one-time paths
- Speed control and timing
- Progress persistence across restarts
- Visual path preview with particles
What Journey Does NOT Provide
Section titled “What Journey Does NOT Provide”Journey does NOT include:
- NPC creation/spawning system (use Cobblemon’s NPC system)
- Dialogue tree editor (use Cobblemon’s dialogue system)
- NPC AI/behavior trees
- NPC trading systems
- Quest giver GUIs
- Relationship/reputation systems
All NPC management is done through Cobblemon. Journey provides scripting extensions and movement automation.
Reference: Cobblemon NPC Documentation
NPC MoLang Functions
Section titled “NPC MoLang Functions”These functions are available in NPC contexts (Cobblemon NPC dialogues, interactions). All functions are called on the npc object.
Animation Functions
Section titled “Animation Functions”npc.play_animation(animation_name, [player_uuid])
Section titled “npc.play_animation(animation_name, [player_uuid])”Plays an animation on the NPC.
Parameters:
animation_name(string): The name of the animation to playplayer_uuid(string, optional): UUID of specific player to show animation to. If not provided, shows to all nearby players.
Returns: 1.0 on success, 0.0 on failure
Examples:
# Play animation for all nearby playersnpc.play_animation('wave')
# Play animation for specific player onlynpc.play_animation('point', 'player-uuid-here')Particle Functions
Section titled “Particle Functions”npc.snowstorm_entity_particle(particle_id, locator, [player_uuid])
Section titled “npc.snowstorm_entity_particle(particle_id, locator, [player_uuid])”Spawns a Cobblemon Snowstorm particle effect attached to the NPC.
Parameters:
particle_id(string): Resource location of the particle effectlocator(string): Locator name on the NPC modelplayer_uuid(string, optional): UUID of specific player to show particle to
Examples:
# Show particle to all playersnpc.snowstorm_entity_particle('cobblemon:quest_exclamation', 'head')
# Show particle to specific playernpc.snowstorm_entity_particle('cobblemon:dialogue_dots', 'chest', 'player-uuid')npc.snowstorm_particle(particle_id, x, y, z, [player_uuid])
Section titled “npc.snowstorm_particle(particle_id, x, y, z, [player_uuid])”Spawns a particle effect at a specific position relative to the NPC.
Parameters:
particle_id(string): Resource location of the particle effectx,y,z(number): Relative position offsetsplayer_uuid(string, optional): UUID of specific player to show particle to
Examples:
# Spawn particle 2 blocks above NPC for all playersnpc.snowstorm_particle('cobblemon:sparkles', 0, 2, 0)
# Spawn particle for specific playernpc.snowstorm_particle('cobblemon:marker', 1, 1, 0, 'player-uuid')Communication Functions
Section titled “Communication Functions”npc.say(message, [player_uuid])
Section titled “npc.say(message, [player_uuid])”Makes the NPC say a message in chat.
Parameters:
message(string): The message text. Use%npc%as placeholder for NPC name.player_uuid(string, optional): UUID of specific player to send message to. If not provided, sends to all nearby players (within 64 blocks).
Examples:
# Message to all nearby playersnpc.say('Hello there! I am %npc%!')
# Message to specific playernpc.say('Welcome back, trainer!', 'player-uuid')Distance Functions
Section titled “Distance Functions”npc.distance_to_player(player_uuid)
Section titled “npc.distance_to_player(player_uuid)”Gets the distance between the NPC and a specific player.
Parameters:
player_uuid(string): UUID of the player
Returns: Distance in blocks (number)
Examples:
# Check if player is within 5 blocksnpc.distance_to_player('player-uuid') < 5.0 ? 1.0 : 0.0
# Get exact distancetemp.dist = npc.distance_to_player('player-uuid');Path Walking System
Section titled “Path Walking System”Journey includes a comprehensive automated NPC movement system based on waypoints.
Path Walking Control Functions
Section titled “Path Walking Control Functions”npc.walk_path(path_id)
Section titled “npc.walk_path(path_id)”Starts the NPC walking along a predefined path.
Parameters:
path_id(string): The ID of the path to walk
Returns: 1.0 on success, 0.0 on failure
Example:
npc.walk_path('village_patrol_route')npc.stop_walking()
Section titled “npc.stop_walking()”Stops the NPC from walking its current path.
Returns: 1.0 on success, 0.0 on failure
Example:
npc.stop_walking()npc.pause_walking()
Section titled “npc.pause_walking()”Pauses the NPC’s path walking (can be resumed later).
Returns: 1.0 on success, 0.0 on failure
Example:
npc.pause_walking()npc.resume_walking()
Section titled “npc.resume_walking()”Resumes a paused path walk.
Returns: 1.0 on success, 0.0 on failure
Example:
npc.resume_walking()npc.is_walking_path()
Section titled “npc.is_walking_path()”Checks if the NPC is currently walking a path.
Returns: 1.0 if walking, 0.0 if not
Example:
# Check if NPC is walkingnpc.is_walking_path() == 1.0 ? 'walking' : 'idle'npc.walking_progress()
Section titled “npc.walking_progress()”Gets the NPC’s progress along the current path.
Returns: Progress percentage (0.0-100.0)
Example:
# Check if halfway through pathnpc.walking_progress() >= 50.0 ? 1.0 : 0.0npc.assign_path(path_id, [auto_start])
Section titled “npc.assign_path(path_id, [auto_start])”Assigns a path to the NPC that persists across restarts.
Parameters:
path_id(string): The ID of the path to assignauto_start(number, optional):1.0to auto-start (default),0.0to not auto-start
Returns: 1.0 on success
Examples:
# Assign path with auto-startnpc.assign_path('daily_route')
# Assign path without auto-startnpc.assign_path('event_route', 0.0)npc.unassign_path()
Section titled “npc.unassign_path()”Unassigns the current path from the NPC.
Returns: 1.0 on success
Example:
npc.unassign_path()npc.has_assigned_path()
Section titled “npc.has_assigned_path()”Checks if the NPC has an assigned path.
Returns: 1.0 if has assigned path, 0.0 if not
Example:
npc.has_assigned_path() == 1.0 ? 'patrolling' : 'stationary'npc.get_assigned_path()
Section titled “npc.get_assigned_path()”Gets the ID of the NPC’s assigned path.
Returns: Path ID string, or empty string if none assigned
Example:
# Store path IDtemp.current_path = npc.get_assigned_path();Path Configuration
Section titled “Path Configuration”Paths are defined in JSON configuration files. Each path consists of waypoints (nodes) with positions, look directions, and optional scripts.
File Location: config/journey/paths/<name>.json
Basic Path Structure
Section titled “Basic Path Structure”{ "id": "example_path", "name": "Example Path", "nodes": [ { "position": { "x": 213.0, "y": 63.0, "z": -104.0 }, "look_target": { "x": 105.0, "y": 64.0, "z": 100.0 }, "script": "" }, { "position": { "x": 213.0, "y": 63.0, "z": -112.0 }, "script": "q.wait(20);" } ], "loop": true, "speed_multiplier": 0.5, "description": "An example path demonstrating the JSON format"}Path Fields
Section titled “Path Fields”| Field | Type | Required | Default | Description |
|---|---|---|---|---|
id | String | Yes | - | Unique path identifier |
name | String | Yes | - | Human-readable display name |
nodes | Array | Yes | - | List of PathNode objects (minimum 1) |
loop | Boolean | No | false | Whether path repeats from beginning |
speed_multiplier | Float | No | 1.0 | Movement speed modifier (must be positive) |
description | String | No | "" | Optional description text |
PathNode Structure
Section titled “PathNode Structure”Each node represents a waypoint with position, look direction, and optional script.
{ "position": { "x": 100.0, "y": 64.0, "z": -200.0 }, "look_target": { "x": 110.0, "y": 64.0, "z": -200.0 }, "script": "q.wait_seconds(3);"}| Field | Type | Required | Description |
|---|---|---|---|
position | Vector3 | Yes | World coordinates where NPC walks to |
position.x | Float | Yes | X coordinate |
position.y | Float | Yes | Y coordinate |
position.z | Float | Yes | Z coordinate |
look_target | Vector3 | No | Coordinates NPC faces when reaching node |
look_target.x | Float | No | X coordinate to look at |
look_target.y | Float | No | Y coordinate to look at |
look_target.z | Float | No | Z coordinate to look at |
script | String | No | MoLang expression to execute at this node |
If look_target is omitted: NPC will look in the direction of the next node (or current position if last node).
Node Scripts
Section titled “Node Scripts”Scripts execute when the NPC reaches a node. Use MoLang expressions for timing and actions.
Wait for ticks:
q.wait(20); // Wait 20 ticks (1 second)Wait for seconds:
q.wait_seconds(3); // Wait 3 seconds (60 ticks)Check if waiting:
q.is_waiting(); // Returns 1.0 if NPC is waiting, 0.0 otherwiseGet remaining wait time:
q.wait_remaining(); // Returns remaining ticksExamples:
Pause for 5 seconds:
{ "position": {"x": 100, "y": 64, "z": 200}, "script": "q.wait_seconds(5);"}Quick pause (0.5 seconds):
{ "position": {"x": 100, "y": 64, "z": 200}, "script": "q.wait(10);"}No pause (move immediately):
{ "position": {"x": 100, "y": 64, "z": 200}, "script": ""}NPC Path Assignments
Section titled “NPC Path Assignments”Assign paths to NPCs in the global assignment file.
File Location: config/journey/npc_paths.json
[ { "npc_uuid": "0af627fe-3254-48b0-9892-881a42512c43", "assigned_path_id": "example_path", "auto_start": true, "current_index": 0, "is_complete": false, "is_paused": false, "reach_threshold": 0.9 }]Assignment Fields
Section titled “Assignment Fields”| Field | Type | Required | Default | Description |
|---|---|---|---|---|
npc_uuid | UUID String | Yes | - | UUID of the NPC entity |
assigned_path_id | String | Yes | - | ID of the path to follow |
auto_start | Boolean | No | true | Automatically start path when NPC loads |
current_index | Integer | No | 0 | Current node index (for progress persistence) |
is_complete | Boolean | No | false | Whether path has been completed |
is_paused | Boolean | No | false | Whether path walker is paused |
reach_threshold | Float | No | 0.9 | Distance (blocks) to consider node “reached” |
Movement Mechanics
Section titled “Movement Mechanics”Reach Threshold
Section titled “Reach Threshold”The reach_threshold determines how close the NPC must get to a node before moving to the next:
{ "reach_threshold": 0.9 // NPC must be within 0.9 blocks}Recommendations:
- Precise positioning:
0.2 - 0.5 - Standard movement:
0.9(default) - Forgiving movement:
1.5 - 2.0
Speed Multiplier
Section titled “Speed Multiplier”Adjust movement speed for the entire path:
{ "speed_multiplier": 1.5 // 50% faster}Examples:
0.5- Half speed (slow, cautious)1.0- Normal speed (default)1.5- 50% faster2.0- Double speed
Looping Behavior
Section titled “Looping Behavior”Looping Path:
{ "loop": true}- After reaching the last node, returns to first node
- Continues indefinitely
- Perfect for patrols, circular routes
One-Time Path:
{ "loop": false}- After reaching the last node, stops moving
- Remains at final position
- Perfect for delivery routes, one-way journeys
Stuck Detection & Recovery
Section titled “Stuck Detection & Recovery”The system automatically handles stuck NPCs:
Detection:
- If NPC moves < 0.05 blocks for 60 ticks (3 seconds), considered stuck
Recovery:
- If close to target (within 2.5x threshold): Force progression to next node
- If far from target: Retarget with 1.5x speed boost and apply gentle push force
Corrective Movement:
- Applied when NPC is close but not quite at target
- Gentle push force (0.02 × speed multiplier) toward target
- Prevents NPCs from circling waypoints
Progress Persistence
Section titled “Progress Persistence”NPC path progress saves automatically:
When Progress is Saved:
- Every 10 seconds (200 ticks) during normal operation
- When path completes
- When path is cancelled/paused
- When NPC is removed or unloaded
What is Saved:
- Current node index
- Completion status
- Pause state
On Server Restart:
- NPCs resume from saved node index
- Progress continues seamlessly
Path Visualization
Section titled “Path Visualization”Players can preview paths in-game using particle effects.
Enable Visualization
Section titled “Enable Visualization”Via Player Query Function:
q.player.toggle_path_visualization() // Toggle on/offq.player.show_path_visualization(true) // Enableq.player.show_path_visualization(false) // Disableq.player.preview_path('guard_patrol') // Preview specific pathVia GUI:
/journey pathmanagerOpens a GUI for path creation and visualization control.
Visualization Elements
Section titled “Visualization Elements”Path Lines:
END_RODparticles: Regular node-to-node connectionsENCHANTparticles: Loop connection (last node → first node)- Particle spacing: 0.5 blocks apart
Node Markers:
- Circular particle effect at each node
- 8 particles in a 0.5-block radius circle
- Animated vertical movement
- Different colors based on node index
Look Direction Arrows:
- Small arrow particles showing look_target direction
- Only shown when look_target differs from node position
Rendering:
- Maximum distance: 64 blocks from player
- Update frequency: Every 10 ticks (0.5 seconds)
- Per-player visibility (doesn’t affect other players)
Complete Examples
Section titled “Complete Examples”Example 1: Quest NPC with Animation
Section titled “Example 1: Quest NPC with Animation”# In Cobblemon dialogue scriptnpc.play_animation('greet');npc.say('Welcome, %npc% here! Need any help?');Example 2: Conditional Particle Effects
Section titled “Example 2: Conditional Particle Effects”# Show exclamation if player has quest availableq.player.has_flag('quest_available') == 1.0 ? npc.snowstorm_entity_particle('cobblemon:quest_ready_exclamation', 'head') : 0.0Example 3: Proximity-Based Greeting
Section titled “Example 3: Proximity-Based Greeting”# In dialogue conditionnpc.distance_to_player(query.player.uuid) < 3.0 &&q.player.has_completed_task('introduction_quest') == 0.0 ? npc.say('Hello there! You look new around here.') : 0.0Example 4: Path Walking with Conditions
Section titled “Example 4: Path Walking with Conditions”# Start patrol during daytime onlyquery.world.is_day == 1.0 ? npc.walk_path('day_patrol_route') : npc.walk_path('night_patrol_route')Example 5: Dynamic NPC Behavior
Section titled “Example 5: Dynamic NPC Behavior”# Check walking progress and trigger event at midpointtemp.progress = npc.walking_progress();temp.progress >= 50.0 && temp.progress < 51.0 ? npc.say('Halfway there!') : 0.0Example 6: Simple Guard Patrol
Section titled “Example 6: Simple Guard Patrol”File: config/journey/paths/guard_patrol.json
{ "id": "guard_patrol", "name": "Guard Patrol Route", "nodes": [ { "position": {"x": 100, "y": 64, "z": 100}, "look_target": {"x": 100, "y": 64, "z": 110}, "script": "q.wait_seconds(2);" }, { "position": {"x": 120, "y": 64, "z": 100}, "look_target": {"x": 120, "y": 64, "z": 110}, "script": "q.wait_seconds(2);" }, { "position": {"x": 120, "y": 64, "z": 120}, "look_target": {"x": 110, "y": 64, "z": 120}, "script": "q.wait_seconds(2);" }, { "position": {"x": 100, "y": 64, "z": 120}, "look_target": {"x": 110, "y": 64, "z": 120}, "script": "q.wait_seconds(2);" } ], "loop": true, "speed_multiplier": 0.8, "description": "Rectangular patrol with 2-second pauses at each corner"}Behavior: Guard walks a rectangle, pausing 2 seconds at each corner, looking outward.
Example 7: Traveling Merchant
Section titled “Example 7: Traveling Merchant”File: config/journey/paths/merchant_route.json
{ "id": "merchant_route", "name": "Traveling Merchant Route", "nodes": [ { "position": {"x": 0, "y": 64, "z": 0}, "look_target": {"x": 5, "y": 64, "z": 0}, "script": "q.wait_seconds(30);" }, { "position": {"x": 100, "y": 64, "z": 0}, "script": "" }, { "position": {"x": 100, "y": 64, "z": 100}, "look_target": {"x": 105, "y": 64, "z": 105}, "script": "q.wait_seconds(30);" }, { "position": {"x": 0, "y": 64, "z": 100}, "script": "" } ], "loop": true, "speed_multiplier": 0.6, "description": "Merchant travels between two towns, staying 30 seconds at each"}Behavior: Merchant travels slowly between two locations, pausing for 30 seconds at each stop.
Example 8: One-Time Delivery
Section titled “Example 8: One-Time Delivery”File: config/journey/paths/messenger_delivery.json
{ "id": "messenger_delivery", "name": "Urgent Message Delivery", "nodes": [ { "position": {"x": 0, "y": 64, "z": 0}, "script": "" }, { "position": {"x": 50, "y": 64, "z": 50}, "script": "q.wait(10);" }, { "position": {"x": 100, "y": 64, "z": 0}, "look_target": {"x": 110, "y": 64, "z": 0}, "script": "q.wait_seconds(5);" } ], "loop": false, "speed_multiplier": 1.5, "description": "Fast one-way delivery from point A to point B"}Behavior: Messenger runs quickly from start to end, stops at destination.
Commands & GUI
Section titled “Commands & GUI”Path Manager GUI
Section titled “Path Manager GUI”/journey pathmanagerOpens interactive GUI for:
- Creating paths visually
- Editing existing paths
- Toggling path visualization
- Testing paths
Integration with Other Systems
Section titled “Integration with Other Systems”With Interactables
Section titled “With Interactables”Create interactables that control paths:
q.player.preview_path('merchant_route');q.player.toggle_path_visualization();With Tasks
Section titled “With Tasks”Use NPC arrival as task objectives:
{ "event": "ENTITY_INTERACT", "event_data": { "uuid": "npc-uuid-here" }, "filter": "q.entity.position.x >= 100.0 && q.entity.position.x <= 105.0", "target": 1}Best Practices
Section titled “Best Practices”Path Design
Section titled “Path Design”✅ Smooth corners:
// Add intermediate nodes for smooth turns{"position": {"x": 100, "y": 64, "z": 100}},{"position": {"x": 105, "y": 64, "z": 105}}, // Curve{"position": {"x": 110, "y": 64, "z": 110}}✅ Appropriate node spacing:
- Close nodes (2-5 blocks): Smooth, curved paths
- Far nodes (10-20 blocks): Straight routes, faster travel
✅ Test in-game:
- Use visualization to preview
- Watch NPC movement
- Adjust reach_threshold if stuck
Timing
Section titled “Timing”✅ Realistic pauses:
// Guard looking around"script": "q.wait_seconds(3);"
// Merchant selling"script": "q.wait_seconds(30);"
// Quick checkpoint"script": "q.wait(10);"✅ Match speed to purpose:
- Patrols: 0.7 - 0.9
- Normal walking: 1.0
- Running/urgent: 1.3 - 1.8
Look Targets
Section titled “Look Targets”✅ Point NPCs at points of interest:
{ "position": {"x": 100, "y": 64, "z": 100}, "look_target": {"x": 95, "y": 64, "z": 110}, // Look at shop "script": "q.wait_seconds(2);"}✅ Face next waypoint for natural movement:
{ "position": {"x": 100, "y": 64, "z": 100}, "look_target": {"x": 120, "y": 64, "z": 100} // Next node direction}Organization
Section titled “Organization”✅ Descriptive path IDs and names:
{ "id": "town_guard_patrol_north", "name": "North Gate Patrol Route"}✅ Document complex paths:
{ "description": "Merchant travels from village to city, stopping at waypoint tavern for 1 minute"}Troubleshooting
Section titled “Troubleshooting”NPC not playing animation
Section titled “NPC not playing animation”- Verify the animation name exists in the NPC’s model
- Check that the NPC entity is loaded
- Ensure player UUID is valid (if targeting specific player)
Path walking not working
Section titled “Path walking not working”- Verify the path JSON is valid and loaded
- Check that the NPC entity can move (
canMove: true) - Ensure path nodes are accessible (no walls/obstacles)
- Verify NPC is not in dialogue or other interrupted state
Particles not showing
Section titled “Particles not showing”- Verify particle ID is correct and registered
- Check that locator exists on NPC model (for entity particles)
- Ensure player is within render distance
NPC Not Moving
Section titled “NPC Not Moving”- Check
auto_startistruein npc_paths.json - NPC UUID is correct
- Path ID matches exactly
- At least one node exists in path
- NPC is not paused (
is_paused: false)
NPC Getting Stuck
Section titled “NPC Getting Stuck”- Increase
reach_threshold(try 1.5 or 2.0) - Check for obstacles in path
- Ensure Y coordinates match terrain height
- Add intermediate nodes to avoid complex terrain
NPC Moving Too Fast/Slow
Section titled “NPC Moving Too Fast/Slow”{ "speed_multiplier": 1.2 // Increase for faster, decrease for slower}NPC Not Pausing at Nodes
Section titled “NPC Not Pausing at Nodes”{ "script": "q.wait_seconds(5);" // Must have semicolon}Visualization Not Showing
Section titled “Visualization Not Showing”- Player is within 64 blocks of path
- Visualization is enabled for player
- Path ID is correct in preview command
Performance Considerations
Section titled “Performance Considerations”Paths are lightweight:
- Minimal server impact
- Efficient AI pathfinding
- Cached node targets
- Automatic stuck recovery
Optimization tips:
- Use fewer nodes for simple paths (system handles straight lines)
- Avoid excessive wait scripts on many NPCs
- Limit visualization to when needed (not always-on)
The NPCs & Path System provides complete control over NPC scripting and movement with minimal configuration. Combine with other Journey systems for dynamic, living worlds with realistic NPC behavior.