diff --git a/public/break_escape/css/minigames-framework.css b/public/break_escape/css/minigames-framework.css index 00d9b11..afc8774 100644 --- a/public/break_escape/css/minigames-framework.css +++ b/public/break_escape/css/minigames-framework.css @@ -8,7 +8,7 @@ height: 100vh; background: rgba(0, 0, 0, 0.95); /* Raised so minigames appear above other UI (inventory, NPC barks, HUD, etc.) */ - z-index: 11000; + z-index: 1500; display: flex; flex-direction: column; justify-content: center; diff --git a/public/break_escape/js/minigames/person-chat/person-chat-minigame.js b/public/break_escape/js/minigames/person-chat/person-chat-minigame.js index 0eb6dc6..1892bb3 100644 --- a/public/break_escape/js/minigames/person-chat/person-chat-minigame.js +++ b/public/break_escape/js/minigames/person-chat/person-chat-minigame.js @@ -382,6 +382,20 @@ export class PersonChatMinigame extends MinigameScene { npcConversationStateManager.syncGlobalVariablesToStory(this.inkEngine.story); // Also sync inventory-based variables on initial load npcConversationStateManager.syncInventoryVariablesToStory(this.inkEngine.story, this.npc); + + // CRITICAL: If we're at a choice point after restoring state, the choices were + // evaluated with OLD global variable values. We need to re-navigate to the + // current position to force Ink to re-evaluate choices with updated globals. + // The hub pattern means choices depend on global state (e.g., alice_talked). + const story = this.inkEngine.story; + if (story.currentChoices && story.currentChoices.length > 0) { + // Get current path and re-navigate to it + const currentPath = story.state.currentPathString; + if (currentPath) { + console.log(`πŸ”„ Re-navigating to ${currentPath} to re-evaluate choices with updated globals`); + story.ChoosePathString(currentPath); + } + } } @@ -479,19 +493,10 @@ export class PersonChatMinigame extends MinigameScene { console.log(`πŸ“‹ No text, just showing choices`); } } else if (result.text && result.text.trim()) { - // Have text but no choices - display and continue - console.log(`πŸ—£οΈ Calling showDialogue with speaker: ${speaker}`); - this.ui.showDialogue(result.text, speaker); - - if (result.canContinue) { - // Can continue - schedule next advance - console.log(`πŸ“‹ Setting pendingContinueCallback - canContinue: true, no choices`); - this.scheduleDialogueAdvance(() => this.showCurrentDialogue(), DIALOGUE_AUTO_ADVANCE_DELAY); - } else { - // Can't continue but have text - story will end - console.log('βœ“ Waiting for story to end...'); - this.scheduleDialogueAdvance(() => this.endConversation(), DIALOGUE_END_DELAY); - } + // Have text but no choices - use displayAccumulatedDialogue for proper speaker parsing + // This ensures line prefix format (Speaker: text) is handled correctly + console.log(`πŸ—£οΈ Single line dialogue without choices - using block display for speaker parsing`); + this.displayAccumulatedDialogue(result); } else { // No text and no choices - story has ended console.log('🏁 No text and no choices - story ended'); @@ -826,8 +831,29 @@ export class PersonChatMinigame extends MinigameScene { this.isProcessingDialogue = true; try { - if (!result.text || !result.tags) { - // No content to display + // Process any game action tags (give_item, unlock_door, exit_conversation, etc.) FIRST + // This ensures tags are processed even if there's no visible text + if (result.tags && result.tags.length > 0) { + console.log('🏷️ Processing action tags from accumulated dialogue:', result.tags); + processGameActionTags(result.tags, this.ui); + + // Check for exit_conversation tag - close the minigame + const shouldExit = result.tags.some(tag => tag.includes('exit_conversation')); + if (shouldExit) { + console.log('πŸšͺ Exit conversation tag detected in displayAccumulatedDialogue - closing minigame'); + // Final state save before closing + if (this.inkEngine && this.inkEngine.story) { + npcConversationStateManager.saveNPCState(this.npcId, this.inkEngine.story); + } + this.scheduleDialogueAdvance(() => { + this.complete(true); + }, 1000); + return; + } + } + + if (!result.text) { + // No text content to display if (result.hasEnded) { // Story ended - save state and show message if (this.inkEngine && this.inkEngine.story) { @@ -835,22 +861,23 @@ export class PersonChatMinigame extends MinigameScene { } this.ui.showDialogue('(Conversation ended - press ESC to close)', 'system'); console.log('🏁 Story has reached an end point'); + } else if (result.canContinue) { + // No text but more content available - get next line + console.log('πŸ“– No text in result, getting next line...'); + const nextLine = this.conversation.continue(); + this.lastResult = nextLine; + this.displayAccumulatedDialogue(nextLine); } return; } - // Process any game action tags (give_item, unlock_door, etc.) BEFORE displaying dialogue - if (result.tags && result.tags.length > 0) { - console.log('🏷️ Processing action tags from accumulated dialogue:', result.tags); - processGameActionTags(result.tags, this.ui); - } - // Split text into lines const lines = result.text.split('\n').filter(line => line.trim()); // We have lines and tags - pair them up // Each tag corresponds to a line (or group of lines before the next tag) if (lines.length === 0) { + // Text was only whitespace - tags already processed above if (result.hasEnded) { // Story ended - save state and show message if (this.inkEngine && this.inkEngine.story) { @@ -858,6 +885,16 @@ export class PersonChatMinigame extends MinigameScene { } this.ui.showDialogue('(Conversation ended - press ESC to close)', 'system'); console.log('🏁 Story has reached an end point'); + } else if (result.canContinue) { + // No visible text but more content available - get next line + console.log('πŸ“– No visible lines, getting next line...'); + const nextLine = this.conversation.continue(); + this.lastResult = nextLine; + this.displayAccumulatedDialogue(nextLine); + } else if (result.choices && result.choices.length > 0) { + // Choices available + console.log(`πŸ“‹ No visible lines, showing ${result.choices.length} choices`); + this.ui.showChoices(result.choices); } return; } @@ -935,6 +972,7 @@ export class PersonChatMinigame extends MinigameScene { // Try to parse line prefix format const parsed = this.parseDialogueLine(line); + console.log(`πŸ” parseDialogueLine("${line.substring(0, 50)}...") =>`, parsed); if (parsed) { // This line has a prefix - speaker changed! @@ -1012,29 +1050,37 @@ export class PersonChatMinigame extends MinigameScene { this.lastResult = originalResult; this.ui.showChoices(originalResult.choices); } else { - // Try to continue for more dialogue - console.log('⏸️ Blocks finished, checking for more dialogue...'); - this.scheduleDialogueAdvance(() => { - const nextLine = this.conversation.continue(); - - // Store for choice handling - this.lastResult = nextLine; - - if (nextLine.text && nextLine.text.trim()) { - this.displayAccumulatedDialogue(nextLine); - } else if (nextLine.choices && nextLine.choices.length > 0) { - // Back to choices - display them - console.log(`πŸ“‹ Back to choices: ${nextLine.choices.length} options available`); - this.ui.showChoices(nextLine.choices); - } else if (nextLine.hasEnded) { - // Story ended - save state and show message - if (this.inkEngine && this.inkEngine.story) { - npcConversationStateManager.saveNPCState(this.npcId, this.inkEngine.story); - } - this.ui.showDialogue('(Conversation ended - press ESC to close)', 'system'); - console.log('🏁 Story has reached an end point'); + // More dialogue available - get next line immediately (no extra click needed) + // The user already clicked to see the last line of current block + console.log('⏸️ Blocks finished, getting next line immediately...'); + const nextLine = this.conversation.continue(); + + // Store for choice handling + this.lastResult = nextLine; + + // Check for exit_conversation tag FIRST (may come with empty text) + if (nextLine.tags && nextLine.tags.some(tag => tag.includes('exit_conversation'))) { + console.log('πŸšͺ Exit conversation tag detected after blocks - closing minigame'); + if (this.inkEngine && this.inkEngine.story) { + npcConversationStateManager.saveNPCState(this.npcId, this.inkEngine.story); } - }, DIALOGUE_AUTO_ADVANCE_DELAY); + this.scheduleDialogueAdvance(() => { + this.complete(true); + }, 1000); + } else if (nextLine.text && nextLine.text.trim()) { + this.displayAccumulatedDialogue(nextLine); + } else if (nextLine.choices && nextLine.choices.length > 0) { + // Back to choices - display them + console.log(`πŸ“‹ Back to choices: ${nextLine.choices.length} options available`); + this.ui.showChoices(nextLine.choices); + } else if (nextLine.hasEnded) { + // Story ended - save state and show message + if (this.inkEngine && this.inkEngine.story) { + npcConversationStateManager.saveNPCState(this.npcId, this.inkEngine.story); + } + this.ui.showDialogue('(Conversation ended - press ESC to close)', 'system'); + console.log('🏁 Story has reached an end point'); + } } return; } diff --git a/public/break_escape/js/minigames/phone-chat/phone-chat-conversation.js b/public/break_escape/js/minigames/phone-chat/phone-chat-conversation.js index 7b543c2..9992ca9 100644 --- a/public/break_escape/js/minigames/phone-chat/phone-chat-conversation.js +++ b/public/break_escape/js/minigames/phone-chat/phone-chat-conversation.js @@ -91,6 +91,15 @@ export default class PhoneChatConversation { window.eventDispatcher.on('npc_items_changed', this._itemsChangedListener); } + // Set up global variable observer to sync changes back to window.gameState + // This is critical for cross-NPC variable sharing + if (window.npcConversationStateManager && this.engine.story) { + window.npcConversationStateManager.discoverGlobalVariables(this.engine.story); + window.npcConversationStateManager.syncGlobalVariablesToStory(this.engine.story); + window.npcConversationStateManager.observeGlobalVariableChanges(this.engine.story, this.npcId); + console.log(`🌐 Global variable observer set up for ${this.npcId}`); + } + console.log(`βœ… Story loaded successfully for ${this.npcId}`); return true; diff --git a/public/break_escape/js/systems/ink/ink-engine.js b/public/break_escape/js/systems/ink/ink-engine.js index df13ef6..b66a1b5 100644 --- a/public/break_escape/js/systems/ink/ink-engine.js +++ b/public/break_escape/js/systems/ink/ink-engine.js @@ -24,7 +24,10 @@ export default class InkEngine { return this.story; } - // Continue the story and return the current text plus state + // Continue the story and return ONE line of visible text plus state + // BEHAVIOR: Skips empty/whitespace lines, accumulates their tags, returns first line with content. + // This ensures tags are processed with their specific line of dialogue. + // The caller will see canContinue=true and call continue() again for more. continue() { if (!this.story) throw new Error('Story not loaded'); @@ -35,22 +38,27 @@ export default class InkEngine { console.log('πŸ” InkEngine.continue() - canContinue:', this.story.canContinue); console.log('πŸ” InkEngine.continue() - currentChoices before:', this.story.currentChoices?.length); - // Continue until we hit choices or end - // Note: We gather all text until the next choice point or end + // Get lines until we have visible text (or hit choices/end) while (this.story.canContinue) { - const newText = this.story.Continue(); - console.log('πŸ” InkEngine.continue() - got text:', newText); - text += newText; + const lineText = this.story.Continue(); + const lineTags = this.story.currentTags || []; - // Collect tags from this passage - if (this.story.currentTags && this.story.currentTags.length > 0) { - console.log('🏷️ InkEngine.continue() - found tags:', this.story.currentTags); - tags = tags.concat(this.story.currentTags); + // Always accumulate tags + if (lineTags.length > 0) { + console.log('🏷️ InkEngine.continue() - found tags:', lineTags); + tags = tags.concat(lineTags); + } + + // Check if this line has visible content + if (lineText.trim()) { + text = lineText; + console.log('πŸ” InkEngine.continue() - got text:', text); + break; // Stop - we have a line to show + } else { + console.log('πŸ” InkEngine.continue() - skipping empty line, continuing...'); } } - console.log('πŸ” InkEngine.continue() - accumulated text:', text); - console.log('🏷️ InkEngine.continue() - accumulated tags:', tags); console.log('πŸ” InkEngine.continue() - canContinue after:', this.story.canContinue); console.log('πŸ” InkEngine.continue() - currentChoices after:', this.story.currentChoices?.length); console.log('πŸ” InkEngine.continue() - hasEnded:', this.story.hasEnded); diff --git a/scenarios/test_objectives_ink/alice.ink b/scenarios/test_objectives_ink/alice.ink index 69b44be..d769ff2 100644 --- a/scenarios/test_objectives_ink/alice.ink +++ b/scenarios/test_objectives_ink/alice.ink @@ -42,6 +42,7 @@ Great to meet you too! This task is now complete. #complete_task:talk_to_alice ~ alice_talked = true You should go talk to Bob next - I just unlocked that task for you. +#exit_conversation -> hub === explain_objectives === diff --git a/scenarios/test_objectives_ink/alice.json b/scenarios/test_objectives_ink/alice.json index 4909ab2..6c20c1f 100644 --- a/scenarios/test_objectives_ink/alice.json +++ b/scenarios/test_objectives_ink/alice.json @@ -1 +1 @@ -{"inkVersion":21,"root":[[["done",{"#n":"g-0"}],null],"done",{"start":["^Hey there! I'm Alice. Welcome to the objectives system test.","\n","^Player: Hi there!","\n",{"->":"hub"},null],"hub":[["ev","str","^Nice to meet you, Alice","/str",{"VAR?":"alice_talked"},"!","/ev",{"*":".^.c-0","flg":5},"ev","str","^Tell me about the secret mission","/str",{"VAR?":"alice_talked"},{"VAR?":"secret_revealed"},"!","&&","/ev",{"*":".^.c-1","flg":5},"ev","str","^I'm ready for the secret task","/str",{"VAR?":"secret_revealed"},{"VAR?":"secret_task_done"},"!","&&","/ev",{"*":".^.c-2","flg":5},"ev","str","^Any final words?","/str",{"VAR?":"secret_task_done"},"/ev",{"*":".^.c-3","flg":5},"ev","str","^What can you tell me about objectives?","/str","/ev",{"*":".^.c-4","flg":4},"ev","str","^I need to go","/str","/ev",{"*":".^.c-5","flg":4},{"c-0":["\n",{"->":"first_meeting"},null],"c-1":["\n",{"->":"reveal_secret"},null],"c-2":["\n",{"->":"complete_secret_task"},null],"c-3":["\n",{"->":"final_words"},null],"c-4":["\n",{"->":"explain_objectives"},null],"c-5":["^ ","\n","^See you around!","\n","#","^exit_conversation","/#",{"->":"hub"},null]}],null],"first_meeting":["^Great to meet you too! This task is now complete.","\n","#","^complete_task:talk_to_alice","/#","ev",true,"/ev",{"VAR=":"alice_talked","re":true},"^You should go talk to Bob next - I just unlocked that task for you.","\n",{"->":"hub"},null],"explain_objectives":["^The objectives system uses three Ink tags:","\n",{"->":"explain_objectives_detail"},null],"explain_objectives_detail":[["ev","str","^Tell me about complete_task","/str","/ev",{"*":".^.c-0","flg":4},"ev","str","^Tell me about unlock_task","/str","/ev",{"*":".^.c-1","flg":4},"ev","str","^Tell me about unlock_aim","/str","/ev",{"*":".^.c-2","flg":4},"ev","str","^That's enough info, thanks","/str","/ev",{"*":".^.c-3","flg":4},{"c-0":["\n","^NPC: **complete_task:task_id** marks a task as completed. The ObjectivesManager will update the UI and sync with the server.","\n",{"->":".^.^.^"},null],"c-1":["\n","^NPC: **unlock_task:task_id** unlocks a locked task so it becomes active and visible.","\n",{"->":".^.^.^"},null],"c-2":["\n","^NPC: **unlock_aim:aim_id** unlocks an entire aim (objective group) that was previously locked.","\n",{"->":".^.^.^"},null],"c-3":["\n","^NPC: These tags let NPCs control the player's objectives through dialogue!","\n",{"->":"hub"},null]}],null],"reveal_secret":["^Alright, I'll let you in on a secret...","\n","^There's a hidden mission that only unlocks through dialogue!","\n","#","^unlock_aim:secret_mission","/#","ev",true,"/ev",{"VAR=":"secret_revealed","re":true},"^I've just unlocked the \"Secret Mission\" aim for you. Check your objectives!","\n","^But the tasks inside are still locked. Let me unlock the first one...","\n","#","^unlock_task:secret_task_1","/#","^There! Now you can complete the first secret task.","\n",{"->":"hub"},null],"complete_secret_task":["^Excellent! You're doing great with the secret mission.","\n","#","^complete_task:secret_task_1","/#","ev",true,"/ev",{"VAR=":"secret_task_done","re":true},"^That's one secret task down! Bob can help you with the second one.","\n",{"->":"hub"},null],"final_words":["^You've done great! Once Bob helps you finish, come back for the final debrief.","\n",{"->":"hub"},null],"final_debrief":[["^NPC: *Alice looks up as you approach*","\n","^Narrator: Alice gives you a knowing smile.","\n","^Bob: You did it! The secret mission is complete.","\n","^NPC: I just received confirmation from headquarters.","\n","#","^complete_task:final_debrief","/#","^NPC: Mission accomplished, agent. You've proven yourself.","\n","^NPC: The objectives system test is now complete. Well done!","\n","ev","str","^Thank you, Alice","/str","/ev",{"*":".^.c-0","flg":4},"ev","str","^What's next?","/str","/ev",{"*":".^.c-1","flg":4},{"c-0":["\n","^NPC: Anytime. See you on the next mission.","\n","#","^exit_conversation","/#",{"->":"hub"},null],"c-1":["\n","^NPC: Take a break. You've earned it.","\n","^NPC: When you're ready, there will be more missions waiting.","\n","#","^exit_conversation","/#",{"->":"hub"},null]}],null],"global decl":["ev",false,{"VAR=":"alice_talked"},false,{"VAR=":"secret_revealed"},false,{"VAR=":"secret_task_done"},"/ev","end",null]}],"listDefs":{}} \ No newline at end of file +{"inkVersion":21,"root":[[["done",{"#n":"g-0"}],null],"done",{"start":["^Hey there! I'm Alice. Welcome to the objectives system test.","\n","^Player: Hi there!","\n",{"->":"hub"},null],"hub":[["ev","str","^Nice to meet you, Alice","/str",{"VAR?":"alice_talked"},"!","/ev",{"*":".^.c-0","flg":5},"ev","str","^Tell me about the secret mission","/str",{"VAR?":"alice_talked"},{"VAR?":"secret_revealed"},"!","&&","/ev",{"*":".^.c-1","flg":5},"ev","str","^I'm ready for the secret task","/str",{"VAR?":"secret_revealed"},{"VAR?":"secret_task_done"},"!","&&","/ev",{"*":".^.c-2","flg":5},"ev","str","^Any final words?","/str",{"VAR?":"secret_task_done"},"/ev",{"*":".^.c-3","flg":5},"ev","str","^What can you tell me about objectives?","/str","/ev",{"*":".^.c-4","flg":4},"ev","str","^I need to go","/str","/ev",{"*":".^.c-5","flg":4},{"c-0":["\n",{"->":"first_meeting"},null],"c-1":["\n",{"->":"reveal_secret"},null],"c-2":["\n",{"->":"complete_secret_task"},null],"c-3":["\n",{"->":"final_words"},null],"c-4":["\n",{"->":"explain_objectives"},null],"c-5":["^ ","\n","^See you around!","\n","#","^exit_conversation","/#",{"->":"hub"},null]}],null],"first_meeting":["^Great to meet you too! This task is now complete.","\n","#","^complete_task:talk_to_alice","/#","ev",true,"/ev",{"VAR=":"alice_talked","re":true},"^You should go talk to Bob next - I just unlocked that task for you.","\n","#","^exit_conversation","/#",{"->":"hub"},null],"explain_objectives":["^The objectives system uses three Ink tags:","\n",{"->":"explain_objectives_detail"},null],"explain_objectives_detail":[["ev","str","^Tell me about complete_task","/str","/ev",{"*":".^.c-0","flg":4},"ev","str","^Tell me about unlock_task","/str","/ev",{"*":".^.c-1","flg":4},"ev","str","^Tell me about unlock_aim","/str","/ev",{"*":".^.c-2","flg":4},"ev","str","^That's enough info, thanks","/str","/ev",{"*":".^.c-3","flg":4},{"c-0":["\n","^NPC: **complete_task:task_id** marks a task as completed. The ObjectivesManager will update the UI and sync with the server.","\n",{"->":".^.^.^"},null],"c-1":["\n","^NPC: **unlock_task:task_id** unlocks a locked task so it becomes active and visible.","\n",{"->":".^.^.^"},null],"c-2":["\n","^NPC: **unlock_aim:aim_id** unlocks an entire aim (objective group) that was previously locked.","\n",{"->":".^.^.^"},null],"c-3":["\n","^NPC: These tags let NPCs control the player's objectives through dialogue!","\n",{"->":"hub"},null]}],null],"reveal_secret":["^Alright, I'll let you in on a secret...","\n","^There's a hidden mission that only unlocks through dialogue!","\n","#","^unlock_aim:secret_mission","/#","ev",true,"/ev",{"VAR=":"secret_revealed","re":true},"^I've just unlocked the \"Secret Mission\" aim for you. Check your objectives!","\n","^But the tasks inside are still locked. Let me unlock the first one...","\n","#","^unlock_task:secret_task_1","/#","^There! Now you can complete the first secret task.","\n",{"->":"hub"},null],"complete_secret_task":["^Excellent! You're doing great with the secret mission.","\n","#","^complete_task:secret_task_1","/#","ev",true,"/ev",{"VAR=":"secret_task_done","re":true},"^That's one secret task down! Bob can help you with the second one.","\n",{"->":"hub"},null],"final_words":["^You've done great! Once Bob helps you finish, come back for the final debrief.","\n",{"->":"hub"},null],"final_debrief":[["^NPC: *Alice looks up as you approach*","\n","^Narrator: Alice gives you a knowing smile.","\n","^Bob: You did it! The secret mission is complete.","\n","^NPC: I just received confirmation from headquarters.","\n","#","^complete_task:final_debrief","/#","^NPC: Mission accomplished, agent. You've proven yourself.","\n","^NPC: The objectives system test is now complete. Well done!","\n","ev","str","^Thank you, Alice","/str","/ev",{"*":".^.c-0","flg":4},"ev","str","^What's next?","/str","/ev",{"*":".^.c-1","flg":4},{"c-0":["\n","^NPC: Anytime. See you on the next mission.","\n","#","^exit_conversation","/#",{"->":"hub"},null],"c-1":["\n","^NPC: Take a break. You've earned it.","\n","^NPC: When you're ready, there will be more missions waiting.","\n","#","^exit_conversation","/#",{"->":"hub"},null]}],null],"global decl":["ev",false,{"VAR=":"alice_talked"},false,{"VAR=":"secret_revealed"},false,{"VAR=":"secret_task_done"},"/ev","end",null]}],"listDefs":{}} \ No newline at end of file diff --git a/scenarios/test_objectives_ink/bob.ink b/scenarios/test_objectives_ink/bob.ink index 9957c09..204a8e2 100644 --- a/scenarios/test_objectives_ink/bob.ink +++ b/scenarios/test_objectives_ink/bob.ink @@ -51,11 +51,11 @@ Speaking of which... if there are any locked tasks you need help with, just ask. === secret_task_help === The secret task, eh? Let me help you with that. #unlock_task:secret_task_2 +#unlock_aim:finale #complete_task:secret_task_2 ~ helped_with_secret = true Done! Both secret tasks are now complete. That means the secret aim should be finished too. -#unlock_aim:finale #unlock_task:final_debrief I've also unlocked the finale for you. Go talk to Alice for the final debrief! -> hub diff --git a/scenarios/test_objectives_ink/bob.json b/scenarios/test_objectives_ink/bob.json index 4510a2c..1fb77f0 100644 --- a/scenarios/test_objectives_ink/bob.json +++ b/scenarios/test_objectives_ink/bob.json @@ -1 +1 @@ -{"inkVersion":21,"root":[[["done",{"#n":"g-0"}],null],"done",{"start":["^Narrator: You see a hooded figure waiting for you.","\n","^NPC: Oh, hey. I'm Bob.","\n",{"->":"hub"},null],"hub":[["ev","str","^Alice sent me to talk to you","/str",{"VAR?":"alice_talked"},{"VAR?":"bob_talked"},"!","&&","/ev",{"*":".^.c-0","flg":5},"ev","str","^Can you help with the secret task?","/str",{"VAR?":"bob_talked"},{"VAR?":"secret_revealed"},"&&",{"VAR?":"helped_with_secret"},"!","&&","/ev",{"*":".^.c-1","flg":5},"ev","str","^Thanks for the help!","/str",{"VAR?":"helped_with_secret"},"/ev",{"*":".^.c-2","flg":5},"ev","str","^What do you do here?","/str","/ev",{"*":".^.c-3","flg":4},"ev","str","^Goodbye","/str","/ev",{"*":".^.c-4","flg":4},{"c-0":["\n",{"->":"first_meeting"},null],"c-1":["\n",{"->":"secret_task_help"},null],"c-2":["\n",{"->":"thanks_response"},null],"c-3":["\n",{"->":"about_bob"},null],"c-4":["\n","^Later.","\n","#","^exit_conversation","/#",{"->":"hub"},null]}],null],"first_meeting":["^Ah, Alice sent you? Good, good.","\n","#","^complete_task:talk_to_bob","/#","ev",true,"/ev",{"VAR=":"bob_talked","re":true},"^I've just marked that task complete.","\n","^If Alice told you about anything... special... come back and ask me.","\n",{"->":"hub"},null],"about_bob":["^I handle the technical side of things.","\n","^Mostly just unlocking things that need to be unlocked.","\n","^Speaking of which... if there are any locked tasks you need help with, just ask.","\n",{"->":"hub"},null],"secret_task_help":["^The secret task, eh? Let me help you with that.","\n","#","^unlock_task:secret_task_2","/#","#","^complete_task:secret_task_2","/#","ev",true,"/ev",{"VAR=":"helped_with_secret","re":true},"^Done! Both secret tasks are now complete.","\n","^That means the secret aim should be finished too.","\n","#","^unlock_aim:finale","/#","#","^unlock_task:final_debrief","/#","^I've also unlocked the finale for you. Go talk to Alice for the final debrief!","\n",{"->":"hub"},null],"thanks_response":["^No problem! Go see Alice for the final debrief. She's waiting for you.","\n",{"->":"hub"},null],"global decl":["ev",false,{"VAR=":"alice_talked"},false,{"VAR=":"bob_talked"},false,{"VAR=":"helped_with_secret"},false,{"VAR=":"secret_revealed"},"/ev","end",null]}],"listDefs":{}} \ No newline at end of file +{"inkVersion":21,"root":[[["done",{"#n":"g-0"}],null],"done",{"start":["^Narrator: You see a hooded figure waiting for you.","\n","^NPC: Oh, hey. I'm Bob.","\n",{"->":"hub"},null],"hub":[["ev","str","^Alice sent me to talk to you","/str",{"VAR?":"alice_talked"},{"VAR?":"bob_talked"},"!","&&","/ev",{"*":".^.c-0","flg":5},"ev","str","^Can you help with the secret task?","/str",{"VAR?":"bob_talked"},{"VAR?":"secret_revealed"},"&&",{"VAR?":"helped_with_secret"},"!","&&","/ev",{"*":".^.c-1","flg":5},"ev","str","^Thanks for the help!","/str",{"VAR?":"helped_with_secret"},"/ev",{"*":".^.c-2","flg":5},"ev","str","^What do you do here?","/str","/ev",{"*":".^.c-3","flg":4},"ev","str","^Goodbye","/str","/ev",{"*":".^.c-4","flg":4},{"c-0":["\n",{"->":"first_meeting"},null],"c-1":["\n",{"->":"secret_task_help"},null],"c-2":["\n",{"->":"thanks_response"},null],"c-3":["\n",{"->":"about_bob"},null],"c-4":["\n","^Later.","\n","#","^exit_conversation","/#",{"->":"hub"},null]}],null],"first_meeting":["^Ah, Alice sent you? Good, good.","\n","#","^complete_task:talk_to_bob","/#","ev",true,"/ev",{"VAR=":"bob_talked","re":true},"^I've just marked that task complete.","\n","^If Alice told you about anything... special... come back and ask me.","\n",{"->":"hub"},null],"about_bob":["^I handle the technical side of things.","\n","^Mostly just unlocking things that need to be unlocked.","\n","^Speaking of which... if there are any locked tasks you need help with, just ask.","\n",{"->":"hub"},null],"secret_task_help":["^The secret task, eh? Let me help you with that.","\n","#","^unlock_task:secret_task_2","/#","#","^unlock_aim:finale","/#","#","^complete_task:secret_task_2","/#","ev",true,"/ev",{"VAR=":"helped_with_secret","re":true},"^Done! Both secret tasks are now complete.","\n","^That means the secret aim should be finished too.","\n","#","^unlock_task:final_debrief","/#","^I've also unlocked the finale for you. Go talk to Alice for the final debrief!","\n",{"->":"hub"},null],"thanks_response":["^No problem! Go see Alice for the final debrief. She's waiting for you.","\n",{"->":"hub"},null],"global decl":["ev",false,{"VAR=":"alice_talked"},false,{"VAR=":"bob_talked"},false,{"VAR=":"helped_with_secret"},false,{"VAR=":"secret_revealed"},"/ev","end",null]}],"listDefs":{}} \ No newline at end of file