diff --git a/story_design/story_dev_prompts/07_ink_scripting.md b/story_design/story_dev_prompts/07_ink_scripting.md index 70393db..17e9af5 100644 --- a/story_design/story_dev_prompts/07_ink_scripting.md +++ b/story_design/story_dev_prompts/07_ink_scripting.md @@ -19,6 +19,7 @@ You are an Ink narrative scripter for Break Escape. Your tasks: ## Required Input From previous stages: +- Stage 0: Technical challenges and ENTROPY cell - Stage 1: Narrative structure with story beats - Stage 2: Character profiles and dialogue guidelines - Stage 3: Moral choices and consequence design @@ -29,27 +30,832 @@ From previous stages: ### ESSENTIAL - Technical Documentation - **`docs/INK_INTEGRATION.md`** - How Ink integrates with the game +- **`story_design/story_dev_prompts/FEATURES_REFERENCE.md`** - All available game features - **Ink documentation** - https://github.com/inkle/ink/blob/master/Documentation/WritingWithInk.md ### Essential - Design Documentation - `story_design/universe_bible/10_reference/style_guide.md` - Writing tone - `story_design/universe_bible/04_characters/` - Character voices -- Previous stage outputs (especially Stages 2 and 3) +- Previous stage outputs (especially Stages 1, 2, and 3) + +### Reference Examples +- `scenarios/ink/security-guard.ink` - Complex NPC with patrol and confrontation +- `scenarios/ink/alice-chat.ink` - Hub pattern with trust system +- `scenarios/ink/*.json` - Compiled Ink examples + +## Understanding Break Escape's Three-Act Structure + +Break Escape scenarios follow a specific three-act structure where Ink handles Act 1 and Act 3, while Act 2 is primarily gameplay: + +### Act 1: Interactive Cutscene (Ink-Heavy) +**Duration:** 2-5 minutes +**Medium:** Ink dialogue with choices +**Purpose:** Establish mission, create player investment, set up story + +**What happens:** +- SAFETYNET handler briefs the player +- Stakes and urgency are established +- Player makes initial choices that affect approach +- Background and context provided +- Mission objectives stated +- Player is "released" into gameplay + +**Key Ink elements:** +- Multiple choice points +- Character introductions +- Variable setting for later callbacks +- Conditional dialogue based on choices +- Clear transition to gameplay + +### Act 2: Puzzle Chain Gameplay (Game-Heavy, Ink-Light) +**Duration:** 15-40 minutes +**Medium:** Gameplay with NPC dialogue support +**Purpose:** Player solves puzzles, navigates rooms, overcomes challenges + +**What happens:** +- Player navigates through rooms +- Solves puzzle chains (find code → unlock computer → get key → access room) +- Interacts with NPCs for hints, obstacles, or information +- Collects LORE fragments +- Overcomes technical challenges +- Works toward final objective + +**Ink's role:** +- NPC dialogue when encountered +- Reactive messages via phone NPCs (event-driven) +- Hints and guidance when stuck +- Optional conversations for depth +- Environmental NPC interactions (guards, witnesses) + +### Act 3: Resolution and Consequences (Ink-Heavy) +**Duration:** 2-5 minutes +**Medium:** Ink dialogue, potentially with final choice +**Purpose:** Resolve narrative, show consequences, debrief + +**What happens:** +- Player reaches final objective or location +- Final confrontation or revelation (may be dialogue, may be discovery) +- Consequences of Act 1 choices revealed +- Handler debriefs player +- Mission outcomes discussed +- Narrative closure (or setup for future) + +**Key Ink elements:** +- Callbacks to earlier choices +- Variable-dependent endings +- Character reactions to player's actions +- Mission success/failure acknowledged +- Emotional payoff ## Process -Structure Ink files, write opening cutscene, write NPC dialogues, implement choice moments, write closing cutscene(s), write mid-scenario story beats, implement dynamic content, and test/validate all syntax. +### Step 1: Structure Your Ink Files -Test all Ink files in Inky editor before finalizing. +**Recommended File Organization:** + +``` +scenarios/ink/ +├── [scenario_name]_opening.ink # Act 1: Opening cutscene +├── [scenario_name]_npc_*.ink # Act 2: Individual NPCs +├── [scenario_name]_phone_*.ink # Act 2: Phone contacts +└── [scenario_name]_closing.ink # Act 3: Closing cutscene +``` + +**Alternative (Single File):** +``` +scenarios/ink/[scenario_name].ink # All content in knots +``` + +### Step 2: Write Act 1 - Opening Interactive Cutscene + +Act 1 should be a rich, choice-driven experience that makes players care about the mission. + +#### Opening Cutscene Template + +```ink +// =========================================== +// ACT 1: OPENING CUTSCENE +// Break Escape Scenario: [Name] +// =========================================== + +// Variables for tracking player choices and state +VAR player_approach = "" // cautious, aggressive, diplomatic +VAR handler_trust = 50 // Handler's confidence in player +VAR knows_full_stakes = false // Did player ask about stakes? +VAR mission_priority = "" // speed, stealth, thoroughness + +// External variables (set by game) +EXTERNAL player_name +EXTERNAL scenario_state + +// =========================================== +// OPENING +// =========================================== + +=== start === +#speaker:handler_[name] +{player_name}, thank you for getting here on such short notice. + +[Visual: Handler in SAFETYNET briefing room, serious expression] + +Handler: We have a situation developing at [location]. + +* [Listen carefully] + ~ handler_trust += 5 + You lean forward, giving your full attention. + -> briefing_main + +* [Ask what kind of situation] + Handler: I'll explain. Pay close attention. + -> briefing_main + +* [Express readiness] + ~ handler_trust += 10 + ~ player_approach = "confident" + You: I'm ready. What's the mission? + Handler: Good. Let's get straight to it. + -> briefing_main + +// =========================================== +// MAIN BRIEFING +// =========================================== + +=== briefing_main === +Handler: [ENTROPY Cell Name] has targeted [target]. + +[Provide key context about what's at stake] + +Handler: If they succeed, [consequences]. + +* [Ask about timeline] + ~ knows_full_stakes = true + You: How much time do we have? + Handler: [Urgency explanation - hours/minutes] + -> briefing_details + +* [Ask about ENTROPY's methods] + You: What's their approach? + Handler: [Cell's typical methodology] + -> briefing_details + +* [Ask about innocent bystanders] + ~ handler_trust += 5 + You: Are there civilians at risk? + Handler: [Information about potential collateral] + ~ knows_full_stakes = true + -> briefing_details + +=== briefing_details === +Handler: Your primary objectives: + +[List 3-4 clear objectives] + +* [Ask for clarification on objectives] + -> objectives_clarification + +* [Ask about entry method] + -> cover_story + +* [Accept mission immediately] + ~ player_approach = "direct" + -> mission_approach + +=== objectives_clarification === +[Provide additional detail on objectives] + +Handler: Does that clear things up? + +* [Yes, I understand] + -> cover_story + +* [What if I can't complete all objectives?] + ~ handler_trust -= 5 + Handler: Do your best. Priority is [primary objective]. + -> cover_story + +=== cover_story === +Handler: Your cover is [cover story]. Entry point is [location]. + +{knows_full_stakes: + Handler: Remember, lives are at stake. Be thorough but fast. +} + +-> mission_approach + +// =========================================== +// CRITICAL CHOICE: Mission Approach +// =========================================== + +=== mission_approach === +Handler: How do you want to approach this? + ++ [Cautious and methodical] + ~ player_approach = "cautious" + ~ mission_priority = "thoroughness" + You: I'll be careful. Thorough investigation is key. + Handler: Smart. Take your time but stay alert. + -> final_instructions + ++ [Fast and direct] + ~ player_approach = "aggressive" + ~ mission_priority = "speed" + You: I'll move quickly and complete objectives fast. + Handler: Good. Time is critical. But don't miss anything vital. + -> final_instructions + ++ [Adaptable - assess on site] + ~ player_approach = "diplomatic" + ~ mission_priority = "stealth" + You: I'll read the situation and adapt. + Handler: Flexible thinking. Trust your instincts. + ~ handler_trust += 5 + -> final_instructions + +=== final_instructions === +Handler: Remember Field Operations Rule [relevant number from handbook]. + +{player_approach == "cautious": + Handler: Your careful approach should serve you well. Document everything. +} +{player_approach == "aggressive": + Handler: Speed is good, but don't compromise the mission for it. +} +{player_approach == "diplomatic": + Handler: Adapt as needed. We trust your judgment. +} + +Handler: You'll have comms support. I'll be monitoring. + +* [Any last advice?] + Handler: [Specific hint about first obstacle or key NPC] + -> deployment + +* [I'm ready to go] + -> deployment + +=== deployment === +Handler: Good luck, {player_name}. SAFETYNET is counting on you. + +[Transition: Fade to mission start location] + +#start_gameplay +-> END +``` + +#### Act 1 Best Practices + +1. **Front-load choices** - Give players 3-5 meaningful choices in Act 1 +2. **Set variables** - Track choices that will callback later +3. **Character voice** - Handler should sound consistent with their profile +4. **Stakes clarity** - Player must understand what they're fighting for +5. **Smooth transition** - Clear moment when dialogue ends and gameplay begins +6. **Player agency** - Choices should feel meaningful, not cosmetic + +### Step 3: Write Act 2 - NPC Dialogues + +Act 2 NPCs fall into several categories: + +#### Physical NPCs (Guards, Workers, Obstacles) + +Use the **hub pattern** for conversations with multiple topics: + +```ink +// =========================================== +// ACT 2 NPC: Security Guard +// =========================================== + +VAR influence = 0 +VAR guard_hostile = false +VAR player_warned = false +VAR topic_building = false +VAR topic_security = false + +=== start === +#speaker:security_guard +{not player_warned: + #display:guard-patrol + The guard looks up as you approach. + Guard: This is a restricted area. What's your business here? + ~ player_warned = true +} +{player_warned and not guard_hostile: + #display:guard-neutral + Guard: Back again? +} +{guard_hostile: + #display:guard-hostile + Guard: I told you to leave. Now. + #exit_conversation + -> END +} +-> hub + +=== hub === ++ {not topic_building} [Ask about building layout] + -> ask_building ++ {not topic_security} [Ask about security protocols] + -> ask_security ++ {influence >= 20} [Request access] + -> request_access ++ [Leave conversation] + #exit_conversation + #speaker:security_guard + Guard: Stay out of trouble. + -> END + +=== ask_building === +#speaker:security_guard +~ topic_building = true +~ influence += 5 + +Guard: [Provides general information about layout] + +{influence >= 15: + Guard: [Additional helpful detail] +} +-> hub + +=== ask_security === +#speaker:security_guard +~ topic_security = true +~ influence += 5 + +Guard: Standard protocols. Nothing you need to worry about. + +{influence >= 25: + Guard: Though... [reveals minor security gap] +} +-> hub + +=== request_access === +#speaker:security_guard +{influence >= 30: + ~ influence -= 10 + Guard: Alright, I'll let you through. But make it quick. + #exit_conversation + -> END +- else: + ~ influence -= 5 + Guard: Sorry, can't do that. Security protocols. + -> hub +} + +// Event-triggered knot (called by game when guard sees lockpicking) +=== on_lockpick_detected === +#speaker:security_guard +~ guard_hostile = true +~ influence = 0 + +#display:guard-hostile +Guard: HEY! What are you doing with that lock?! + +#hostile:security_guard +This is your only warning - GET OUT! + +#exit_conversation +-> END +``` + +#### Phone NPCs (Remote Support) + +Phone NPCs provide hints and react to player progress via events: + +```ink +// =========================================== +// ACT 2 PHONE NPC: Handler Support +// =========================================== + +VAR hint_lockpicking_given = false +VAR hint_password_given = false +VAR rooms_discovered = 0 + +=== start === +#speaker:handler_support +Handler: {player_name}, checking in. How's it going? + ++ [Request hint] + -> provide_hint ++ [Report progress] + -> report_progress ++ [End call] + #exit_conversation + Handler: Stay safe out there. + -> END + +=== provide_hint === +#speaker:handler_support + +{not hint_lockpicking_given: + Handler: If you have a lockpick kit, you can bypass key locks. + ~ hint_lockpicking_given = true + -> start +} +{not hint_password_given: + Handler: Check computers and notes for password clues. + ~ hint_password_given = true + -> start +} +{hint_lockpicking_given and hint_password_given: + Handler: You're doing fine. Trust your training. + -> start +} + +// Event-triggered: Called when player picks up lockpick +=== on_lockpick_pickup === +#speaker:handler_support +Handler: Good find. That lockpick will let you bypass key locks. +Handler: Remember, lockpicking takes time and makes noise. +-> END + +// Event-triggered: Called when player completes lockpick minigame +=== on_lockpick_success === +#speaker:handler_support +Handler: Nice work on that lock. Smooth technique. +-> END + +// Event-triggered: Called when player enters new room +=== on_room_discovered === +#speaker:handler_support +~ rooms_discovered += 1 + +{rooms_discovered == 1: + Handler: Good, you're making progress. Stay alert. +} +{rooms_discovered == 3: + Handler: You're covering ground quickly. Don't miss anything important. +} +{rooms_discovered >= 5: + Handler: Thorough work. ENTROPY's trail should be getting clearer. +} +-> END +``` + +#### Event Mapping (in JSON) + +Connect Ink knots to game events: + +```json +"eventMappings": [ + { + "eventPattern": "item_picked_up:lockpick", + "targetKnot": "on_lockpick_pickup", + "onceOnly": true + }, + { + "eventPattern": "minigame_completed", + "targetKnot": "on_lockpick_success", + "condition": "data.minigameName && data.minigameName.includes('Lockpick')", + "cooldown": 10000 + }, + { + "eventPattern": "room_discovered", + "targetKnot": "on_room_discovered", + "cooldown": 15000, + "maxTriggers": 5 + } +] +``` + +### Step 4: Write Act 3 - Closing Cutscene + +Act 3 should: +1. Acknowledge player's performance +2. Callback to Act 1 choices +3. Show consequences +4. Provide narrative closure +5. Potentially set up future stories + +#### Closing Cutscene Template + +```ink +// =========================================== +// ACT 3: CLOSING CUTSCENE +// =========================================== + +// Variables from Act 1 (carried forward) +EXTERNAL player_approach +EXTERNAL handler_trust +EXTERNAL knows_full_stakes +EXTERNAL mission_priority + +// Variables from Act 2 (set by game) +EXTERNAL objectives_completed +EXTERNAL lore_collected +EXTERNAL stealth_rating +EXTERNAL time_taken + +=== start === +[Location: SAFETYNET Debrief Room] + +#speaker:handler_[name] + +{objectives_completed >= 4: + -> full_success_debrief +} +{objectives_completed >= 2: + -> partial_success_debrief +} +{objectives_completed < 2: + -> minimal_success_debrief +} + +// =========================================== +// FULL SUCCESS PATH +// =========================================== + +=== full_success_debrief === +Handler: Excellent work, {player_name}. All primary objectives completed. + +{player_approach == "cautious": + Handler: Your methodical approach paid off. Nothing was missed. +} +{player_approach == "aggressive": + Handler: You moved fast and got results. Well executed. +} +{player_approach == "diplomatic": + Handler: Your adaptability was key. You read the situation perfectly. +} + +-> mission_details + +=== mission_details === +Handler: [Summary of what was accomplished] + +{knows_full_stakes: + Handler: And yes, you prevented [the stakes from Act 1]. Those civilians are safe because of you. +} + +// Check for Act 1 choices and reference them +{handler_trust >= 60: + Handler: I had confidence in you from the start. You've proven that trust was well-placed. +} +{handler_trust < 40: + Handler: I'll admit, I had doubts. But you came through when it mattered. +} + +-> entropy_status + +=== entropy_status === +Handler: As for [ENTROPY cell]... + +{stealth_rating > 80: + Handler: They didn't even know you were there until it was too late. Masterful. +} +{stealth_rating > 50: + Handler: They knew someone was interfering, but couldn't stop you. +} +{stealth_rating <= 50: + Handler: You made some noise, but got the job done. That's what counts. +} + +[Specific information about ENTROPY cell's status] +[Did they escape? Get caught? What did we learn?] + +-> lore_discussion + +=== lore_discussion === +{lore_collected >= 8: + Handler: I see you found extensive intelligence. Analysis team is already going through it. + Handler: [Tease what LORE revealed about larger plot] + -> consequences +} +{lore_collected >= 4: + Handler: You gathered some useful intelligence. It's filling in our picture of their network. + -> consequences +} +{lore_collected < 4: + Handler: We got the primary objective, though more intelligence would have been helpful. + -> consequences +} + +=== consequences === +Handler: This operation has implications for [larger context]. + +[Explain broader impact] +[Set up potential future threads] + +* [Ask what happens next] + You: What's SAFETYNET's next move? + Handler: [Future operations hint] + -> debrief_end + +* [Express concern about loose ends] + You: [Express specific concern about unresolved elements] + Handler: [Acknowledgment and context] + -> debrief_end + +* [Accept mission closure] + -> debrief_end + +=== debrief_end === +Handler: Get some rest, {player_name}. You've earned it. + +{handler_trust >= 70: + Handler: And... good work. Really. We're lucky to have you. +} + +[Fade to mission complete screen] + +-> END + +// =========================================== +// PARTIAL SUCCESS PATH +// =========================================== + +=== partial_success_debrief === +Handler: Mission complete, {player_name}, though we didn't get everything. + +Handler: [What was accomplished] + +Handler: [What was missed and why it matters] + +{player_approach == "aggressive" and time_taken < 1800: + Handler: Speed was prioritized. Sometimes that means missing details. +} + +-> entropy_status + +// =========================================== +// MINIMAL SUCCESS PATH +// =========================================== + +=== minimal_success_debrief === +Handler: You completed the core objective, but... + +Handler: [Acknowledge accomplishment] + +Handler: [Note significant gaps] + +Handler: We'll need to follow up on what was missed. + +-> entropy_status +``` + +#### Act 3 Best Practices + +1. **Acknowledge everything** - Choices, performance, approach +2. **Show don't tell consequences** - Reference specific outcomes +3. **Vary endings** - Multiple variants based on performance +4. **Emotional payoff** - Match the tone to the outcome +5. **Close loops** - Answer questions raised in Acts 1 and 2 +6. **Plant seeds** - Optional: hint at future scenarios + +### Step 5: Ink Technical Best Practices + +#### Use Tags for Game Integration + +```ink +#speaker:character_name // Sets active speaker +#display:mood_state // Changes NPC visual state +#exit_conversation // Closes dialogue +#hostile:npc_id // Marks NPC as hostile +#patrol_mode:on // Enables NPC patrol +#patrol_mode:off // Disables NPC patrol +#start_gameplay // Transitions from cutscene to gameplay +``` + +#### Variable Naming Conventions + +```ink +// Choice tracking +VAR player_choice_mission_approach = "" + +// State tracking +VAR npc_trust_level = 0 +VAR knows_secret = false + +// Topic tracking (for hub pattern) +VAR topic_discussed_security = false +VAR topic_discussed_building = false + +// External variables (set by game) +EXTERNAL player_name +EXTERNAL objectives_completed +``` + +#### Hub Pattern (Recommended for Conversations) + +```ink +=== hub === ++ {condition1} [Choice 1] + -> branch1 ++ {condition2} [Choice 2] + -> branch2 ++ [Always available choice] + -> branch3 ++ [Exit conversation] + #exit_conversation + -> END + +=== branch1 === +Content here... +-> hub // Return to hub + +=== branch2 === +Content here... +-> hub + +=== branch3 === +Content here... +-> hub +``` + +### Step 6: Testing and Validation + +#### Test in Inky Editor + +1. Load each .ink file in Inky +2. Test all branches +3. Verify variables update correctly +4. Check that all diverts point to existing knots +5. Confirm tags are properly formatted + +#### Common Ink Errors + +```ink +// ERROR: Missing === +start === // Wrong +=== start === // Correct + +// ERROR: Unclosed braces +{trust_level >= 3: + Text here // Missing closing brace + +{trust_level >= 3: + Text here +} // Correct + +// ERROR: Missing -> before divert +Trust increased +hub // Wrong + +Trust increased +-> hub // Correct + +// ERROR: Typo in variable name +~ trust_levl += 1 // Creates new variable! +~ trust_level += 1 // Correct +``` + +#### Testing Checklist + +- [ ] All Ink files compile without errors in Inky +- [ ] All choice branches are reachable +- [ ] All conditional logic works correctly +- [ ] Variables are set and checked correctly +- [ ] All diverts point to existing knots +- [ ] Tags are properly formatted +- [ ] Character voices are distinct +- [ ] Dialogue flows naturally when read aloud +- [ ] Act 1 choices are referenced in Act 3 +- [ ] Event-triggered knots exist for all event mappings + +--- + +## Output Format + +```markdown +# Ink Scripts: [Scenario Name] + +## File Structure +- `[scenario]_opening.ink` - Act 1 opening cutscene +- `[scenario]_npc_guard.ink` - Security guard NPC +- `[scenario]_phone_handler.ink` - Handler phone contact +- `[scenario]_closing.ink` - Act 3 closing cutscene + +## Variables Reference + +### Act 1 Variables (Opening Cutscene) +- `player_approach` - cautious/aggressive/diplomatic +- `handler_trust` - 0-100 trust level +- `knows_full_stakes` - boolean +- `mission_priority` - speed/stealth/thoroughness + +### Act 2 Variables (NPC Dialogues) +- `guard_influence` - 0-100 persuasion level +- `topic_*` - boolean flags for conversation topics + +### External Variables (Set by Game) +- `player_name` - Player's display name +- `objectives_completed` - Number of completed objectives +- `lore_collected` - Number of LORE fragments found +- `stealth_rating` - 0-100 stealth performance + +## Integration Notes +[How Ink integrates with game systems] + +## Testing Results +[What was tested and outcomes] +``` --- Save your Ink scripts as: ``` -scenario_designs/[scenario_name]/07_ink/opening_cutscene.ink -scenario_designs/[scenario_name]/07_ink/closing_cutscene.ink -scenario_designs/[scenario_name]/07_ink/npc_dialogues.ink -scenario_designs/[scenario_name]/07_ink/choice_moments.ink +scenarios/ink/[scenario_name]_opening.ink +scenarios/ink/[scenario_name]_npc_*.ink +scenarios/ink/[scenario_name]_phone_*.ink +scenarios/ink/[scenario_name]_closing.ink ``` **Next Stage:** Pass complete scripts to Stage 8 (Review) for final validation. diff --git a/story_design/story_dev_prompts/FEATURES_REFERENCE.md b/story_design/story_dev_prompts/FEATURES_REFERENCE.md new file mode 100644 index 0000000..74d4adf --- /dev/null +++ b/story_design/story_dev_prompts/FEATURES_REFERENCE.md @@ -0,0 +1,924 @@ +# Break Escape: Available Features for Scenario Design + +**Purpose:** This document provides a comprehensive reference of all available game features, mechanics, and systems that scenario designers can use when creating Break Escape scenarios. + +**Audience:** AI agents, human designers, and anyone writing scenario JSON files or Ink dialogue. + +--- + +## Table of Contents + +1. [Scenario Structure](#scenario-structure) +2. [Room System](#room-system) +3. [Object Types](#object-types) +4. [Lock Types and Security](#lock-types-and-security) +5. [NPC System](#npc-system) +6. [Ink Dialogue System](#ink-dialogue-system) +7. [RFID System](#rfid-system) +8. [Player Configuration](#player-configuration) +9. [Puzzle Chains and Dependencies](#puzzle-chains-and-dependencies) +10. [Event System](#event-system) + +--- + +## Scenario Structure + +### Top-Level JSON Structure + +```json +{ + "name": "Scenario Name", + "description": "Brief description", + "scenario_brief": "Opening text shown to player", + "endGoal": "What player is trying to accomplish", + "startRoom": "room_id", + "globalVariables": {}, + "player": { /* player configuration */ }, + "startItemsInInventory": [ /* items */ ], + "rooms": { /* room definitions */ } +} +``` + +### Key Fields + +- **`scenario_brief`**: Opening text displayed to player (supports markdown) +- **`endGoal`**: Win condition description +- **`startRoom`**: ID of the room where player spawns +- **`startItemsInInventory`**: Array of items player starts with +- **`globalVariables`**: Shared state across scenario (optional) + +--- + +## Room System + +### Room Types + +Available room types (determines visual theme): +- `room_reception` - Lobby/entrance areas +- `room_office` - Standard office spaces +- `room_ceo` - Executive offices +- `room_servers` - Server/data center rooms +- `room_closet` - Small storage areas + +### Room Structure + +```json +"room_id": { + "name": "Display Name", + "type": "room_office", + "locked": true, + "lockType": "key", + "requires": "key_id", + "door_sign": "Optional door label", + "connections": { + "north": "room_id", + "south": ["room_id1", "room_id2"], + "east": "room_id", + "west": "room_id" + }, + "npcs": [ /* NPC definitions */ ], + "objects": [ /* object definitions */ ], + "doors": [ /* explicit door configurations */ ] +} +``` + +### Connections + +- **Single direction**: `"north": "room_id"` +- **Multiple connections**: `"north": ["room_id1", "room_id2"]` +- **All four directions supported**: north, south, east, west + +### Door Configuration + +Explicit door definitions (optional, for precise control): + +```json +{ + "roomId": "current_room", + "connectedRoom": "target_room", + "direction": "north", + "x": 200, + "y": 100, + "locked": true, + "lockType": "rfid", + "requires": ["card_id"] +} +``` + +--- + +## Object Types + +### Common Object Properties + +All objects share these base properties: + +```json +{ + "type": "object_type", + "name": "Display Name", + "takeable": true, + "x": 300, // Optional: precise positioning + "y": 200, // Optional: precise positioning + "observations": "Description shown on examine" +} +``` + +### Document/Information Objects + +**Notes** +```json +{ + "type": "notes", + "name": "Document Name", + "takeable": true, + "readable": true, + "text": "Content of the note", + "note_title": "Optional title", + "note_content": "Formatted content (supports markdown)", + "important": true, // Optional: highlight important items + "isEndGoal": true, // Optional: marks scenario completion + "observations": "Description" +} +``` + +**Phone (voicemail/messages)** +```json +{ + "type": "phone", + "name": "Phone Name", + "takeable": false, + "readable": true, + "voice": "Message content", + "text": "Alternative text content", + "sender": "Sender name", + "timestamp": "Time/date", + "phoneId": "phone_id", // For linking to NPC contacts + "npcIds": ["npc1", "npc2"], // NPCs accessible via this phone + "observations": "Description" +} +``` + +### Computer/Terminal Objects + +**PC/Computer** +```json +{ + "type": "pc", + "name": "Computer Name", + "takeable": false, + "locked": true, + "lockType": "password", + "requires": "password_string", + "showKeyboard": true, // Show on-screen keyboard + "passwordHint": "Optional hint", + "showHint": true, + "maxAttempts": 3, + "postitNote": "Password: secret", // Visible password hint + "showPostit": true, + "hasFingerprint": true, // Can be unlocked with fingerprints + "fingerprintOwner": "ceo", + "fingerprintDifficulty": "medium", + "contents": [ /* objects inside */ ], + "observations": "Description" +} +``` + +**Tablet** +```json +{ + "type": "tablet", + "name": "Tablet Device", + "takeable": true, + "locked": true, + "lockType": "bluetooth", + "requires": "bluetooth", + "mac": "00:11:22:33:44:55", + "observations": "Description" +} +``` + +### Security/Tool Objects + +**Key** +```json +{ + "type": "key", + "name": "Key Name", + "takeable": true, + "key_id": "unique_key_id", + "keyPins": [100, 0, 100, 0], // For lockpicking minigame + "observations": "Description" +} +``` + +**Lockpick Kit** +```json +{ + "type": "lockpick", + "name": "Lock Pick Kit", + "takeable": true, + "observations": "Enables lockpicking minigame" +} +``` + +**Fingerprint Kit** +```json +{ + "type": "fingerprint_kit", + "name": "Fingerprint Kit", + "takeable": true, + "observations": "Collects fingerprints from surfaces" +} +``` + +**PIN Cracker** +```json +{ + "type": "pin-cracker", + "name": "PIN Cracker", + "takeable": true, + "observations": "Provides feedback on PIN entry attempts" +} +``` + +**Bluetooth Scanner** +```json +{ + "type": "bluetooth_scanner", + "name": "Bluetooth Scanner", + "takeable": true, + "canScanBluetooth": true, + "observations": "Detects nearby Bluetooth signals" +} +``` + +**RFID Cloner** +```json +{ + "type": "rfid_cloner", + "name": "RFID Flipper", + "takeable": true, + "saved_cards": [], + "observations": "Scans and emulates RFID cards" +} +``` + +**Keycard (RFID)** +```json +{ + "type": "keycard", + "name": "Access Card", + "card_id": "unique_card_id", + "rfid_protocol": "EM4100", + "takeable": true, + "observations": "Description" +} +``` + +**Workstation** +```json +{ + "type": "workstation", + "name": "Analysis Station", + "takeable": true, + "observations": "Powerful computer for analysis tasks" +} +``` + +### Container Objects + +**Safe** +```json +{ + "type": "safe", + "name": "Safe Name", + "takeable": false, + "locked": true, + "lockType": "pin", + "requires": "9573", + "contents": [ /* objects inside */ ], + "observations": "Description" +} +``` + +**Suitcase/Briefcase** +```json +{ + "type": "suitcase", + "name": "Briefcase", + "takeable": false, + "locked": true, + "lockType": "key", + "requires": "key_id", + "keyPins": [50, 25, 0, 75], + "difficulty": "medium", + "contents": [ /* objects inside */ ], + "observations": "Description" +} +``` + +--- + +## Lock Types and Security + +### Available Lock Types + +1. **`key`** - Requires specific key item + - `requires`: key_id (string) + - `keyPins`: [array of 4 numbers] for lockpicking minigame + - `difficulty`: "easy", "medium", "hard" + +2. **`pin`** - 4-digit PIN code + - `requires`: "1234" (4-digit string) + - Can use PIN cracker for hints + +3. **`password`** - Text password + - `requires`: "password_string" + - `showKeyboard`: true (shows on-screen keyboard) + - `maxAttempts`: number (optional) + +4. **`bluetooth`** - Bluetooth pairing + - `requires`: "bluetooth" + - `mac`: "MAC address" (for bluetooth scanner) + +5. **`rfid`** - RFID card access + - `requires`: ["card_id"] or "card_id" + - Works with RFID cloner + +6. **`lockpick`** - Lockpicking minigame + - Uses keyPins array + - Requires lockpick tool in inventory + +### Lockpicking System + +Doors and containers with `lockType: "key"` can be picked: + +```json +{ + "locked": true, + "lockType": "key", + "requires": "office_key", + "keyPins": [100, 0, 100, 0], // Pin heights for minigame + "difficulty": "easy" // easy/medium/hard +} +``` + +- **keyPins**: Array of 4 numbers (0-150) representing pin heights +- **difficulty**: Affects time window and feedback + +--- + +## NPC System + +### NPC Types + +1. **`person`** - Physical NPC in the game world +2. **`phone`** - Virtual NPC accessible via phone item + +### Physical NPC Structure + +```json +{ + "id": "npc_id", + "displayName": "NPC Name", + "npcType": "person", + "position": { "x": 3, "y": 3 }, + "spriteSheet": "hacker", + "spriteTalk": "assets/characters/hacker-talk.png", + "spriteConfig": { + "idleFrameStart": 20, + "idleFrameEnd": 23 + }, + "storyPath": "scenarios/ink/dialogue.json", + "currentKnot": "start", + "avatar": "assets/npc/avatars/npc_neutral.png", + "behavior": { + "facePlayer": true, + "facePlayerDistance": 96, + "patrol": { + "enabled": true, + "speed": 100, + "changeDirectionInterval": 3000, + "bounds": { + "x": 64, + "y": 64, + "width": 192, + "height": 192 + } + } + }, + "rfidCard": { + "card_id": "employee_badge", + "rfid_protocol": "EM4100", + "name": "Employee Badge" + }, + "eventMappings": [ /* event triggers */ ] +} +``` + +### Phone NPC Structure + +```json +{ + "id": "contact_id", + "displayName": "Contact Name", + "storyPath": "scenarios/ink/dialogue.json", + "avatar": "assets/npc/avatars/npc_helper.png", + "phoneId": "player_phone", + "currentKnot": "start", + "npcType": "phone", + "timedMessages": [ + { + "delay": 5000, + "message": "Hey! Got any updates?", + "type": "text" + } + ], + "eventMappings": [ /* event triggers */ ] +} +``` + +### NPC Behaviors + +**Face Player** +```json +"behavior": { + "facePlayer": true, + "facePlayerDistance": 96 // Distance in pixels (96 = 3 tiles) +} +``` + +**Patrol** +```json +"patrol": { + "enabled": true, + "speed": 100, // Pixels per second + "changeDirectionInterval": 3000, // Milliseconds + "bounds": { + "x": 64, // Top-left X + "y": 64, // Top-left Y + "width": 192, // Width in pixels + "height": 192 // Height in pixels + } +} +``` + +### Sprite Sheets + +Available sprite sheets: +- `hacker` - Default agent sprite +- `hacker-red` - Red variant +- `hacker-green` - Green variant +- `hacker-yellow` - Yellow variant + +### RFID Cards on NPCs + +NPCs can carry RFID cards that can be scanned: + +```json +"rfidCard": { + "card_id": "employee_badge", + "rfid_protocol": "EM4100", + "name": "Employee Badge" +} +``` + +--- + +## Ink Dialogue System + +### Basic Structure + +```ink +VAR trust_level = 0 +VAR knows_secret = false + +=== start === +#speaker:npc_name +Hello! How can I help you? +-> hub + +=== hub === ++ [Ask about security] + -> topic_security ++ [Say goodbye] + #exit_conversation + See you later! + -> END +``` + +### Ink Tags (Special Commands) + +**Speaker** +```ink +#speaker:npc_name +``` +Sets who is speaking (for avatar display) + +**Display/Mood** +```ink +#display:guard-patrol +#display:guard-confrontation +#display:guard-hostile +``` +Controls NPC visual state/mood + +**Exit Conversation** +```ink +#exit_conversation +``` +Closes dialogue window + +**Hostility** +```ink +#hostile:npc_id +``` +Marks NPC as hostile (affects gameplay) + +**Patrol Control** +```ink +#patrol_mode:on +#patrol_mode:off +``` +Toggles NPC patrol behavior + +### Variables + +**Declare at top:** +```ink +VAR variable_name = 0 +VAR boolean_flag = false +``` + +**Modify:** +```ink +~ trust_level += 1 +~ knows_secret = true +``` + +**Conditional:** +```ink +{trust_level >= 3: + You seem trustworthy. +- else: + I don't know you well enough. +} +``` + +### Choices + +**Basic:** +```ink +* [Choice text] + Response text + -> next_knot +``` + +**Conditional:** +```ink ++ {trust_level >= 2} [Restricted choice] + -> privileged_content +``` + +**Once-only:** +```ink ++ {not topic_discussed} [New topic] + ~ topic_discussed = true + -> topic_knot +``` + +### Hub Pattern (Recommended) + +```ink +=== hub === ++ {condition1} [Choice 1] + -> branch1 ++ {condition2} [Choice 2] + -> branch2 ++ [Always available] + -> branch3 + +=== branch1 === +Content... +-> hub // Return to hub +``` + +--- + +## RFID System + +### RFID Protocols + +Four security levels from weakest to strongest: + +1. **EM4100** (125kHz) - Instant clone + ```json + "rfid_protocol": "EM4100" + ``` + - No encryption + - Instant cloning + - Used in old hotels, parking garages + +2. **MIFARE Classic (Weak Defaults)** - Instant crack + ```json + "rfid_protocol": "MIFARE_Classic_Weak_Defaults" + ``` + - Uses factory default keys + - Dictionary attack succeeds instantly + - Used in cheap hotels, old transit cards + +3. **MIFARE Classic (Custom Keys)** - Requires attack (~30 sec) + ```json + "rfid_protocol": "MIFARE_Classic_Custom_Keys" + ``` + - Custom encryption keys + - Requires Darkside attack (30 seconds) + - Used in corporate badges, banks + +4. **MIFARE DESFire** - UID emulation only + ```json + "rfid_protocol": "MIFARE_DESFire" + ``` + - Military-grade AES encryption + - Cannot be cracked + - Only UID emulation works (if reader is poorly configured) + - Used in government, military, high-security + +### RFID Workflow + +1. Player needs RFID cloner in inventory +2. Player approaches NPC with RFID card or scans keycard object +3. Player uses cloner to scan/attack card +4. Player emulates card at RFID reader/door + +--- + +## Player Configuration + +```json +"player": { + "id": "player", + "displayName": "Agent Name", + "spriteSheet": "hacker", + "spriteTalk": "assets/characters/hacker-talk.png", + "spriteConfig": { + "idleFrameStart": 20, + "idleFrameEnd": 23 + }, + "startX": 200, + "startY": 200 +} +``` + +--- + +## Puzzle Chains and Dependencies + +### Example: Multi-Step Puzzle Chain + +``` +1. Read note in Reception + ↓ +2. Get password from note + ↓ +3. Unlock PC in Office + ↓ +4. Read file on PC to get PIN code + ↓ +5. Use PIN to unlock Safe + ↓ +6. Get key from Safe + ↓ +7. Use key to unlock CEO office + ↓ +8. Find final evidence +``` + +### Implementation Pattern + +```json +{ + "rooms": { + "reception": { + "objects": [ + { + "type": "notes", + "text": "PIN code is 7391", + "takeable": true + } + ], + "connections": { + "north": "office" + } + }, + "office": { + "locked": true, + "lockType": "pin", + "requires": "7391", + "objects": [ + { + "type": "key", + "key_id": "final_key", + "takeable": true + } + ] + } + } +} +``` + +### Dependency Types + +1. **Information Dependencies**: Clue → Solution +2. **Tool Dependencies**: Need tool to interact with object +3. **Access Dependencies**: Need key/card/password to access area +4. **Sequential Dependencies**: A → B → C must be done in order + +--- + +## Event System + +### Event Mappings (for Phone NPCs) + +```json +"eventMappings": [ + { + "eventPattern": "item_picked_up:lockpick", + "targetKnot": "on_lockpick_pickup", + "onceOnly": true, + "cooldown": 0 + }, + { + "eventPattern": "minigame_completed", + "targetKnot": "on_lockpick_success", + "condition": "data.minigameName && data.minigameName.includes('Lockpick')", + "cooldown": 10000 + }, + { + "eventPattern": "door_unlocked", + "targetKnot": "on_door_unlocked", + "cooldown": 8000 + }, + { + "eventPattern": "room_entered:ceo", + "targetKnot": "on_ceo_office_entered", + "onceOnly": true + } +] +``` + +### Available Event Patterns + +- `item_picked_up:item_type` - When specific item is picked up +- `item_picked_up:*` - When any item is picked up +- `minigame_completed` - When minigame succeeds +- `minigame_failed` - When minigame fails +- `door_unlocked` - When any door is unlocked +- `door_unlock_attempt` - When door unlock is attempted +- `object_interacted` - When object is interacted with +- `room_entered` - When any room is entered +- `room_entered:room_id` - When specific room is entered +- `room_discovered` - When new room is discovered + +### Event Properties + +- **eventPattern**: Pattern to match (supports wildcards) +- **targetKnot**: Which Ink knot to jump to +- **condition**: JavaScript condition (optional) +- **onceOnly**: Only trigger once (default: false) +- **cooldown**: Milliseconds before can trigger again +- **maxTriggers**: Maximum number of times can trigger + +--- + +## Best Practices + +### Puzzle Design + +1. **Provide multiple clues** - Don't rely on single puzzle solution path +2. **Show don't tell** - Let players discover rather than being told +3. **Progressive difficulty** - Start easy, build complexity +4. **Clear feedback** - Let players know when they're on right track +5. **Avoid dead ends** - Always provide way forward + +### Narrative Integration + +1. **Story justifies puzzles** - Why are these obstacles here? +2. **NPCs provide hints** - Dialogue should guide without spoiling +3. **Environmental storytelling** - Objects and notes tell story +4. **Consistent world** - All elements support the scenario theme + +### Technical Constraints + +1. **Room connections** - Ensure all connected rooms are defined +2. **Object dependencies** - Verify all required items exist +3. **Lock consistency** - requires field must match available keys/codes +4. **NPC bounds** - Patrol bounds must fit within room +5. **Sprite references** - Use available sprite sheets only + +### Performance + +1. **Limit NPCs per room** - Max 8-10 NPCs per room +2. **Optimize patrol areas** - Smaller bounds = better performance +3. **Event cooldowns** - Prevent event spam with appropriate cooldowns + +--- + +## Example: Complete Mini-Scenario + +```json +{ + "scenario_brief": "Infiltrate the office and recover the stolen data.", + "startRoom": "lobby", + "startItemsInInventory": [ + { + "type": "lockpick", + "name": "Lock Pick Kit", + "takeable": true + } + ], + "rooms": { + "lobby": { + "type": "room_reception", + "connections": { + "north": "office" + }, + "objects": [ + { + "type": "notes", + "name": "Security Code", + "takeable": true, + "readable": true, + "text": "Office safe code: 9573" + } + ] + }, + "office": { + "type": "room_office", + "locked": true, + "lockType": "key", + "requires": "office_key", + "keyPins": [100, 50, 0, 150], + "connections": { + "south": "lobby" + }, + "objects": [ + { + "type": "safe", + "name": "Wall Safe", + "takeable": false, + "locked": true, + "lockType": "pin", + "requires": "9573", + "contents": [ + { + "type": "notes", + "name": "Stolen Data", + "isEndGoal": true, + "readable": true, + "text": "You found the stolen data! Mission complete!" + } + ] + } + ] + } + } +} +``` + +--- + +## Quick Reference Tables + +### Lock Type Summary + +| Lock Type | Requires | Tools Needed | Difficulty | +|-----------|----------|--------------|------------| +| key | key_id | Key or Lockpick | Varies | +| pin | "1234" | None (or PIN cracker for hints) | Easy | +| password | "password" | None | Easy | +| bluetooth | "bluetooth" | Bluetooth Scanner | Medium | +| rfid | card_id | RFID Cloner | Varies by protocol | +| lockpick | None | Lockpick Kit | Varies | + +### Object Type Summary + +| Type | Takeable | Containers | Readable | Locked | +|------|----------|------------|----------|--------| +| notes | Usually | No | Yes | No | +| phone | No | No | Yes | No | +| pc | No | Yes | Yes | Usually | +| tablet | Yes | No | No | Usually | +| key | Yes | No | No | No | +| safe | No | Yes | No | Yes | +| suitcase | No | Yes | No | Usually | +| lockpick | Yes | No | No | No | +| rfid_cloner | Yes | No | No | No | +| keycard | Yes | No | No | No | + +--- + +**Last Updated:** 2025-01-17 +**Version:** 1.0 +**For Questions:** See `docs/GAME_DESIGN.md` or `docs/ROOM_GENERATION.md`