Browse/Narrative & Choice/Companion Betrayal
Narrative & Choice

Companion Betrayal

Design pattern addressing companion betrayal, defining how this system creates engagement and supports the overall game experience.

Medium complexity
2 examples
3 patterns

Overview

The companion betrayal mechanic provides a framework that establishes rules governing player behavior and system responses. Designers must carefully balance the system's depth against its learning curve, ensuring that new players can engage while experienced players find room for mastery. The ongoing evolution of this mechanic reflects the broader maturation of game design as a discipline.

Game Examples

Turn-Based RPGs

Turn-Based RPGs use this mechanic where players time their actions precisely to support their team effectively. The mechanic creates natural tension and release cycles, resulting in competitive depth.

Deck Builders

Deck Builders use this mechanic where players plan their approach to survive increasingly difficult challenges. The feedback loop reinforces player engagement, resulting in meaningful player agency.

Pros & Cons

Advantages

  • Creates meaningful mechanical decisions for players
  • Provides long-term engagement for dedicated players
  • Supports numerous viable strategies and approaches

Disadvantages

  • Requires significant design iteration to implement well
  • Can create griefing if not carefully balanced
  • Can create confusing when RNG is unfavorable
  • Requires extensive playtesting to avoid edge cases
  • May overwhelm competitive players with too many options

Implementation Patterns

Journal Controller

Core implementation pattern for handling companion betrayal logic with clean state management.

class CompanionBetrayalEngine {
  currentNode: string = "intro";
  flags: Set<string> = new Set();

  getDialogue() {
    const node = DIALOGUE_TREE[this.currentNode];
    return {
      text: node.text,
      choices: node.choices.filter(c =>
        !c.requires || c.requires.every(f => this.flags.has(f))
      )
    };
  }

  choose(choiceIndex: number) {
    const node = DIALOGUE_TREE[this.currentNode];
    const choice = node.choices[choiceIndex];
    if (choice.setsFlag) this.flags.add(choice.setsFlag);
    this.currentNode = choice.next;
    return this.getDialogue();
  }
}

NPC Scheduler

Optimized pattern for companion betrayal that minimizes per-frame computation cost.

class CompanionBetrayalEngine {
  currentNode: string = "start";
  flags: Set<string> = new Set();

  getDialogue() {
    const node = DIALOGUE_TREE[this.currentNode];
    return {
      text: node.text,
      choices: node.choices.filter(c =>
        !c.requires || c.requires.every(f => this.flags.has(f))
      )
    };
  }

  choose(choiceIndex: number) {
    const node = DIALOGUE_TREE[this.currentNode];
    const choice = node.choices[choiceIndex];
    if (choice.setsFlag) this.flags.add(choice.setsFlag);
    this.currentNode = choice.next;
    return this.getDialogue();
  }
}

Journal Engine

Event-driven pattern that reacts to companion betrayal changes and updates dependent systems.

class CompanionBetrayalHandler {
  currentNode: string = "start";
  flags: Set<string> = new Set();

  getDialogue() {
    const node = DIALOGUE_TREE[this.currentNode];
    return {
      text: node.text,
      choices: node.choices.filter(c =>
        !c.requires || c.requires.every(f => this.flags.has(f))
      )
    };
  }

  choose(choiceIndex: number) {
    const node = DIALOGUE_TREE[this.currentNode];
    const choice = node.choices[choiceIndex];
    if (choice.setsFlag) this.flags.add(choice.setsFlag);
    this.currentNode = choice.next;
    return this.getDialogue();
  }
}