Abstract
Problem: How do you make NPCs and companions react meaningfully to player actions in an RPG — and what's the best technical approach to support it?
Approach: Tim Cain walks through two generations of implementation: the variable-and-script system used in Arcanum, and the more efficient event handler pattern used in later games like The Outer Worlds.
Findings: Both approaches work, but event-driven systems are more scalable and decoupled. The key design challenge isn't technical — it's deciding what characters should care about based on their personality, then giving them natural-feeling moments to bring it up.
Key insight: Good NPC reactions require both a robust notification system and careful timing — characters should wait for a natural pause (not mid-combat) before confronting the player, and the player should have a chance to use dialogue skills to manage the fallout.
1. The Two Approaches: Scripts vs Events
1.1. Arcanum's Script-and-Variable System
In Arcanum, NPC reactions were handled through two mechanisms: dialogues and scripts.
Every dialogue line — whether spoken by the player or an NPC — could call out to a script. That script could set a variable or, as in The Outer Worlds, directly trigger a companion interjection mid-conversation ("you're talking and suddenly one of your companions says 'Hey, blah blah blah'").
Scripts could also be attached to world events: the player picking up an item, killing a creature, finishing a quest, or advancing the numerical story state. When triggered, these scripts would set variables for later checking.
Tim acknowledges this system was simple and not optimally efficient, but defends the choice: the inefficiency never caused actual performance problems. The frame rate and memory issues Arcanum had came from elsewhere — not from NPC reaction checks. His advice to programmers: don't optimize what isn't actually a bottleneck.
1.2. The Heartbeat Check
NPCs in Arcanum ran on a "heartbeat" — a periodic check every two seconds at most, slowing down the farther the NPC was from the player. During each heartbeat, the NPC would check its variables and decide if it wanted to initiate conversation.
Before starting dialogue, the system verified:
- The player is not in combat
- The player is not already in dialogue
- No enemies are nearby
- At least 30 seconds have passed since the last interaction
This prevented the jarring experience of an NPC demanding to talk the instant combat ends or another conversation finishes.
1.3. The Better Way: Event Handlers
The more efficient approach Tim describes is an event-driven system. When something happens (a creature dies, a quest completes), an event is thrown into a central event system with attached data: what happened, who was involved, when and where.
The critical advantage: the event creator doesn't know or care who's listening. This decoupling means:
- Programmers create events for game state changes
- Narrative designers listen for events on specific NPCs
- Audio designers can listen for events to trigger sound effects or ambient chatter
- End-slide systems can listen and track counters for the ending montage
- Multiple listeners can hear the same event, or one can "consume" it (preventing five companions from all shouting "I need to talk to you!" simultaneously)
Events can also be time-delayed — thrown into a queue set to fire 30 seconds or minutes later, avoiding immediate interruptions.
2. The Cysty Pig Example
Tim illustrates the event system with a concrete example from The Outer Worlds:
- The player kills a cysty pig. A "creature killed" event fires with data: creature type, killer, time, location.
- Companion Bob (who hates cysty pig killing) listens for this event.
- Bob checks: Was it a cysty pig? Did the player kill it? Could Bob see or hear it?
- If the killer wasn't the player, Bob just does ambient chatter: "Oh God, no! That poor cysty pig!"
- If the player did kill it, Bob creates his own event — "Bob sees player kill pig" — set to fire in 30 seconds.
- When that delayed event fires, Bob checks: Is this a good time? Am I conscious? Is the player not in combat or dialogue?
- If not, he resets the event for another 30 seconds.
- When the moment is right, Bob initiates dialogue.
If the player dismisses Bob ("I don't want to talk about this"), Bob can reset the event for two hours or two days later, even saying "We will talk about this eventually" — and he follows through.
This layered event system — programmer events triggering designer events triggering delayed designer events — keeps things efficient and natural.
3. What Should NPCs React To?
With a good event system, programmers will be throwing events for everything: picking up items, dropping items, equipping gear, selling things, talking to NPCs, killing creatures, discovering new maps.
The narrative designer's job is to decide what each character cares about based on their personality — their likes and dislikes. Examples:
- The player equips an evil-aligned helmet
- The player completes a quest by betraying the quest-giver
- The player keeps a quest item instead of turning it in
- The player kills a prominent citizen or a friend of the companion
The designer picks from the available events and writes reactions that fit the character's worldview. The NPC checks each relevant event in real time and decides: do I care about this enough to bring it up?
4. Dialogue as a Resolution Mechanism
Tim emphasizes that handling reactions through dialogue (rather than just automatic consequences) creates space for player agency. When a companion threatens to leave the party, the dialogue system lets the player use leadership or persuasion skills to convince them to stay.
The elegant twist: each time the player talks a companion down, a counter increments, raising the skill threshold for next time. Eventually, if the player keeps doing things the companion hates, no amount of persuasion will prevent them from leaving. This creates a natural escalation that feels earned rather than arbitrary.
5. References
- Tim Cain. YouTube video. https://www.youtube.com/watch?v=OXdyfCNcahw