--- id: wiki-2026-0508-drama-management-systems title: Drama Management Systems category: 10_Wiki/Topics status: verified canonical_id: self aliases: [drama manager, narrative AI, DM, story generation, interactive narrative] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [game-design, narrative, drama-management, interactive-narrative, ai, story-generation] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: Game Design applicable_to: [Interactive Narrative, Game AI, LLM Story] --- # Drama Management Systems ## 매 한 줄 > **"매 game 의 author 의 intended pacing / arc 의 maintain 의 AI"**. 매 player agency ↔ author intent 의 tension 의 mediate. 매 famous: Façade (Mateas & Stern), Storytron (Crawford), Left 4 Dead AI Director. 매 modern: 매 LLM 의 dynamic story. ## 매 핵심 ### 매 motivation - **Plot vs play**: 매 player 의 freedom + author 의 arc. - **Pacing**: 매 tension curve 의 maintain. - **Coherence**: 매 character / setting 의 consistent. ### 매 famous example - **Façade** (Mateas & Stern, 2005): 매 ABL behavior + plot points. - **Storytron** (Crawford): 매 character-driven. - **Left 4 Dead Director** (Valve): 매 pacing + drama. - **AI Dungeon**: 매 GPT-driven. - **Inkle (Heaven's Vault, 80 Days)**: 매 ink narrative scripting. ### 매 architecture pattern - **Plot-point manager**: 매 must-hit beat. - **Character authority**: 매 NPC autonomy. - **Pacing controller**: 매 tension curve. - **Coherence guardian**: 매 contradiction 의 detect. ### 매 method - **Search-based** (Roberts/Riedl): 매 plot tree 의 search. - **Beat-based**: 매 narrative beat 의 sequence. - **Constraint-based**: 매 author intent 의 constraint. - **LLM-based**: 매 token 의 narrative. ### 매 응용 1. **RPG**: 매 quest pacing. 2. **Interactive fiction**: 매 plot. 3. **Sim**: 매 emergent story. 4. **Educational**: 매 lesson narrative. 5. **VR experience**: 매 presence + arc. ## 💻 패턴 ### Plot-point manager ```python class PlotPointManager: def __init__(self, plot_points): self.points = plot_points # 매 ordered required events self.completed = [] def hint(self, world_state): """매 next required point 의 condition 의 nudge.""" for p in self.points: if p not in self.completed and p.preconditions(world_state): return p.suggest_nudge(world_state) return None def mark_complete(self, point): self.completed.append(point) # 매 example plot = [ PlotPoint('meet_mentor', preconditions=lambda w: w.location == 'tavern'), PlotPoint('learn_skill', preconditions=lambda w: 'meet_mentor' in w.events), PlotPoint('confront_villain', preconditions=lambda w: 'learn_skill' in w.events), ] ``` ### Pacing controller (tension curve) ```python class PacingController: def __init__(self): # 매 ideal 5-act curve self.target = lambda t: 4 * t * (1 - t) + 0.2 * np.sin(20 * t) def desired_tension(self, story_progress): return self.target(story_progress) # 매 0 to 1 def adjust(self, current_tension, story_progress): target = self.desired_tension(story_progress) if current_tension < target - 0.1: return 'introduce_conflict' elif current_tension > target + 0.1: return 'introduce_calm' return 'maintain' ``` ### Beat-based (Mateas-style) ```python class Beat: def __init__(self, name, preconditions, characters, duration): self.name = name self.preconditions = preconditions self.characters = characters self.duration = duration def can_fire(self, world): return all(c(world) for c in self.preconditions) class DramaManager: def __init__(self, beats): self.beats = beats self.active_beat = None def select_beat(self, world): candidates = [b for b in self.beats if b.can_fire(world)] # 매 score by drama value return max(candidates, key=lambda b: b.drama_score(world)) ``` ### Search-based (declarative author intent) ```python import heapq def best_first_search(state, author_intent, max_depth=10): """매 plot search 의 author 의 maximize.""" queue = [(0, state, [])] while queue: score, s, path = heapq.heappop(queue) if author_intent.satisfied(s) or len(path) >= max_depth: return path for action in s.legal_actions(): new_s = s.apply(action) new_score = -author_intent.evaluate(new_s) heapq.heappush(queue, (new_score, new_s, path + [action])) return None ``` ### LLM-based dynamic story ```python def llm_drama_step(world_state, history, author_intent): prompt = f"""You are a drama manager. Author intent: "{author_intent}" Recent history: {format_history(history[-5:])} World state: {world_state} Output the next narrative beat that: 1. Honors player choice (do NOT railroad) 2. Moves toward author intent 3. Maintains pacing (current tension: {tension}) 4. Stays consistent with character traits Beat (JSON): {{ "type": "...", "description": "...", "characters": [...], "consequences": {{...}} }}""" return llm.generate(prompt) ``` ### Coherence guardian ```python class CoherenceCheck: def __init__(self, kb): self.kb = kb # 매 character traits, world facts def validate(self, beat): contradictions = [] for fact in self.kb.facts: if beat.contradicts(fact): contradictions.append((beat, fact)) return contradictions def auto_fix(self, beat): # 매 LLM 의 use 의 reword contras = self.validate(beat) if contras: return llm.fix_contradictions(beat, contras) return beat ``` ### Inkle ink-style (declarative narrative) ```ink === meet_mentor === The old wizard turns. "You've come at last." * [Bow] -> bow_branch * [Question] -> question_branch - -> learn_skill === bow_branch === You bow deeply. He nods. ~ respect += 1 -> learn_skill ``` ### AI Director-style pacing (Valve) ```python class L4DDirector: """매 Left 4 Dead pacing AI.""" def __init__(self): self.intensity = 0 # 매 0-1 def update(self, players): # 매 measure recent danger recent_damage = sum(p.recent_damage for p in players) recent_kills = sum(p.recent_kills for p in players) self.intensity = min(1, recent_damage / 100 + recent_kills / 20) def decide(self): if self.intensity > 0.7: return 'relaxation' # 매 calm period elif self.intensity < 0.3: return 'build_up' # 매 zombie wave return 'sustain' ``` ### Player model integration ```python def adapt_drama(player_history): """매 player preference 의 drama 의 tune.""" if player_history.combat_seek_count > 10: return 'combat_heavy' if player_history.dialog_engagement > 5: return 'dialog_heavy' return 'balanced' ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | Linear story | Plot-point manager | | Emergent | Beat + character authority | | Action-paced | AI Director (Valve) | | Branching narrative | Inkle / Yarn | | Open-ended LLM | GPT + author constraint | | Educational | Lesson-arc + scaffold | **기본값**: 매 hybrid — 매 plot-point + character authority + LLM dialog + coherence guardian. ## 🔗 Graph - 부모: [[Game-Design]] · [[Interactive-Narrative]] - 응용: [[Dynamic Difficulty Adjustment (DDA)]] - Adjacent: [[Generative-AI]] · [[Computational_Creativity|Computational-Creativity]] ## 🤖 LLM 활용 **언제**: 매 narrative game. 매 interactive fiction. 매 dynamic story. **언제 X**: 매 strict scripted (no agency). 매 pure sim. ## ❌ 안티패턴 - **Railroad**: 매 player choice 의 fake. - **No coherence check**: 매 contradiction story. - **LLM-only**: 매 author intent 의 lose. - **Static beat**: 매 player adaptation X. - **Drama at all cost**: 매 player frustrate. ## 🧪 검증 / 중복 - Verified (Façade paper, Roberts/Riedl, Crawford). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-04-20 | Auto-reinforced | | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — DM architectures + 매 plot point / pacing / beat / LLM / Inkle code |