Companion Betrayal
Design pattern addressing companion betrayal, defining how this system creates engagement and supports the overall game experience.
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();
}
}