diff --git a/assets/objects/phone.png b/assets/objects/phone.png new file mode 100644 index 0000000..211ab60 Binary files /dev/null and b/assets/objects/phone.png differ diff --git a/index.html b/index.html index ab7a720..dec7d5e 100644 --- a/index.html +++ b/index.html @@ -40,7 +40,6 @@ - diff --git a/js/core/game.js b/js/core/game.js index c3305ea..33626c0 100644 --- a/js/core/game.js +++ b/js/core/game.js @@ -418,6 +418,15 @@ export function create() { } gameScenario = window.gameScenario; + // Register NPCs from scenario if they exist + if (gameScenario.npcs && window.npcManager) { + console.log('📱 Loading NPCs from scenario:', gameScenario.npcs.length); + gameScenario.npcs.forEach(npc => { + window.npcManager.registerNPC(npc); + console.log(`✅ Registered NPC: ${npc.id} (${npc.displayName})`); + }); + } + // Normalize keyPins in all rooms and objects from 0-100 scale to 25-65 scale normalizeScenarioKeyPins(gameScenario); diff --git a/js/core/rooms.js b/js/core/rooms.js index 5c4984c..12df71d 100644 --- a/js/core/rooms.js +++ b/js/core/rooms.js @@ -325,6 +325,11 @@ function applyScenarioProperties(sprite, scenarioObj, roomId, index) { sprite[key] = scenarioObj[key]; }); + // Ensure phones are always interactable (override scenario data if needed) + if (scenarioObj.type === 'phone') { + sprite.interactable = true; + } + // Log applied data for debugging console.log(`Applied scenario data to ${scenarioObj.type}:`, { name: scenarioObj.name, diff --git a/js/minigames/phone-chat/phone-chat-minigame.js b/js/minigames/phone-chat/phone-chat-minigame.js index 05bfbae..defd91b 100644 --- a/js/minigames/phone-chat/phone-chat-minigame.js +++ b/js/minigames/phone-chat/phone-chat-minigame.js @@ -225,6 +225,10 @@ export class PhoneChatMinigame extends MinigameScene { ? this.npcManager.getNPCsByPhone(this.phoneId) : Array.from(this.npcManager.npcs.values()); + console.log('📱 Preloading intro messages for phone:', this.phoneId); + console.log('📱 Found NPCs:', npcs.length, npcs.map(n => n.displayName)); + console.log('📱 All registered NPCs:', Array.from(this.npcManager.npcs.values()).map(n => ({ id: n.id, phoneId: n.phoneId, displayName: n.displayName }))); + for (const npc of npcs) { const history = this.npcManager.getConversationHistory(npc.id); @@ -446,14 +450,6 @@ export class PhoneChatMinigame extends MinigameScene { // Make choice in Ink story (this also continues and returns the result) const result = this.conversation.makeChoice(choiceIndex); - // Display the result from makeChoice (don't call continueStory again!) - if (result.hasEnded) { - console.log('🏁 Conversation ended'); - this.ui.showNotification('Conversation ended', 'info'); - this.isConversationActive = false; - return; - } - // Show typing indicator briefly this.ui.showTypingIndicator(); @@ -472,6 +468,14 @@ export class PhoneChatMinigame extends MinigameScene { }); } + // Check if conversation ended AFTER displaying the final text + if (result.hasEnded) { + console.log('🏁 Conversation ended'); + this.ui.showNotification('Conversation ended', 'info'); + this.isConversationActive = false; + return; + } + // Display choices if (result.choices && result.choices.length > 0) { this.ui.addChoices(result.choices); diff --git a/js/systems/interactions.js b/js/systems/interactions.js index 81eee33..80a6338 100644 --- a/js/systems/interactions.js +++ b/js/systems/interactions.js @@ -525,17 +525,17 @@ export function handleObjectInteraction(sprite) { message += `Observations: ${data.observations}\n`; } - // For phone type objects, use phone-chat with runtime conversion - if (data.type === 'phone' && (data.text || data.voice)) { + // For phone type objects, use phone-chat with runtime conversion or direct NPC access + if (data.type === 'phone' && (data.text || data.voice || data.npcIds)) { console.log('Phone object detected:', { type: data.type, text: data.text, voice: data.voice, npcIds: data.npcIds }); // Check if phone-chat system is available if (window.MinigameFramework && window.npcManager) { const phoneId = data.phoneId || 'default_phone'; - // Check if phone has already been converted (has npcIds) + // Check if phone has already been converted or has npcIds if (data.npcIds && data.npcIds.length > 0) { - console.log('Phone already converted, opening phone-chat directly'); + console.log('Phone has npcIds, opening phone-chat directly'); // Phone already has NPCs, open directly window.MinigameFramework.startMinigame('phone-chat', null, { phoneId: phoneId, @@ -544,7 +544,7 @@ export function handleObjectInteraction(sprite) { return; } - // Need to convert - import the converter + // Need to convert simple message - import the converter import('../utils/phone-message-converter.js').then(module => { const PhoneMessageConverter = module.default; diff --git a/scenarios/ceo_exfil.json b/scenarios/ceo_exfil.json index 150ba66..d99384f 100644 --- a/scenarios/ceo_exfil.json +++ b/scenarios/ceo_exfil.json @@ -1,7 +1,35 @@ { "scenario_brief": "Hi! You are a cyber investigator tasked with uncovering evidence of corporate espionage. Anonymous tips suggest the CEO has been selling company secrets, but you need proof.", "startRoom": "reception", + "npcs": [ + { + "id": "neye_eve", + "displayName": "Neye Eve", + "storyPath": "scenarios/ink/neye-eve.json", + "avatar": null, + "phoneId": "player_phone", + "currentKnot": "start", + "npcType": "phone" + }, + { + "id": "gossip_girl", + "displayName": "Gossip Girl", + "storyPath": "scenarios/ink/gossip-girl.json", + "avatar": null, + "phoneId": "player_phone", + "currentKnot": "start", + "npcType": "phone" + } + ], "startItemsInInventory": [ + { + "type": "phone", + "name": "Your Phone", + "takeable": true, + "phoneId": "player_phone", + "npcIds": ["neye_eve", "gossip_girl"], + "observations": "Your personal phone with some interesting contacts" + }, { "type": "workstation", "name": "Crypto Analysis Station", @@ -94,6 +122,25 @@ "name": "PIN Cracker", "takeable": true, "observations": "A sophisticated device that can analyze PIN entry patterns and provide feedback on attempts" + }, + { + "type": "safe", + "name": "Reception Safe", + "takeable": false, + "locked": true, + "lockType": "pin", + "requires": "9573", + "observations": "A small wall safe behind the reception desk. Looks like it needs a 4-digit code.", + "contents": [ + { + "type": "notes", + "name": "IT Access Credentials", + "takeable": true, + "readable": true, + "text": "Emergency IT Admin Credentials:\nUsername: admin\nPassword: ITsecure2024\n\nServer Room Backup Code: 4829\nCEO Office Alarm Override: 1337", + "observations": "Sensitive IT credentials that could be very useful" + } + ] } ] }, diff --git a/scenarios/ink/gossip-girl.ink b/scenarios/ink/gossip-girl.ink new file mode 100644 index 0000000..b8ac7a0 --- /dev/null +++ b/scenarios/ink/gossip-girl.ink @@ -0,0 +1,60 @@ +// Gossip Girl - Office Gossip +// Provides intel about staff including manager names + +VAR talked_about_it = false +VAR talked_about_ceo = false +VAR talked_about_security = false + +=== start === +Gossip Girl: OMG hiiii! You're new here, right? I know EVERYONE in this office! +Gossip Girl: Want to hear the latest tea? ☕ +-> hub + +=== hub === ++ {not talked_about_it} [Ask about the IT department] + -> topic_it ++ {not talked_about_ceo} [Ask about the CEO] + -> topic_ceo ++ {not talked_about_security} [Ask about security concerns] + -> topic_security ++ [That's enough gossip for now] + -> ending + +=== topic_it === +~ talked_about_it = true +Gossip Girl: Oh the IT team? They're actually pretty cool! +Gossip Girl: Neye Eve is super smart but a bit gullible, you know? Always trying to be helpful. +Gossip Girl: Their manager is Alex Chen - really nice but SUPER busy. Barely see them around these days. +Gossip Girl: Fun fact: Neye is SO worried about impressing Alex that they practically jump when Alex's name comes up! 😂 ++ [Tell me more about the team dynamics] + -> it_details ++ [Interesting! What else?] + -> hub + +=== it_details === +Gossip Girl: Well, Neye handles a lot of the day-to-day stuff - passwords, access codes, that kind of thing. +Gossip Girl: Alex trusts them completely. Maybe TOO much if you ask me... 👀 +Gossip Girl: Like, Neye would probably do ANYTHING if they thought Alex was asking for it! +-> hub + +=== topic_ceo === +~ talked_about_ceo = true +Gossip Girl: The CEO? *lowers voice* Girl, I have THEORIES... +Gossip Girl: Like, why are they here so late at night? And those "business trips" that nobody knows about? +Gossip Girl: I saw them coming out of the server room at 3 AM once. Like... what?! +Gossip Girl: Something shady is definitely going on. Mark my words! 🕵️ +-> hub + +=== topic_security === +~ talked_about_security = true +Gossip Girl: Security? Oh honey, let me tell you about the "security" around here... +Gossip Girl: Half the people write their passwords on sticky notes! Including the CEO! +Gossip Girl: And that reception safe? Pretty sure the code hasn't been changed in forever. +Gossip Girl: Actually wait - Neye mentioned they just updated it last week. Something about "security protocols." +Gossip Girl: They were SO proud of themselves for remembering to update it! 😊 +-> hub + +=== ending === +Gossip Girl: Okay okay, I'll let you go! But if you hear anything juicy, come find me! +Gossip Girl: And remember - you didn't hear any of this from me! 😉🤫 +-> END diff --git a/scenarios/ink/gossip-girl.json b/scenarios/ink/gossip-girl.json new file mode 100644 index 0000000..45fbe67 --- /dev/null +++ b/scenarios/ink/gossip-girl.json @@ -0,0 +1 @@ +{"inkVersion":21,"root":[[["done",{"#n":"g-0"}],null],"done",{"start":["^Gossip Girl: OMG hiiii! You're new here, right? I know EVERYONE in this office!","\n","^Gossip Girl: Want to hear the latest tea? ☕","\n",{"->":"hub"},null],"hub":[["ev","str","^Ask about the IT department","/str",{"VAR?":"talked_about_it"},"!","/ev",{"*":".^.c-0","flg":5},"ev","str","^Ask about the CEO","/str",{"VAR?":"talked_about_ceo"},"!","/ev",{"*":".^.c-1","flg":5},"ev","str","^Ask about security concerns","/str",{"VAR?":"talked_about_security"},"!","/ev",{"*":".^.c-2","flg":5},"ev","str","^That's enough gossip for now","/str","/ev",{"*":".^.c-3","flg":4},{"c-0":["\n",{"->":"topic_it"},null],"c-1":["\n",{"->":"topic_ceo"},null],"c-2":["\n",{"->":"topic_security"},null],"c-3":["\n",{"->":"ending"},null]}],null],"topic_it":[["ev",true,"/ev",{"VAR=":"talked_about_it","re":true},"^Gossip Girl: Oh the IT team? They're actually pretty cool!","\n","^Gossip Girl: Neye Eve is super smart but a bit gullible, you know? Always trying to be helpful.","\n","^Gossip Girl: Their manager is Alex Chen - really nice but SUPER busy. Barely see them around these days.","\n","^Gossip Girl: Fun fact: Neye is SO worried about impressing Alex that they practically jump when Alex's name comes up! 😂","\n","ev","str","^Tell me more about the team dynamics","/str","/ev",{"*":".^.c-0","flg":4},"ev","str","^Interesting! What else?","/str","/ev",{"*":".^.c-1","flg":4},{"c-0":["\n",{"->":"it_details"},null],"c-1":["\n",{"->":"hub"},null]}],null],"it_details":["^Gossip Girl: Well, Neye handles a lot of the day-to-day stuff - passwords, access codes, that kind of thing.","\n","^Gossip Girl: Alex trusts them completely. Maybe TOO much if you ask me... 👀","\n","^Gossip Girl: Like, Neye would probably do ANYTHING if they thought Alex was asking for it!","\n",{"->":"hub"},null],"topic_ceo":["ev",true,"/ev",{"VAR=":"talked_about_ceo","re":true},"^Gossip Girl: The CEO? *lowers voice* Girl, I have THEORIES...","\n","^Gossip Girl: Like, why are they here so late at night? And those \"business trips\" that nobody knows about?","\n","^Gossip Girl: I saw them coming out of the server room at 3 AM once. Like... what?!","\n","^Gossip Girl: Something shady is definitely going on. Mark my words! 🕵️","\n",{"->":"hub"},null],"topic_security":["ev",true,"/ev",{"VAR=":"talked_about_security","re":true},"^Gossip Girl: Security? Oh honey, let me tell you about the \"security\" around here...","\n","^Gossip Girl: Half the people write their passwords on sticky notes! Including the CEO!","\n","^Gossip Girl: And that reception safe? Pretty sure the code hasn't been changed in forever.","\n","^Gossip Girl: Actually wait - Neye mentioned they just updated it last week. Something about \"security protocols.\"","\n","^Gossip Girl: They were SO proud of themselves for remembering to update it! 😊","\n",{"->":"hub"},null],"ending":["^Gossip Girl: Okay okay, I'll let you go! But if you hear anything juicy, come find me!","\n","^Gossip Girl: And remember - you didn't hear any of this from me! 😉🤫","\n","end",null],"global decl":["ev",false,{"VAR=":"talked_about_it"},false,{"VAR=":"talked_about_ceo"},false,{"VAR=":"talked_about_security"},"/ev","end",null]}],"listDefs":{}} \ No newline at end of file diff --git a/scenarios/ink/neye-eve.ink b/scenarios/ink/neye-eve.ink new file mode 100644 index 0000000..1096a1e --- /dev/null +++ b/scenarios/ink/neye-eve.ink @@ -0,0 +1,121 @@ +// Neye Eve - IT Team Member +// Social engineering scenario - player must convince Neye they are the manager + +VAR trust_level = 0 +VAR knows_manager_name = false +VAR gave_code = false +VAR suspicious = false + +=== start === +~ trust_level = 0 +Neye Eve: Oh, hi! I'm Neye Eve from IT. Can I help you with something? +-> hub + +=== hub === ++ {not knows_manager_name} [Ask who their manager is] + -> ask_manager ++ [Claim to be their manager and ask for the reception safe code] + -> claim_manager ++ {suspicious} [Try to apologize and rebuild trust] + -> apologize ++ [Say goodbye] + -> ending_neutral + +=== ask_manager === +~ knows_manager_name = true +~ suspicious = true +~ trust_level = -3 +Neye Eve: Why would you need to know that? That's... kind of suspicious. +Neye Eve: If you were really my manager, you wouldn't need to ask that question. +Neye Eve: I think I should end this conversation now. +-> ending_suspicious + +=== claim_manager === +Neye Eve: Wait, you're my manager? ++ [Yes, I'm Alex Chen] + -> correct_manager ++ [Yes, I'm Sarah Johnson] + -> wrong_manager ++ [Yes, I'm Michael Torres] + -> wrong_manager_2 ++ [Yes, I'm the IT director] + -> vague_manager ++ [Never mind, just asking] + -> hub + +=== correct_manager === +{knows_manager_name: + Neye Eve: Oh! Alex! I didn't recognize you at first. What do you need? + + [I need the reception safe code for an audit] + -> give_code_success + + [Just checking in on the team] + -> casual_chat +- else: + Neye Eve: Oh! Alex! I didn't recognize you at first. What do you need? + + [I need the reception safe code for an audit] + -> give_code_success + + [Just checking in on the team] + -> casual_chat +} + +=== wrong_manager === +~ suspicious = true +~ trust_level = -5 +Neye Eve: Sarah Johnson? That's not my manager's name... +Neye Eve: Who ARE you? I think I need to report this to security. +-> ending_suspicious + +=== wrong_manager_2 === +~ suspicious = true +~ trust_level = -5 +Neye Eve: Michael Torres? That's not my manager's name... +Neye Eve: I don't know who you are, but you're definitely not my boss. This is really inappropriate. +-> ending_suspicious + +=== vague_manager === +~ suspicious = true +~ trust_level = -2 +Neye Eve: The IT director? That's not how you'd introduce yourself if you were really my boss... +Neye Eve: This feels weird. I'm going to need to verify your identity. +-> hub + +=== give_code_success === +~ gave_code = true +~ trust_level = 5 +Neye Eve: Oh, for the audit! Sure thing, Alex. +Neye Eve: The reception safe code is 9573. It was changed last week for security reasons. +Neye Eve: Let me know if you need anything else! +-> ending_success + +=== casual_chat === +~ trust_level += 2 +Neye Eve: Oh, thanks for checking in! The network monitoring project is going well. +Neye Eve: Actually, while I have you here - did you need something specific? ++ [Yes, I need the reception safe code] + -> give_code_success ++ [No, just wanted to see how things are going] + -> ending_success + +=== apologize === +{trust_level >= -2: + Neye Eve: Look, I appreciate the apology, but I'm still not comfortable with this. + Neye Eve: Maybe we should start over? + ~ trust_level = 0 + ~ suspicious = false + -> hub +- else: + Neye Eve: Sorry, but I've already contacted security. You should probably go. + -> ending_suspicious +} + +=== ending_success === +Neye Eve: Have a great day, Alex! +-> END + +=== ending_neutral === +Neye Eve: Okay, see you around! +-> END + +=== ending_suspicious === +Neye Eve: I really don't feel comfortable continuing this conversation. +-> END diff --git a/scenarios/ink/neye-eve.json b/scenarios/ink/neye-eve.json new file mode 100644 index 0000000..459573c --- /dev/null +++ b/scenarios/ink/neye-eve.json @@ -0,0 +1 @@ +{"inkVersion":21,"root":[[["done",{"#n":"g-0"}],null],"done",{"start":["ev",0,"/ev",{"VAR=":"trust_level","re":true},"^Neye Eve: Oh, hi! I'm Neye Eve from IT. Can I help you with something?","\n",{"->":"hub"},null],"hub":[["ev","str","^Ask who their manager is","/str",{"VAR?":"knows_manager_name"},"!","/ev",{"*":".^.c-0","flg":5},"ev","str","^Claim to be their manager and ask for the reception safe code","/str","/ev",{"*":".^.c-1","flg":4},"ev","str","^Try to apologize and rebuild trust","/str",{"VAR?":"suspicious"},"/ev",{"*":".^.c-2","flg":5},"ev","str","^Say goodbye","/str","/ev",{"*":".^.c-3","flg":4},{"c-0":["\n",{"->":"ask_manager"},null],"c-1":["\n",{"->":"claim_manager"},null],"c-2":["\n",{"->":"apologize"},null],"c-3":["\n",{"->":"ending_neutral"},null]}],null],"ask_manager":["ev",true,"/ev",{"VAR=":"knows_manager_name","re":true},"ev",true,"/ev",{"VAR=":"suspicious","re":true},"ev",-3,"/ev",{"VAR=":"trust_level","re":true},"^Neye Eve: Why would you need to know that? That's... kind of suspicious.","\n","^Neye Eve: If you were really my manager, you wouldn't need to ask that question.","\n","^Neye Eve: I think I should end this conversation now.","\n",{"->":"ending_suspicious"},null],"claim_manager":[["^Neye Eve: Wait, you're my manager?","\n","ev","str","^Yes, I'm Alex Chen","/str","/ev",{"*":".^.c-0","flg":4},"ev","str","^Yes, I'm Sarah Johnson","/str","/ev",{"*":".^.c-1","flg":4},"ev","str","^Yes, I'm Michael Torres","/str","/ev",{"*":".^.c-2","flg":4},"ev","str","^Yes, I'm the IT director","/str","/ev",{"*":".^.c-3","flg":4},"ev","str","^Never mind, just asking","/str","/ev",{"*":".^.c-4","flg":4},{"c-0":["\n",{"->":"correct_manager"},null],"c-1":["\n",{"->":"wrong_manager"},null],"c-2":["\n",{"->":"wrong_manager_2"},null],"c-3":["\n",{"->":"vague_manager"},null],"c-4":["\n",{"->":"hub"},null]}],null],"correct_manager":["ev",{"VAR?":"knows_manager_name"},"/ev",[{"->":".^.b","c":true},{"b":["\n","^Neye Eve: Oh! Alex! I didn't recognize you at first. What do you need?","\n","ev","str","^I need the reception safe code for an audit","/str","/ev",{"*":".^.c-0","flg":4},"ev","str","^Just checking in on the team","/str","/ev",{"*":".^.c-1","flg":4},{"->":".^.^.^.5"},{"c-0":["\n",{"->":"give_code_success"},null],"c-1":["\n",{"->":"casual_chat"},null]}]}],[{"->":".^.b"},{"b":["\n","^Neye Eve: Oh! Alex! I didn't recognize you at first. What do you need?","\n","ev","str","^I need the reception safe code for an audit","/str","/ev",{"*":".^.c-0","flg":4},"ev","str","^Just checking in on the team","/str","/ev",{"*":".^.c-1","flg":4},{"->":".^.^.^.5"},{"c-0":["\n",{"->":"give_code_success"},null],"c-1":["\n",{"->":"casual_chat"},null]}]}],"nop","\n",null],"wrong_manager":["ev",true,"/ev",{"VAR=":"suspicious","re":true},"ev",-5,"/ev",{"VAR=":"trust_level","re":true},"^Neye Eve: Sarah Johnson? That's not my manager's name...","\n","^Neye Eve: Who ARE you? I think I need to report this to security.","\n",{"->":"ending_suspicious"},null],"wrong_manager_2":["ev",true,"/ev",{"VAR=":"suspicious","re":true},"ev",-5,"/ev",{"VAR=":"trust_level","re":true},"^Neye Eve: Michael Torres? That's not my manager's name...","\n","^Neye Eve: I don't know who you are, but you're definitely not my boss. This is really inappropriate.","\n",{"->":"ending_suspicious"},null],"vague_manager":["ev",true,"/ev",{"VAR=":"suspicious","re":true},"ev",-2,"/ev",{"VAR=":"trust_level","re":true},"^Neye Eve: The IT director? That's not how you'd introduce yourself if you were really my boss...","\n","^Neye Eve: This feels weird. I'm going to need to verify your identity.","\n",{"->":"hub"},null],"give_code_success":["ev",true,"/ev",{"VAR=":"gave_code","re":true},"ev",5,"/ev",{"VAR=":"trust_level","re":true},"^Neye Eve: Oh, for the audit! Sure thing, Alex.","\n","^Neye Eve: The reception safe code is 9573. It was changed last week for security reasons.","\n","^Neye Eve: Let me know if you need anything else!","\n",{"->":"ending_success"},null],"casual_chat":[["ev",{"VAR?":"trust_level"},2,"+",{"VAR=":"trust_level","re":true},"/ev","^Neye Eve: Oh, thanks for checking in! The network monitoring project is going well.","\n","^Neye Eve: Actually, while I have you here - did you need something specific?","\n","ev","str","^Yes, I need the reception safe code","/str","/ev",{"*":".^.c-0","flg":4},"ev","str","^No, just wanted to see how things are going","/str","/ev",{"*":".^.c-1","flg":4},{"c-0":["\n",{"->":"give_code_success"},null],"c-1":["\n",{"->":"ending_success"},null]}],null],"apologize":["ev",{"VAR?":"trust_level"},-2,">=","/ev",[{"->":".^.b","c":true},{"b":["\n","^Neye Eve: Look, I appreciate the apology, but I'm still not comfortable with this.","\n","^Neye Eve: Maybe we should start over?","\n","ev",0,"/ev",{"VAR=":"trust_level","re":true},"ev",false,"/ev",{"VAR=":"suspicious","re":true},{"->":"hub"},{"->":".^.^.^.7"},null]}],[{"->":".^.b"},{"b":["\n","^Neye Eve: Sorry, but I've already contacted security. You should probably go.","\n",{"->":"ending_suspicious"},{"->":".^.^.^.7"},null]}],"nop","\n",null],"ending_success":["^Neye Eve: Have a great day, Alex!","\n","end",null],"ending_neutral":["^Neye Eve: Okay, see you around!","\n","end",null],"ending_suspicious":["^Neye Eve: I really don't feel comfortable continuing this conversation.","\n","end",null],"global decl":["ev",0,{"VAR=":"trust_level"},false,{"VAR=":"knows_manager_name"},false,{"VAR=":"gave_code"},false,{"VAR=":"suspicious"},"/ev","end",null]}],"listDefs":{}} \ No newline at end of file