Files
BreakEscape/docs/TIMED_CONVERSATIONS.md
Z. Cliffe Schreuders 9a8ef9b9f5 Add Timed Conversations feature for Person NPCs
- 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.
2025-11-07 16:24:27 +00:00

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 conversation

    • 0 = immediately when scenario loads
    • 3000 = 3 seconds
    • 60000 = 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

  1. Game startsinitializeGame() is called
  2. NPCManager createdwindow.npcManager = new NPCManager(...)
  3. Timed messages startedwindow.npcManager.startTimedMessages()
    • This starts a timer that checks every 1 second
  4. Scenario loads → NPCs are registered via registerNPC(npcDef)
  5. NPCManager detects timedConversation → Calls scheduleTimedConversation()

Trigger Flow

  1. Timer ticks → Every 1 second, _checkTimedMessages() runs
  2. Time threshold reached → If elapsed >= conversation.triggerTime:
    • _deliverTimedConversation() is called
    • Updates NPC's currentKnot to targetKnot
    • Opens person-chat minigame via MinigameFramework.startMinigame('person-chat', ...)
  3. 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 currentKnot to match targetKnot
  • 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 currentKnot to start at the correct conversation point
  • Game initialization: startTimedMessages() is already called in js/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_back automatically opens a conversation after 3 seconds
  • Conversation starts at the group_meeting knot
  • 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

  1. Check browser console for errors
  2. Verify MinigameFramework is available: console.log(window.MinigameFramework)
  3. Confirm NPC ID matches: console.log(window.npcManager.getNPC('npcId'))
  4. Verify targetKnot exists in Ink story

Conversation Opens at Wrong Knot

  1. Verify targetKnot spelling matches Ink file
  2. Check that knot exists in compiled JSON (not just .ink source)
  3. Ensure JSON was recompiled after Ink changes

Timer Not Running

  1. Verify window.npcManager.startTimedMessages() was called
  2. Check window.npcManager.timerInterval is not null
  3. Verify game time is advancing (check window.npcManager.gameStartTime)

Limitations & Future Enhancements

Current Limitations

  • Only one timedConversation per NPC (can add multiple via scheduleTimedConversation() 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