- Created CHANGES_APPLIED.md to document critical clarifications and updates based on code review findings. - Established a new review and improvements document (REVIEW_AND_IMPROVEMENTS.md) outlining 9 critical improvements and 12 enhancement opportunities for NPC behavior. - Updated TECHNICAL_SPEC.md, IMPLEMENTATION_PLAN.md, QUICK_REFERENCE.md, and example_scenario.json with necessary changes regarding roomId tracking, sprite storage, wall collision setup, animation creation timing, and personal space behavior. - Clarified async import patterns and depth update requirements in the implementation plan. - Enhanced personal space behavior design to ensure NPCs back away slowly while maintaining eye contact with players. - Documented all changes and improvements to ensure better integration with existing systems and reduce complexity in NPC behavior implementation.
7.6 KiB
NPC Behavior System - Quick Reference
For Scenario Designers
Basic Setup (Face Player Only - Default)
{
"id": "receptionist",
"displayName": "Receptionist",
"npcType": "person",
"position": { "x": 5, "y": 3 },
"spriteSheet": "hacker-red",
"storyPath": "scenarios/ink/receptionist.json"
}
Result: NPC will automatically turn to face player when player is within 3 tiles (96px).
Add Patrol Behavior
{
"id": "guard",
"displayName": "Security Guard",
"npcType": "person",
"position": { "x": 5, "y": 3 },
"behavior": {
"patrol": {
"enabled": true,
"speed": 80,
"changeDirectionInterval": 4000
}
},
"spriteSheet": "guard",
"storyPath": "scenarios/ink/guard.json"
}
Result: NPC will wander randomly in the room, changing direction every 4 seconds. Will still face player when approached.
Add Personal Space
{
"id": "nervous_npc",
"displayName": "Nervous Employee",
"npcType": "person",
"position": { "x": 5, "y": 3 },
"behavior": {
"personalSpace": {
"enabled": true,
"distance": 48,
"backAwaySpeed": 30,
"backAwayDistance": 5
}
},
"spriteSheet": "hacker",
"storyPath": "scenarios/ink/nervous.json"
}
Result: NPC will back away slowly (5px at a time) if player gets within 48px (1.5 tiles), while still facing the player. NPC remains within interaction range.
Start as Hostile
{
"id": "enemy_agent",
"displayName": "Enemy Agent",
"npcType": "person",
"position": { "x": 5, "y": 3 },
"behavior": {
"hostile": {
"defaultState": true,
"influenceThreshold": -30
}
},
"spriteSheet": "hacker-red",
"storyPath": "scenarios/ink/enemy.json"
}
Result: NPC will have red tint from start. Can be changed via Ink tags.
For Ink Story Writers
Control Hostility
=== make_hostile ===
# hostile
You've gone too far!
-> END
=== make_friendly ===
# hostile:false
Okay, I forgive you.
-> hub
Set Influence Score
=== gain_favour ===
# influence:25
I really appreciate your help!
-> hub
=== lose_favour ===
# influence:-50
I can't believe you did that.
-> hub
Toggle Patrol
=== start_rounds ===
# patrol_mode:on
I'll be making my rounds now.
-> hub
=== stop_rounds ===
# patrol_mode:off
I'll stay here for now.
-> hub
Adjust Personal Space
=== need_distance ===
# personal_space:128
Please keep your distance (4 tiles).
-> hub
=== no_personal_space ===
# personal_space:0
You can come closer now.
-> hub
For Developers
Register a Behavior
// In game.js create() phase
window.npcBehaviorManager.registerBehavior(
'npc_id', // NPC identifier
npcSprite, // Phaser sprite reference
behaviorConfig // Config from scenario JSON
);
Update Loop Integration
// In game.js update() function
export function update(time, delta) {
// ... existing updates ...
if (window.npcBehaviorManager) {
window.npcBehaviorManager.update(time, delta);
}
}
Control via Code
// Set hostile state
window.npcGameBridge.setNPCHostile('guard', true);
// Set influence
window.npcGameBridge.setNPCInfluence('receptionist', 50);
// Toggle patrol
window.npcGameBridge.setNPCPatrol('guard', false);
Configuration Defaults
| Property | Default Value | Description |
|---|---|---|
facePlayer |
true |
Turn to face player when nearby |
facePlayerDistance |
96 |
Distance (px) to start facing |
patrol.enabled |
false |
Enable random patrolling |
patrol.speed |
100 |
Movement speed (px/s) |
patrol.changeDirectionInterval |
3000 |
Time (ms) between direction changes |
personalSpace.enabled |
false |
Back away when player too close |
personalSpace.distance |
48 |
Min distance (px) - smaller than interaction range |
personalSpace.backAwaySpeed |
30 |
Speed when backing away (px/s) - slow |
personalSpace.backAwayDistance |
5 |
Back away distance per update (px) |
hostile.defaultState |
false |
Start hostile |
hostile.influenceThreshold |
-50 |
Become hostile below this influence |
Personal Space Design: NPCs back away slowly (5px at a time) while facing the player. Distance is 48px (1.5 tiles), which is smaller than the interaction range (64px / 2 tiles), so NPCs remain interactive while maintaining comfort.
Distance Reference
| Tiles | Pixels | Use Case |
|---|---|---|
| 1 | 32 | Very close (touching) |
| 1.5 | 48 | Default personal space (stays interactive) |
| 2 | 64 | Interaction range (player can talk to NPC) |
| 3 | 96 | Default face player range |
| 4 | 128 | Extended personal space |
| 5 | 160 | Hostile aggro range |
| 10 | 320 | One full room width |
Behavior Priority
Behaviors are evaluated in priority order (highest first):
- Chase (5) - Hostile chase (future)
- Flee (4) - Hostile flee (future)
- Maintain Space (3) - Back away from player
- Patrol (2) - Random movement
- Face Player (1) - Turn towards player
- Idle (0) - Default standing
Higher priority behaviors override lower priority behaviors.
Common Patterns
Friendly NPC That Patrols
{
"behavior": {
"patrol": { "enabled": true, "speed": 80 }
}
}
Skittish NPC (Personal Space)
{
"behavior": {
"personalSpace": {
"enabled": true,
"distance": 96
}
}
}
Guard That Patrols Until Confronted
{
"behavior": {
"patrol": { "enabled": true, "speed": 100 },
"hostile": { "defaultState": false }
}
}
Ink Story:
=== confrontation ===
# patrol_mode:off
# hostile
Stop right there!
-> combat
NPC That Warms Up to Player
{
"behavior": {
"personalSpace": { "enabled": true, "distance": 96 },
"hostile": { "defaultState": false }
}
}
Ink Story:
=== start ===
# influence:0
# personal_space:96
Stay back!
-> hub
=== make_friends ===
# influence:50
# personal_space:0
Okay, I trust you now.
-> hub
Troubleshooting
NPC Not Facing Player
- Check
facePlayer: truein config - Verify
facePlayerDistanceis large enough - Check player is within range (use debug mode)
NPC Not Patrolling
- Check
patrol.enabled: true - Verify NPC has collision body (immovable: false for movement)
- Check patrol bounds include NPC starting position
- Wall collisions automatically set up for patrolling NPCs
NPC Not Backing Away
- Check
personalSpace.enabled: true - Verify
distanceis 48px (or custom value smaller than interaction range) - Note: NPC should back away slowly while still facing player
- Ensure NPC has collision body
NPC Backs Away Too Far
- Personal space distance should be smaller than 64px (interaction range)
- Default is 48px - NPC stays within interaction range
- Use
backAwayDistance: 5for subtle 5px adjustments
Hostile Tint Not Showing
- Check
hostile: trueset via config or tag - Verify sprite exists and is visible
- Check console for errors
Debug Mode
Enable debug logging:
// In browser console
window.NPC_BEHAVIOR_DEBUG = true;
Visualize behavior ranges (future):
// In browser console
window.NPC_BEHAVIOR_DEBUG_VISUAL = true;
Performance Tips
- Limit NPCs: Keep < 10 NPCs with behaviors per room
- Disable when not visible: Future enhancement
- Use lower update rates: Default 50ms is good balance
- Simple patrol bounds: Smaller areas = less collision checks
Last Updated: 2025-11-09 Version: 1.0