From 40da85dac446bd430396b1737ec568348e4d821e Mon Sep 17 00:00:00 2001 From: "Z. Cliffe Schreuders" Date: Fri, 31 Oct 2025 16:31:07 +0000 Subject: [PATCH] feat(npc): Enhance NPC interaction with room navigation events and refined greeting logic --- docs/NPC_INTEGRATION_GUIDE.md | 76 ++++++++++++++----- js/core/rooms.js | 56 +++++++++++++- .../npc/progress/01_IMPLEMENTATION_LOG.md | 47 ++++++++++-- scenarios/ceo_exfil.json | 6 ++ scenarios/ink/helper-npc.ink | 35 +++++++-- scenarios/ink/helper-npc.json | 2 +- 6 files changed, 190 insertions(+), 32 deletions(-) diff --git a/docs/NPC_INTEGRATION_GUIDE.md b/docs/NPC_INTEGRATION_GUIDE.md index 2baf138..ed352fc 100644 --- a/docs/NPC_INTEGRATION_GUIDE.md +++ b/docs/NPC_INTEGRATION_GUIDE.md @@ -121,14 +121,22 @@ VAR has_given_item = false ### 2. Define the Entry Points -Every Ink story needs two key knots: +Every Ink story needs a properly structured start flow to avoid repeating messages: ```ink -// Initial greeting - shown only on first contact +// State variable to track if greeting has been shown +VAR has_greeted = false + +// Initial entry point - shows greeting once, then goes to menu === start === -Hello! I'm here to help you with your mission. πŸ‘‹ -How are things going? --> main_menu +{ has_greeted: + -> main_menu +- else: + Hello! I'm here to help you with your mission. πŸ‘‹ + How are things going? + ~ has_greeted = true + -> main_menu +} // Main menu - shown when returning to conversation === main_menu === @@ -137,10 +145,20 @@ How are things going? + [Say goodbye] -> goodbye ``` -**Key Pattern**: -- `start` shows the initial greeting message and immediately redirects to `main_menu` -- `main_menu` presents only choices (no repeated text) since all messages are in conversation history -- All conversation knots should redirect to `main_menu` (not `start`) to avoid repeating the greeting +**Key Pattern for Avoiding Repeated Messages**: +- Add a `has_greeted` variable at the top of your Ink file +- `start` knot checks if greeting has been shown: + - If already greeted, skip directly to `main_menu` + - If not greeted, show greeting text, set `has_greeted = true`, then go to `main_menu` +- `main_menu` presents only choices (no text) since conversation history shows all previous messages +- All conversation knots redirect to `main_menu` (not `start`) to avoid re-triggering the greeting +- Event-triggered barks also redirect to `main_menu` for seamless conversation continuation + +**Why This Works**: +- First contact: Player sees greeting, then choices +- After barks: Player sees bark message (in history), then choices - no repeated greeting +- Reopening conversation: Player sees full history, then choices - no repeated greeting +- The greeting appears in conversation history but never repeats as a new message --- @@ -248,6 +266,21 @@ Excellent work on that challenge! 🎯 - ❌ Using `-> END` - prevents conversation from continuing - ❌ Long messages - barks should be brief notifications +**Important: Avoiding Repeated Greetings** + +When a bark redirects to `main_menu` (not `start`), the conversation flow works like this: + +1. Player performs action (e.g., enters a room) +2. Bark notification appears with contextual message +3. Player clicks the bark to open conversation +4. Conversation shows: + - Original greeting (in history) + - Bark message (just clicked) + - Menu choices (from `main_menu`) +5. **No repeated greeting** because we skipped the `start` knot + +If barks redirect to `start`, the `has_greeted` check prevents re-showing the greeting text, but it's cleaner to go straight to `main_menu`. + --- ## Configuring NPCs in Scenario JSON @@ -444,10 +477,12 @@ The JSON file is what gets loaded by the game engine. ### Conversation Design 1. **Use clear variable names**: `trust_level`, `has_given_keycard`, `knows_secret` -2. **Gate important actions behind trust**: Players should build rapport before getting help -3. **Provide multiple conversation paths**: Not everyone plays the same way -4. **Use emojis sparingly**: They add personality but shouldn't overwhelm -5. **Keep initial greeting brief**: Players want to get to choices quickly +2. **Always add `has_greeted` variable**: Prevents repeated greetings across sessions +3. **Gate important actions behind trust**: Players should build rapport before getting help +4. **Provide multiple conversation paths**: Not everyone plays the same way +5. **Use emojis sparingly**: They add personality but shouldn't overwhelm +6. **Keep initial greeting brief**: Players want to get to choices quickly +7. **Check `has_greeted` in start knot**: Skip directly to `main_menu` if already greeted ### Bark Design @@ -555,11 +590,17 @@ VAR trust_level = 0 VAR has_given_advice = false VAR mission_briefed = false VAR rooms_discovered = 0 +VAR has_greeted = false === start === -Hey, I'm glad you're on this case. This is going to be tricky. πŸ•΅οΈ -Let me know if you need guidance. --> main_menu +{ has_greeted: + -> main_menu +- else: + Hey, I'm glad you're on this case. This is going to be tricky. πŸ•΅οΈ + Let me know if you need guidance. + ~ has_greeted = true + -> main_menu +} === main_menu === + [What should I be looking for?] -> mission_briefing @@ -705,7 +746,8 @@ When integrating a new NPC: **Ink Story Creation**: - [ ] Create `.ink` file in `scenarios/ink/` - [ ] Define state variables at top of file -- [ ] Create `start` knot with initial greeting +- [ ] Add `has_greeted` variable to prevent repeated greetings +- [ ] Create `start` knot with greeting + `has_greeted` check - [ ] Create `main_menu` knot with choices (no repeated text) - [ ] Create conversation knots that redirect to `main_menu` - [ ] Create event-triggered bark knots (also redirect to `main_menu`) diff --git a/js/core/rooms.js b/js/core/rooms.js index 926ef08..af18373 100644 --- a/js/core/rooms.js +++ b/js/core/rooms.js @@ -53,6 +53,11 @@ import { initializeCollision, createWallCollisionBoxes, removeTilesUnderDoor, re export let rooms = {}; export let currentRoom = ''; export let currentPlayerRoom = ''; +// Track which rooms have been DISCOVERED by the player +// NOTE: "Discovered" means the player has ENTERED the room via door transition. +// This is separate from "revealed" (graphics visible). Rooms can be revealed +// (loaded for graphics/performance) without being discovered (player hasn't entered yet). +// This distinction is important for NPC event triggers like "room_discovered". export let discoveredRooms = new Set(); // Helper function to check if a position overlaps with existing items @@ -516,6 +521,10 @@ function loadRoom(roomId) { console.log(`Lazy loading room: ${roomId}`); createRoom(roomId, roomData, position); + + // Reveal (make visible) but do NOT mark as discovered + // The room will only be marked as "discovered" when the player + // actually enters it via door transition revealRoom(roomId); } @@ -527,6 +536,9 @@ export function initializeRooms(gameInstance) { currentRoom = ''; currentPlayerRoom = ''; window.currentPlayerRoom = ''; + + // Clear discovered rooms on scenario load + // This ensures "first visit" detection works correctly for NPC events discoveredRooms = new Set(); // Update global reference window.discoveredRooms = discoveredRooms; @@ -1603,6 +1615,19 @@ export function createRoom(roomId, roomData, position) { } export function revealRoom(roomId) { + // IMPORTANT: revealRoom() makes graphics VISIBLE but does NOT mark as DISCOVERED + // + // "Revealed" = graphics are loaded and visible (for rendering/performance) + // "Discovered" = player has actually ENTERED the room (for gameplay/events) + // + // This separation allows us to: + // 1. Preload/reveal rooms for performance without marking them as "visited" + // 2. Trigger "room_discovered" events when player first ENTERS a room + // 3. Keep "first visit" detection accurate for NPC reactions + // + // Rooms are marked as "discovered" in the door transition code, AFTER + // the room_discovered event is emitted. + if (rooms[roomId]) { const room = rooms[roomId]; @@ -1637,9 +1662,10 @@ export function revealRoom(roomId) { console.log(`No objects found in room ${roomId}`); } - discoveredRooms.add(roomId); - // Update global reference - window.discoveredRooms = discoveredRooms; + // NOTE: We do NOT add to discoveredRooms here! + // Rooms are only marked as "discovered" when the player actually enters them + // via door transition. This allows revealRoom() to be used for preloading/visibility + // without affecting the "first visit" detection for NPC events. } currentRoom = roomId; } @@ -1662,13 +1688,23 @@ export function updatePlayerRoom() { currentPlayerRoom = doorTransitionRoom; window.currentPlayerRoom = doorTransitionRoom; - // Reveal the room if not already discovered + // Check if this is the first time the player has ENTERED this room + // NOTE: The room may already be "revealed" (graphics visible) from preloading, + // but we only mark it as "discovered" when the player actually walks through + // a door into it. This keeps first-visit detection accurate for NPC events. const isFirstVisit = !discoveredRooms.has(doorTransitionRoom); + if (isFirstVisit) { + // Reveal graphics if needed (may already be revealed from preloading) revealRoom(doorTransitionRoom); } // Emit NPC event for room entry + console.log(`πŸšͺ Door transition detected: ${previousRoom} β†’ ${doorTransitionRoom}`); + console.log(` eventDispatcher exists: ${!!window.eventDispatcher}`); + console.log(` previousRoom !== doorTransitionRoom: ${previousRoom !== doorTransitionRoom}`); + console.log(` isFirstVisit: ${isFirstVisit}`); + if (window.eventDispatcher && previousRoom !== doorTransitionRoom) { console.log(`πŸšͺ Emitting room_entered event: ${doorTransitionRoom} (firstVisit: ${isFirstVisit})`); window.eventDispatcher.emit('room_entered', { @@ -1691,6 +1727,16 @@ export function updatePlayerRoom() { roomId: doorTransitionRoom, previousRoom: previousRoom }); + + // Mark as discovered AFTER emitting the event + // This is the ONLY place where rooms are added to discoveredRooms! + // By marking discovered here (not in revealRoom), we ensure: + // 1. The first door transition into a room triggers room_discovered + // 2. NPCs can react to the player's first visit + // 3. Subsequent visits don't re-trigger the event + discoveredRooms.add(doorTransitionRoom); + window.discoveredRooms = discoveredRooms; + console.log(`βœ… Marked room ${doorTransitionRoom} as discovered`); } if (previousRoom) { @@ -1699,6 +1745,8 @@ export function updatePlayerRoom() { nextRoom: doorTransitionRoom }); } + } else { + console.warn(`⚠️ NOT emitting room events - eventDispatcher: ${!!window.eventDispatcher}, previousRoom: ${previousRoom}, doorTransitionRoom: ${doorTransitionRoom}`); } // Player depth is now handled by the simplified updatePlayerDepth function in player.js diff --git a/planning_notes/npc/progress/01_IMPLEMENTATION_LOG.md b/planning_notes/npc/progress/01_IMPLEMENTATION_LOG.md index 9bde388..148b649 100644 --- a/planning_notes/npc/progress/01_IMPLEMENTATION_LOG.md +++ b/planning_notes/npc/progress/01_IMPLEMENTATION_LOG.md @@ -325,10 +325,22 @@ - [x] Verify bark frequency limits (maxTriggers) βœ… Implemented! 6. **Polish UI/UX** πŸ”„ IN PROGRESS - - [ ] Sound effects (message_received.wav, bark_notification.wav) - - [ ] Better NPC avatars (32x32px pixel art) + - [x] Room navigation events (Priority 2) βœ… COMPLETE (2024-10-31) + - [x] Added `room_entered`, `room_entered:${roomId}`, `room_discovered`, `room_exited` events + - [x] Created Ink reaction knots (on_room_discovered, on_ceo_office_entered) + - [x] Added event mappings to scenario JSON + - [x] Fixed conversation flow (main_menu vs start) + - [x] Updated unlock messages to be generic (key or lockpick) + - [ ] Sound effects (Priority 1) + - [ ] Message received sound - assets/sounds/message_received.mp3 + - [ ] NPC avatars (Priority 3) + - [ ] Create default avatars (helper, adversary, neutral) + - [ ] Add avatar support in scenarios + - [ ] More game events (Priority 2 continued) + - [ ] objective_completed + - [ ] evidence_collected + - [ ] player_detected - [ ] Objective notification system - - [ ] Secret/discovery UI - [ ] Achievement/progress tracking 7. **Performance optimization** ⏳ NEXT @@ -338,8 +350,33 @@ - [ ] Optimize bark rendering for multiple simultaneous barks --- -**Last Updated:** 2024-10-31 (Phase 4 Event-Driven Reactions COMPLETE & TESTED) -**Status:** Phase 4 Complete βœ… - Moving to Phase 5: Polish & Additional Features +**Last Updated:** 2024-10-31 (Phase 5 Room Navigation Events COMPLETE) +**Status:** Phase 5 In Progress - Room navigation events βœ…, moving to sound effects and additional events + +## Recent Improvements (2024-10-31 - Phase 5) + +### βœ… Room Navigation Events (Priority 2 - Partial) +- **Event emissions in rooms.js**: + - `room_entered` - General room change event + - `room_entered:${roomId}` - Specific room entry + - `room_discovered` - First-time room visits + - `room_exited` - Leaving a room +- **Ink reactions in helper-npc.ink**: + - `on_room_discovered` - Generic exploration encouragement + - `on_ceo_office_entered` - Special CEO office reaction with trust reward +- **Event mappings configured**: + - `room_discovered` with 15s cooldown, max 5 triggers + - `room_entered:ceo` one-time only reaction +- **Conversation flow refinements**: + - Split `start` and `main_menu` knots + - Barks redirect to `main_menu` (not `start`) to avoid repeated greeting + - "What can I do for you?" only appears in initial greeting +- **Message updates**: + - Unlock messages now generic (work for key or lockpick) + - Lockpicking success bark doesn't assume method +- **Documentation created**: + - `NPC_INTEGRATION_GUIDE.md` - Comprehensive guide for adding NPCs to scenarios + - Includes phone setup, Ink structure, event mappings, testing checklist ## Recent Improvements (2025-10-30) diff --git a/scenarios/ceo_exfil.json b/scenarios/ceo_exfil.json index ee2f403..0262ee1 100644 --- a/scenarios/ceo_exfil.json +++ b/scenarios/ceo_exfil.json @@ -75,6 +75,12 @@ "targetKnot": "on_item_found", "cooldown": 20000 }, + { + "eventPattern": "room_entered", + "targetKnot": "on_room_entered", + "cooldown": 45000, + "maxTriggers": 3 + }, { "eventPattern": "room_discovered", "targetKnot": "on_room_discovered", diff --git a/scenarios/ink/helper-npc.ink b/scenarios/ink/helper-npc.ink index f411b03..1658b4a 100644 --- a/scenarios/ink/helper-npc.ink +++ b/scenarios/ink/helper-npc.ink @@ -7,11 +7,17 @@ VAR has_unlocked_ceo = false VAR has_given_lockpick = false VAR saw_lockpick_used = false VAR saw_door_unlock = false +VAR has_greeted = false === start === -Hey there! I'm here to help you out if you need it. πŸ‘‹ -What can I do for you? --> main_menu +{ has_greeted: + -> main_menu +- else: + Hey there! I'm here to help you out if you need it. πŸ‘‹ + What can I do for you? + ~ has_greeted = true + -> main_menu +} === main_menu === + [Who are you?] -> who_are_you @@ -139,10 +145,29 @@ That door's locked tight. You'll need to find a way to unlock it. πŸ”’ } -> main_menu +// Triggered when player enters any room (general progress check) +=== on_room_entered === +{ has_unlocked_ceo: + Keep searching for that evidence! πŸ” +- else: + { trust_level >= 1: + You're making progress through the building. 🚢 + - else: + Exploring new areas... 🚢 + } +} +-> main_menu + // Triggered when player discovers a new room for the first time === on_room_discovered === -{ trust_level >= 1: - Interesting! You've found a new area. Be careful exploring. πŸ—ΊοΈ +{ trust_level >= 2: + Great find! This new area might have what we need. πŸ—ΊοΈβœ¨ +- else: + { trust_level >= 1: + Interesting! You've found a new area. Be careful exploring. πŸ—ΊοΈ + - else: + A new room... wonder what's inside. πŸšͺ + } } -> main_menu diff --git a/scenarios/ink/helper-npc.json b/scenarios/ink/helper-npc.json index 221fc05..8e951d0 100644 --- a/scenarios/ink/helper-npc.json +++ b/scenarios/ink/helper-npc.json @@ -1 +1 @@ -{"inkVersion":21,"root":[[["done",{"#n":"g-0"}],null],"done",{"start":["^Hey there! I'm here to help you out if you need it. πŸ‘‹","\n","^What can I do for you?","\n",{"->":"main_menu"},null],"main_menu":[["ev","str","^Who are you?","/str","/ev",{"*":".^.c-0","flg":4},"ev","str","^Can you help me get into the CEO's office?","/str","/ev",{"*":".^.c-1","flg":4},"ev","str","^Do you have any items for me?","/str","/ev",{"*":".^.c-2","flg":4},"ev","str","^Thanks for the lockpick! It worked great.","/str",{"VAR?":"saw_lockpick_used"},"/ev",{"*":".^.c-3","flg":5},"ev","str","^Thanks, I'm good for now.","/str","/ev",{"*":".^.c-4","flg":4},{"c-0":["^ ",{"->":"who_are_you"},"\n",null],"c-1":["^ ",{"->":"help_ceo_office"},"\n",null],"c-2":["^ ",{"->":"give_items"},"\n",null],"c-3":["^ ",{"->":"lockpick_feedback"},"\n",null],"c-4":["^ ",{"->":"goodbye"},"\n",null]}],null],"who_are_you":["^I'm a friendly NPC who can help you progress through the mission.","\n","^I can unlock doors, give you items, and provide hints.","\n","ev",{"VAR?":"trust_level"},1,"+","/ev",{"VAR=":"trust_level","re":true},{"->":"main_menu"},null],"help_ceo_office":["ev",{"VAR?":"has_unlocked_ceo"},"/ev",[{"->":".^.b","c":true},{"b":["\n","^I already unlocked the CEO's office for you! Just head on in.","\n",{"->":"main_menu"},{"->":".^.^.^.5"},null]}],[{"->":".^.b"},{"b":["\n","^The CEO's office? That's a tough one...","\n","ev",{"VAR?":"trust_level"},1,">=","/ev",[{"->":".^.b","c":true},{"b":["\n","^Alright, I trust you. Let me unlock that door for you.","\n","ev",true,"/ev",{"VAR=":"has_unlocked_ceo","re":true},"^There you go! The door to the CEO's office is now unlocked. ","#","^unlock_door:ceo","/#","\n","ev",{"VAR?":"trust_level"},2,"+","/ev",{"VAR=":"trust_level","re":true},{"->":"main_menu"},{"->":".^.^.^.10"},null]}],[{"->":".^.b"},{"b":["\n","^I don't know you well enough yet. Ask me something else first.","\n",{"->":"main_menu"},{"->":".^.^.^.10"},null]}],"nop","\n",{"->":".^.^.^.5"},null]}],"nop","\n",null],"give_items":["ev",{"VAR?":"has_given_lockpick"},"/ev",[{"->":".^.b","c":true},{"b":["\n","^I already gave you a lockpick set! Check your inventory.","\n",{"->":"main_menu"},{"->":".^.^.^.5"},null]}],[{"->":".^.b"},{"b":["\n","^Let me see what I have...","\n","ev",{"VAR?":"trust_level"},2,">=","/ev",[{"->":".^.b","c":true},{"b":["\n","^Here's a lockpick set. Use it wisely! πŸ”“","\n","ev",true,"/ev",{"VAR=":"has_given_lockpick","re":true},"#","^give_item:lockpick_set","/#",{"->":"main_menu"},{"->":".^.^.^.10"},null]}],[{"->":".^.b"},{"b":["\n","^I need to trust you more before I give you something like that.","\n",{"->":"main_menu"},{"->":".^.^.^.10"},null]}],"nop","\n",{"->":".^.^.^.5"},null]}],"nop","\n",null],"lockpick_feedback":["^Great! I'm glad it helped you out. That's what I'm here for.","\n","ev",{"VAR?":"trust_level"},1,"+","/ev",{"VAR=":"trust_level","re":true},{"->":"main_menu"},null],"goodbye":["^No problem! Let me know if you need anything.","\n","end",null],"on_lockpick_pickup":["ev",{"VAR?":"has_given_lockpick"},"/ev",[{"->":".^.b","c":true},{"b":["\n","^Great! You found the lockpick I gave you. Try it on a locked door or container!","\n",{"->":".^.^.^.5"},null]}],[{"->":".^.b"},{"b":["\n","^Nice find! That lockpick set looks professional. Could be very useful. πŸ”“","\n",{"->":".^.^.^.5"},null]}],"nop","\n",{"->":"main_menu"},null],"on_lockpick_success":["ev",true,"/ev",{"VAR=":"saw_lockpick_used","re":true},"ev",{"VAR?":"has_given_lockpick"},"/ev",[{"->":".^.b","c":true},{"b":["\n","^Excellent! Glad I could help you get through that. 🎯","\n",{"->":".^.^.^.9"},null]}],[{"->":".^.b"},{"b":["\n","^Nice work getting through that lock! πŸ”“","\n",{"->":".^.^.^.9"},null]}],"nop","\n",{"->":"main_menu"},null],"on_lockpick_failed":["ev",{"VAR?":"has_given_lockpick"},"/ev",[{"->":".^.b","c":true},{"b":["\n","^Don't give up! Lockpicking takes practice. Try adjusting the tension. πŸ”§","\n",{"->":".^.^.^.5"},null]}],[{"->":".^.b"},{"b":["\n","^Tough break. Lockpicking isn't easy without the right tools...","\n",{"->":".^.^.^.5"},null]}],"nop","\n",{"->":"main_menu"},null],"on_door_unlocked":["ev",true,"/ev",{"VAR=":"saw_door_unlock","re":true},"ev",{"VAR?":"has_unlocked_ceo"},"/ev",[{"->":".^.b","c":true},{"b":["\n","^Another door open! You're making great progress. πŸšͺβœ“","\n",{"->":".^.^.^.9"},null]}],[{"->":".^.b"},{"b":["\n","^Nice! You found a way through that door. Keep going!","\n",{"->":".^.^.^.9"},null]}],"nop","\n",{"->":"main_menu"},null],"on_door_attempt":["^That door's locked tight. You'll need to find a way to unlock it. πŸ”’","\n","ev",{"VAR?":"trust_level"},2,">=","/ev",[{"->":".^.b","c":true},{"b":["\n","^Want me to help you out? Just ask!","\n",{"->":".^.^.^.8"},null]}],"nop","\n",{"->":"main_menu"},null],"on_ceo_desk_interact":["ev",{"VAR?":"has_unlocked_ceo"},"/ev",[{"->":".^.b","c":true},{"b":["\n","^The CEO's desk - you made it! Nice work. πŸ“‹","\n",{"->":".^.^.^.5"},null]}],[{"->":".^.b"},{"b":["\n","^Trying to get into the CEO's office? I might be able to help with that...","\n",{"->":".^.^.^.5"},null]}],"nop","\n",{"->":"main_menu"},null],"on_item_found":["ev",{"VAR?":"trust_level"},1,">=","/ev",[{"->":".^.b","c":true},{"b":["\n","^Good find! Every item could be important for your mission. πŸ“¦","\n",{"->":".^.^.^.6"},null]}],"nop","\n",{"->":"main_menu"},null],"on_room_discovered":["ev",{"VAR?":"trust_level"},1,">=","/ev",[{"->":".^.b","c":true},{"b":["\n","^Interesting! You've found a new area. Be careful exploring. πŸ—ΊοΈ","\n",{"->":".^.^.^.6"},null]}],"nop","\n",{"->":"main_menu"},null],"on_ceo_office_entered":["ev",{"VAR?":"has_unlocked_ceo"},"/ev",[{"->":".^.b","c":true},{"b":["\n","^You're in! Remember, you're looking for evidence of the data breach. πŸ•΅οΈ","\n",{"->":".^.^.^.5"},null]}],[{"->":".^.b"},{"b":["\n","^Whoa, you got into the CEO's office! That's impressive! πŸŽ‰","\n","ev",{"VAR?":"trust_level"},1,"+","/ev",{"VAR=":"trust_level","re":true},{"->":".^.^.^.5"},null]}],"nop","\n",{"->":"main_menu"},null],"global decl":["ev",0,{"VAR=":"trust_level"},false,{"VAR=":"has_unlocked_ceo"},false,{"VAR=":"has_given_lockpick"},false,{"VAR=":"saw_lockpick_used"},false,{"VAR=":"saw_door_unlock"},"/ev","end",null]}],"listDefs":{}} \ No newline at end of file +{"inkVersion":21,"root":[[["done",{"#n":"g-0"}],null],"done",{"start":["ev",{"VAR?":"has_greeted"},"/ev",[{"->":".^.b","c":true},{"b":["\n",{"->":"main_menu"},{"->":"start.5"},null]}],[{"->":".^.b"},{"b":["\n","^Hey there! I'm here to help you out if you need it. πŸ‘‹","\n","^What can I do for you?","\n","ev",true,"/ev",{"VAR=":"has_greeted","re":true},{"->":"main_menu"},{"->":"start.5"},null]}],"nop","\n",null],"main_menu":[["ev","str","^Who are you?","/str","/ev",{"*":".^.c-0","flg":4},"ev","str","^Can you help me get into the CEO's office?","/str","/ev",{"*":".^.c-1","flg":4},"ev","str","^Do you have any items for me?","/str","/ev",{"*":".^.c-2","flg":4},"ev","str","^Thanks for the lockpick! It worked great.","/str",{"VAR?":"saw_lockpick_used"},"/ev",{"*":".^.c-3","flg":5},"ev","str","^Thanks, I'm good for now.","/str","/ev",{"*":".^.c-4","flg":4},{"c-0":["^ ",{"->":"who_are_you"},"\n",null],"c-1":["^ ",{"->":"help_ceo_office"},"\n",null],"c-2":["^ ",{"->":"give_items"},"\n",null],"c-3":["^ ",{"->":"lockpick_feedback"},"\n",null],"c-4":["^ ",{"->":"goodbye"},"\n",null]}],null],"who_are_you":["^I'm a friendly NPC who can help you progress through the mission.","\n","^I can unlock doors, give you items, and provide hints.","\n","ev",{"VAR?":"trust_level"},1,"+","/ev",{"VAR=":"trust_level","re":true},{"->":"main_menu"},null],"help_ceo_office":["ev",{"VAR?":"has_unlocked_ceo"},"/ev",[{"->":".^.b","c":true},{"b":["\n","^I already unlocked the CEO's office for you! Just head on in.","\n",{"->":"main_menu"},{"->":".^.^.^.5"},null]}],[{"->":".^.b"},{"b":["\n","^The CEO's office? That's a tough one...","\n","ev",{"VAR?":"trust_level"},1,">=","/ev",[{"->":".^.b","c":true},{"b":["\n","^Alright, I trust you. Let me unlock that door for you.","\n","ev",true,"/ev",{"VAR=":"has_unlocked_ceo","re":true},"^There you go! The door to the CEO's office is now unlocked. ","#","^unlock_door:ceo","/#","\n","ev",{"VAR?":"trust_level"},2,"+","/ev",{"VAR=":"trust_level","re":true},{"->":"main_menu"},{"->":".^.^.^.10"},null]}],[{"->":".^.b"},{"b":["\n","^I don't know you well enough yet. Ask me something else first.","\n",{"->":"main_menu"},{"->":".^.^.^.10"},null]}],"nop","\n",{"->":".^.^.^.5"},null]}],"nop","\n",null],"give_items":["ev",{"VAR?":"has_given_lockpick"},"/ev",[{"->":".^.b","c":true},{"b":["\n","^I already gave you a lockpick set! Check your inventory.","\n",{"->":"main_menu"},{"->":".^.^.^.5"},null]}],[{"->":".^.b"},{"b":["\n","^Let me see what I have...","\n","ev",{"VAR?":"trust_level"},2,">=","/ev",[{"->":".^.b","c":true},{"b":["\n","^Here's a lockpick set. Use it wisely! πŸ”“","\n","ev",true,"/ev",{"VAR=":"has_given_lockpick","re":true},"#","^give_item:lockpick_set","/#",{"->":"main_menu"},{"->":".^.^.^.10"},null]}],[{"->":".^.b"},{"b":["\n","^I need to trust you more before I give you something like that.","\n",{"->":"main_menu"},{"->":".^.^.^.10"},null]}],"nop","\n",{"->":".^.^.^.5"},null]}],"nop","\n",null],"lockpick_feedback":["^Great! I'm glad it helped you out. That's what I'm here for.","\n","ev",{"VAR?":"trust_level"},1,"+","/ev",{"VAR=":"trust_level","re":true},{"->":"main_menu"},null],"goodbye":["^No problem! Let me know if you need anything.","\n","end",null],"on_lockpick_pickup":["ev",{"VAR?":"has_given_lockpick"},"/ev",[{"->":".^.b","c":true},{"b":["\n","^Great! You found the lockpick I gave you. Try it on a locked door or container!","\n",{"->":".^.^.^.5"},null]}],[{"->":".^.b"},{"b":["\n","^Nice find! That lockpick set looks professional. Could be very useful. πŸ”“","\n",{"->":".^.^.^.5"},null]}],"nop","\n",{"->":"main_menu"},null],"on_lockpick_success":["ev",true,"/ev",{"VAR=":"saw_lockpick_used","re":true},"ev",{"VAR?":"has_given_lockpick"},"/ev",[{"->":".^.b","c":true},{"b":["\n","^Excellent! Glad I could help you get through that. 🎯","\n",{"->":".^.^.^.9"},null]}],[{"->":".^.b"},{"b":["\n","^Nice work getting through that lock! πŸ”“","\n",{"->":".^.^.^.9"},null]}],"nop","\n",{"->":"main_menu"},null],"on_lockpick_failed":["ev",{"VAR?":"has_given_lockpick"},"/ev",[{"->":".^.b","c":true},{"b":["\n","^Don't give up! Lockpicking takes practice. Try adjusting the tension. πŸ”§","\n",{"->":".^.^.^.5"},null]}],[{"->":".^.b"},{"b":["\n","^Tough break. Lockpicking isn't easy without the right tools...","\n",{"->":".^.^.^.5"},null]}],"nop","\n",{"->":"main_menu"},null],"on_door_unlocked":["ev",true,"/ev",{"VAR=":"saw_door_unlock","re":true},"ev",{"VAR?":"has_unlocked_ceo"},"/ev",[{"->":".^.b","c":true},{"b":["\n","^Another door open! You're making great progress. πŸšͺβœ“","\n",{"->":".^.^.^.9"},null]}],[{"->":".^.b"},{"b":["\n","^Nice! You found a way through that door. Keep going!","\n",{"->":".^.^.^.9"},null]}],"nop","\n",{"->":"main_menu"},null],"on_door_attempt":["^That door's locked tight. You'll need to find a way to unlock it. πŸ”’","\n","ev",{"VAR?":"trust_level"},2,">=","/ev",[{"->":".^.b","c":true},{"b":["\n","^Want me to help you out? Just ask!","\n",{"->":".^.^.^.8"},null]}],"nop","\n",{"->":"main_menu"},null],"on_ceo_desk_interact":["ev",{"VAR?":"has_unlocked_ceo"},"/ev",[{"->":".^.b","c":true},{"b":["\n","^The CEO's desk - you made it! Nice work. πŸ“‹","\n",{"->":".^.^.^.5"},null]}],[{"->":".^.b"},{"b":["\n","^Trying to get into the CEO's office? I might be able to help with that...","\n",{"->":".^.^.^.5"},null]}],"nop","\n",{"->":"main_menu"},null],"on_item_found":["ev",{"VAR?":"trust_level"},1,">=","/ev",[{"->":".^.b","c":true},{"b":["\n","^Good find! Every item could be important for your mission. πŸ“¦","\n",{"->":".^.^.^.6"},null]}],"nop","\n",{"->":"main_menu"},null],"on_room_entered":["ev",{"VAR?":"has_unlocked_ceo"},"/ev",[{"->":".^.b","c":true},{"b":["\n","^Keep searching for that evidence! πŸ”","\n",{"->":".^.^.^.5"},null]}],[{"->":".^.b"},{"b":["\n","ev",{"VAR?":"trust_level"},1,">=","/ev",[{"->":".^.b","c":true},{"b":["\n","^You're making progress through the building. 🚢","\n",{"->":".^.^.^.8"},null]}],[{"->":".^.b"},{"b":["\n","^Exploring new areas... 🚢","\n",{"->":".^.^.^.8"},null]}],"nop","\n",{"->":".^.^.^.5"},null]}],"nop","\n",{"->":"main_menu"},null],"on_room_discovered":["ev",{"VAR?":"trust_level"},2,">=","/ev",[{"->":".^.b","c":true},{"b":["\n","^Great find! This new area might have what we need. πŸ—ΊοΈβœ¨","\n",{"->":".^.^.^.7"},null]}],[{"->":".^.b"},{"b":["\n","ev",{"VAR?":"trust_level"},1,">=","/ev",[{"->":".^.b","c":true},{"b":["\n","^Interesting! You've found a new area. Be careful exploring. πŸ—ΊοΈ","\n",{"->":".^.^.^.8"},null]}],[{"->":".^.b"},{"b":["\n","^A new room... wonder what's inside. πŸšͺ","\n",{"->":".^.^.^.8"},null]}],"nop","\n",{"->":".^.^.^.7"},null]}],"nop","\n",{"->":"main_menu"},null],"on_ceo_office_entered":["ev",{"VAR?":"has_unlocked_ceo"},"/ev",[{"->":".^.b","c":true},{"b":["\n","^You're in! Remember, you're looking for evidence of the data breach. πŸ•΅οΈ","\n",{"->":".^.^.^.5"},null]}],[{"->":".^.b"},{"b":["\n","^Whoa, you got into the CEO's office! That's impressive! πŸŽ‰","\n","ev",{"VAR?":"trust_level"},1,"+","/ev",{"VAR=":"trust_level","re":true},{"->":".^.^.^.5"},null]}],"nop","\n",{"->":"main_menu"},null],"global decl":["ev",0,{"VAR=":"trust_level"},false,{"VAR=":"has_unlocked_ceo"},false,{"VAR=":"has_given_lockpick"},false,{"VAR=":"saw_lockpick_used"},false,{"VAR=":"saw_door_unlock"},false,{"VAR=":"has_greeted"},"/ev","end",null]}],"listDefs":{}} \ No newline at end of file