- Introduced a new documentation file detailing the Timed Conversations feature, which allows NPCs to automatically initiate dialogues after a specified delay. - Included configuration examples and use cases to enhance narrative flow in scenarios. - Updated NPCManager to support scheduling and triggering of timed conversations, improving interaction dynamics in the game.
9.6 KiB
Timed Conversations for Person NPCs
Overview
The Timed Conversation feature allows you to automatically trigger a person-to-person conversation with an NPC after a specified delay. This is similar to timedMessages for phone NPCs, but instead of a text message, it automatically opens the person-chat minigame and navigates to a specific conversation knot.
Use Cases
- Opening sequences: Automatically start a dialogue when a scenario loads
- Scripted conversations: Trigger a cutscene-like conversation after the player enters a room
- Delayed reactions: Have an NPC approach the player after a delay with a specific message
- Story progression: Advance the narrative with timed character interactions
Configuration
Basic Structure
Add a timedConversation property to any person NPC in your scenario JSON:
{
"id": "test_npc_back",
"displayName": "Back NPC",
"npcType": "person",
"position": { "x": 6, "y": 8 },
"spriteSheet": "hacker",
"storyPath": "scenarios/ink/test2.json",
"currentKnot": "hub",
"timedConversation": {
"delay": 3000,
"targetKnot": "group_meeting"
}
}
Properties
-
delay(number, milliseconds): How long to wait before opening the conversation0= immediately when scenario loads3000= 3 seconds60000= 1 minute
-
targetKnot(string): The Ink knot to navigate to when the conversation opens- Must exist in the NPC's story file
- The conversation will start at this knot instead of the default
currentKnot
Complete Example
Scenario JSON
File: scenarios/my-scenario.json
{
"scenario_brief": "A test scenario with timed NPC conversation",
"startRoom": "meeting_room",
"player": {
"id": "player",
"displayName": "Agent 0x00",
"spriteSheet": "hacker"
},
"rooms": {
"meeting_room": {
"type": "room_office",
"connections": {},
"npcs": [
{
"id": "colleague",
"displayName": "Junior Agent",
"npcType": "person",
"position": { "x": 4, "y": 5 },
"spriteSheet": "hacker-red",
"storyPath": "scenarios/ink/colleague.json",
"currentKnot": "idle",
"timedConversation": {
"delay": 5000,
"targetKnot": "briefing"
}
}
]
}
}
}
Ink Story
File: scenarios/ink/colleague.ink
VAR briefing_given = false
=== idle ===
# speaker:npc:colleague
Hey, I'm just waiting around for now.
-> END
=== briefing ===
# speaker:npc:colleague
Agent! I've been briefed on the situation. We need to discuss the plan.
~ briefing_given = true
-> main_menu
=== main_menu ===
+ [Tell me more about the situation] -> explain_situation
+ [What do you need from me?] -> ask_help
+ [Let's get moving] -> END
=== explain_situation ===
# speaker:npc:colleague
We've got a security breach in the east wing. Three suspects, two exit routes.
-> main_menu
=== ask_help ===
# speaker:npc:colleague
I need you to help me secure the perimeter while I investigate the breach.
-> main_menu
How It Works
Initialization Flow
- Game starts →
initializeGame()is called - NPCManager created →
window.npcManager = new NPCManager(...) - Timed messages started →
window.npcManager.startTimedMessages()- This starts a timer that checks every 1 second
- Scenario loads → NPCs are registered via
registerNPC(npcDef) - NPCManager detects
timedConversation→ CallsscheduleTimedConversation()
Trigger Flow
- Timer ticks → Every 1 second,
_checkTimedMessages()runs - Time threshold reached → If
elapsed >= conversation.triggerTime:_deliverTimedConversation()is called- Updates NPC's
currentKnottotargetKnot - Opens person-chat minigame via
MinigameFramework.startMinigame('person-chat', ...)
- Conversation opens → Player sees the person-chat UI at the specified knot
API Reference
NPCManager Methods
scheduleTimedConversation(opts)
Manually schedule a timed conversation (usually called automatically from registerNPC).
Parameters:
{
npcId: string, // ID of the person NPC
targetKnot: string, // Ink knot to navigate to
delay?: number, // Milliseconds from now (if triggerTime not provided)
triggerTime?: number // Milliseconds from game start (if delay not provided)
}
Example:
window.npcManager.scheduleTimedConversation({
npcId: 'colleague',
targetKnot: 'briefing',
delay: 5000
});
_deliverTimedConversation(conversation)
Internal method called when a timed conversation is ready to trigger. Opens the person-chat minigame.
Implementation Details
Code Changes
File: js/systems/npc-manager.js
Constructor: Added this.timedConversations = [] to track scheduled conversations
registerNPC(): Added code to detect and schedule timedConversation property:
if (entry.timedConversation) {
this.scheduleTimedConversation({
npcId: realId,
targetKnot: entry.timedConversation.targetKnot,
delay: entry.timedConversation.delay
});
}
_checkTimedMessages(): Updated to also check timed conversations:
for (const conversation of this.timedConversations) {
if (!conversation.delivered && elapsed >= conversation.triggerTime) {
this._deliverTimedConversation(conversation);
conversation.delivered = true;
}
}
New Method: _deliverTimedConversation():
- Updates NPC's
currentKnotto matchtargetKnot - Calls
window.MinigameFramework.startMinigame('person-chat', null, { npcId, title }) - Logs the action for debugging
Integration Points
- NPCManager: Handles scheduling and delivery
- MinigameFramework: Opens the person-chat minigame
- PersonChatMinigame: Uses the updated
currentKnotto start at the correct conversation point - Game initialization:
startTimedMessages()is already called injs/main.js:87
Comparison: Timed Messages vs Timed Conversations
| Feature | Timed Messages | Timed Conversations |
|---|---|---|
| NPC Type | Phone NPCs | Person NPCs |
| Result | Bark + message notification | Automatic minigame open |
| Display | Phone chat minigame | Person-chat minigame |
| Interaction | Player clicks bark to open chat | Chat opens automatically |
| Use Case | Reactive notifications | Scripted sequences |
| Config Property | timedMessages (array) |
timedConversation (object) |
Best Practices
1. Design Your Conversation Flow
Always have a main menu knot that conversations return to:
=== briefing ===
# speaker:npc:colleague
Here's the mission briefing...
-> main_menu
=== main_menu ===
+ [Ask about details] -> details
+ [Ready to go] -> END
2. Account for Timed Conversation Delays
When designing your scenario, consider:
- Total elapsed time before conversation appears
- Player may not be ready (still learning controls)
- Consider adding text/UI guidance beforehand
3. Use Delays Appropriately
- 0-2 seconds: Immediate (feels abrupt but useful for sequential conversations)
- 3-5 seconds: Short delay (allows player to orient themselves)
- 10+ seconds: Long delay (gives player time to explore before scripted event)
4. Combine with Events
You can also trigger conversations through game events instead of just time delays:
"eventMappings": [
{
"eventPattern": "room_entered:briefing_room",
"targetKnot": "briefing",
"bark": "Agent, we need to talk!"
}
]
Debugging
Enable NPC Debug Mode
window.NPC_DEBUG = true; // In browser console
This will log all timed conversation scheduling and delivery.
Check Scheduled Conversations
console.log(window.npcManager.timedConversations);
Shows all pending timed conversations with their status.
Verify NPC Registration
console.log(window.npcManager.getNPC('colleague'));
Shows the NPC object, including the timedConversation property.
Test Scenario
See scenarios/npc-sprite-test2.json for a working example where:
test_npc_backautomatically opens a conversation after 3 seconds- Conversation starts at the
group_meetingknot - Player can interact with multiple NPCs in the dialogue
To test:
Open: http://localhost:8000/index.html?scenario=npc-sprite-test2
Wait 3 seconds for automatic conversation to open
Troubleshooting
Conversation Doesn't Open
- Check browser console for errors
- Verify
MinigameFrameworkis available:console.log(window.MinigameFramework) - Confirm NPC ID matches:
console.log(window.npcManager.getNPC('npcId')) - Verify targetKnot exists in Ink story
Conversation Opens at Wrong Knot
- Verify
targetKnotspelling matches Ink file - Check that knot exists in compiled JSON (not just
.inksource) - Ensure JSON was recompiled after Ink changes
Timer Not Running
- Verify
window.npcManager.startTimedMessages()was called - Check
window.npcManager.timerIntervalis not null - Verify game time is advancing (check
window.npcManager.gameStartTime)
Limitations & Future Enhancements
Current Limitations
- Only one
timedConversationper NPC (can add multiple viascheduleTimedConversation()API) - Uses global timer (all timed events checked once per second)
- No built-in "wait for user input" before triggering
Potential Enhancements
- Support multiple conversations per NPC with conditions
- Trigger on specific player actions (e.g., "when player approaches NPC")
- Cinematic camera focus before opening conversation
- Animation/transition effects when opening timed conversation