diff --git a/assets/backgrounds/background1.png b/assets/backgrounds/background1.png
new file mode 100644
index 0000000..b6dad6c
Binary files /dev/null and b/assets/backgrounds/background1.png differ
diff --git a/assets/backgrounds/hq1.png b/assets/backgrounds/hq1.png
new file mode 100644
index 0000000..c599ca8
Binary files /dev/null and b/assets/backgrounds/hq1.png differ
diff --git a/assets/backgrounds/hq2.png b/assets/backgrounds/hq2.png
new file mode 100644
index 0000000..8e4a6a6
Binary files /dev/null and b/assets/backgrounds/hq2.png differ
diff --git a/assets/backgrounds/hq3.png b/assets/backgrounds/hq3.png
new file mode 100644
index 0000000..e78c24a
Binary files /dev/null and b/assets/backgrounds/hq3.png differ
diff --git a/js/minigames/container/container-minigame.js b/js/minigames/container/container-minigame.js
index bb8fd54..2121c12 100644
--- a/js/minigames/container/container-minigame.js
+++ b/js/minigames/container/container-minigame.js
@@ -9,8 +9,14 @@ export class ContainerMinigame extends MinigameScene {
this.contents = params.contents || [];
this.isTakeable = params.isTakeable || false;
- // Auto-detect desktop mode for PC/tablet containers
- this.desktopMode = params.desktopMode || this.shouldUseDesktopMode();
+ // NPC mode support
+ this.mode = params.mode || 'container'; // 'container', 'pc', or 'npc'
+ this.npcId = params.npcId || null;
+ this.npcDisplayName = params.npcDisplayName || null;
+ this.npcAvatar = params.npcAvatar || null;
+
+ // Auto-detect desktop mode for PC/tablet containers (not used in NPC mode)
+ this.desktopMode = (this.mode !== 'npc') && (params.desktopMode || this.shouldUseDesktopMode());
}
shouldUseDesktopMode() {
@@ -58,7 +64,9 @@ export class ContainerMinigame extends MinigameScene {
}
createContainerUI() {
- if (this.desktopMode) {
+ if (this.mode === 'npc') {
+ this.createNPCUI();
+ } else if (this.desktopMode) {
this.createDesktopUI();
} else {
this.createStandardUI();
@@ -71,6 +79,27 @@ export class ContainerMinigame extends MinigameScene {
this.setupEventListeners();
}
+ createNPCUI() {
+ // NPC mode - show NPC avatar and offer items
+ let avatarHtml = '';
+ if (this.npcAvatar) {
+ avatarHtml = `
`;
+ }
+
+ this.gameContainer.innerHTML = `
+
+ ${avatarHtml}
+
${this.npcDisplayName || 'NPC'} offers you items
+
+
Available Items
+
+
+
+
+
+ `;
+ }
+
createStandardUI() {
this.gameContainer.innerHTML = `
@@ -455,6 +484,24 @@ export class ContainerMinigame extends MinigameScene {
const itemIndex = this.contents.findIndex(content => content === item);
if (itemIndex !== -1) {
this.contents.splice(itemIndex, 1);
+
+ // If in NPC mode, also remove from NPC's itemsHeld
+ if (this.mode === 'npc' && this.npcId && window.npcManager) {
+ const npc = window.npcManager.getNPC(this.npcId);
+ if (npc && npc.itemsHeld) {
+ const npcItemIndex = npc.itemsHeld.findIndex(i => i === item);
+ if (npcItemIndex !== -1) {
+ npc.itemsHeld.splice(npcItemIndex, 1);
+
+ // Emit event to update Ink variables
+ if (window.eventDispatcher) {
+ window.eventDispatcher.emit('npc_items_changed', {
+ npcId: this.npcId
+ });
+ }
+ }
+ }
+ }
}
// Show success message
diff --git a/js/minigames/helpers/chat-helpers.js b/js/minigames/helpers/chat-helpers.js
index 04a813c..636592e 100644
--- a/js/minigames/helpers/chat-helpers.js
+++ b/js/minigames/helpers/chat-helpers.js
@@ -79,27 +79,56 @@ export function processGameActionTags(tags, ui) {
case 'give_item':
if (param) {
- // Parse item properties from param (could be "keycard" or "keycard|CEO Keycard")
- const [itemType, itemName] = param.split('|').map(s => s.trim());
- const giveResult = window.NPCGameBridge.giveItem(itemType, {
- name: itemName || itemType
- });
+ const [itemType] = param.split('|').map(s => s.trim());
+ const npcId = window.currentConversationNPCId;
+
+ if (!npcId) {
+ result.message = 'β οΈ No NPC context available';
+ console.warn(result.message);
+ break;
+ }
+
+ const giveResult = window.NPCGameBridge.giveItem(npcId, itemType);
if (giveResult.success) {
result.success = true;
- result.message = `π¦ Received: ${itemName || itemType}`;
+ result.message = `π¦ Received: ${giveResult.item.name}`;
if (ui) ui.showNotification(result.message, 'success');
console.log('β
Item given successfully:', giveResult);
} else {
- result.message = `β οΈ Failed to give item: ${itemType}`;
+ result.message = `β οΈ ${giveResult.error}`;
if (ui) ui.showNotification(result.message, 'warning');
console.warn('β οΈ Item give failed:', giveResult);
}
} else {
- result.message = 'β οΈ give_item tag missing item parameter';
+ result.message = 'β οΈ give_item requires item type parameter';
console.warn(result.message);
}
break;
+ case 'give_npc_inventory_items':
+ const npcId = window.currentConversationNPCId;
+
+ if (!npcId) {
+ result.message = 'β οΈ No NPC context available';
+ console.warn(result.message);
+ break;
+ }
+
+ // Parse filter types (comma-separated)
+ const filterTypes = param ? param.split(',').map(s => s.trim()).filter(s => s) : null;
+
+ const showResult = window.NPCGameBridge.showNPCInventory(npcId, filterTypes);
+ if (showResult.success) {
+ result.success = true;
+ result.message = `π¦ Opening inventory with ${showResult.itemCount} items`;
+ console.log('β
NPC inventory opened:', showResult);
+ } else {
+ result.message = `β οΈ ${showResult.error}`;
+ if (ui) ui.showNotification(result.message, 'warning');
+ console.warn('β οΈ Show inventory failed:', showResult);
+ }
+ break;
+
case 'set_objective':
if (param) {
window.NPCGameBridge.setObjective(param);
diff --git a/js/minigames/person-chat/person-chat-conversation.js b/js/minigames/person-chat/person-chat-conversation.js
index 2a57323..b9c38d7 100644
--- a/js/minigames/person-chat/person-chat-conversation.js
+++ b/js/minigames/person-chat/person-chat-conversation.js
@@ -98,6 +98,53 @@ export default class PersonChatConversation {
// Set variables in the Ink engine using setVariable instead of bindVariable
this.inkEngine.setVariable('last_interaction_type', 'person');
this.inkEngine.setVariable('player_name', 'Player');
+
+ // Sync NPC items to Ink variables
+ this.syncItemsToInk();
+
+ // Set up event listener for item changes
+ if (window.eventDispatcher) {
+ this._itemsChangedListener = (data) => {
+ if (data.npcId === this.npc.id) {
+ this.syncItemsToInk();
+ }
+ };
+ window.eventDispatcher.on('npc_items_changed', this._itemsChangedListener);
+ }
+ }
+
+ /**
+ * Sync NPC's held items to Ink variables
+ * Sets has_ based on itemsHeld array
+ */
+ syncItemsToInk() {
+ if (!this.inkEngine || !this.inkEngine.story) return;
+
+ const npc = this.npc;
+ if (!npc || !npc.itemsHeld) return;
+
+ const varState = this.inkEngine.story.variablesState;
+ if (!varState._defaultGlobalVariables) return;
+
+ // Count items by type
+ const itemCounts = {};
+ npc.itemsHeld.forEach(item => {
+ itemCounts[item.type] = (itemCounts[item.type] || 0) + 1;
+ });
+
+ // Set has_ variables based on inventory
+ Object.keys(itemCounts).forEach(type => {
+ const varName = `has_${type}`;
+ if (varState._defaultGlobalVariables && varState._defaultGlobalVariables.has(varName)) {
+ const hasItem = itemCounts[type] > 0;
+ try {
+ this.inkEngine.setVariable(varName, hasItem);
+ console.log(`β
Synced ${varName} = ${hasItem} for NPC ${npc.id}`);
+ } catch (err) {
+ console.warn(`β οΈ Could not sync ${varName}:`, err.message);
+ }
+ }
+ });
}
/**
@@ -337,6 +384,11 @@ export default class PersonChatConversation {
*/
end() {
try {
+ // Remove event listener
+ if (window.eventDispatcher && this._itemsChangedListener) {
+ window.eventDispatcher.off('npc_items_changed', this._itemsChangedListener);
+ }
+
if (this.inkEngine) {
// Don't destroy - keep for history/dual identity
this.inkEngine = null;
diff --git a/js/minigames/person-chat/person-chat-minigame.js b/js/minigames/person-chat/person-chat-minigame.js
index 210e532..a503253 100644
--- a/js/minigames/person-chat/person-chat-minigame.js
+++ b/js/minigames/person-chat/person-chat-minigame.js
@@ -281,6 +281,9 @@ export class PersonChatMinigame extends MinigameScene {
console.log('π PersonChatMinigame started');
+ // Track NPC context for tag processing
+ window.currentConversationNPCId = this.npcId;
+
// Start conversation with Ink
this.startConversation();
}
@@ -856,6 +859,9 @@ export class PersonChatMinigame extends MinigameScene {
npcConversationStateManager.saveNPCState(this.npcId, this.inkEngine.story);
}
+ // Clear NPC context
+ window.currentConversationNPCId = null;
+
// Call parent cleanup
super.cleanup();
}
diff --git a/js/minigames/person-chat/person-chat-portraits.js b/js/minigames/person-chat/person-chat-portraits.js
index 5a1858b..fadb011 100644
--- a/js/minigames/person-chat/person-chat-portraits.js
+++ b/js/minigames/person-chat/person-chat-portraits.js
@@ -417,7 +417,7 @@ export default class PersonChatPortraits {
if (!this.canvas || !this.ctx) return;
try {
- console.log(`π¨ render() called - useSpriteTalk: ${this.useSpriteTalk}, spriteSheet: ${this.spriteSheet}`);
+ // console.log(`π¨ render() called - useSpriteTalk: ${this.useSpriteTalk}, spriteSheet: ${this.spriteSheet}`);
// Clear canvas
this.ctx.fillStyle = '#000';
@@ -425,7 +425,7 @@ export default class PersonChatPortraits {
// If using spriteTalk image, render that instead
if (this.useSpriteTalk) {
- console.log(`π¨ Rendering spriteTalk image path`);
+ // console.log(`π¨ Rendering spriteTalk image path`);
// Calculate sprite scale for spriteTalk
const spriteTalkScale = this.calculateSpriteTalkScale();
// Draw background with sprite scale if loaded
diff --git a/js/minigames/phone-chat/phone-chat-conversation.js b/js/minigames/phone-chat/phone-chat-conversation.js
index 6894307..1f8176d 100644
--- a/js/minigames/phone-chat/phone-chat-conversation.js
+++ b/js/minigames/phone-chat/phone-chat-conversation.js
@@ -74,6 +74,20 @@ export default class PhoneChatConversation {
this.storyLoaded = true;
this.storyEnded = false;
+
+ // Sync NPC items to Ink variables
+ this.syncItemsToInk();
+
+ // Set up event listener for item changes
+ if (window.eventDispatcher) {
+ this._itemsChangedListener = (data) => {
+ if (data.npcId === this.npcId) {
+ this.syncItemsToInk();
+ }
+ };
+ window.eventDispatcher.on('npc_items_changed', this._itemsChangedListener);
+ }
+
console.log(`β
Story loaded successfully for ${this.npcId}`);
return true;
@@ -117,6 +131,40 @@ export default class PhoneChatConversation {
}
}
+ /**
+ * Sync NPC's held items to Ink variables
+ * Sets has_ based on itemsHeld array
+ */
+ syncItemsToInk() {
+ if (!this.engine || !this.engine.story) return;
+
+ const npc = this.npcManager.getNPC(this.npcId);
+ if (!npc || !npc.itemsHeld) return;
+
+ const varState = this.engine.story.variablesState;
+ if (!varState._defaultGlobalVariables) return;
+
+ // Count items by type
+ const itemCounts = {};
+ npc.itemsHeld.forEach(item => {
+ itemCounts[item.type] = (itemCounts[item.type] || 0) + 1;
+ });
+
+ // Set has_ variables based on inventory
+ Object.keys(itemCounts).forEach(type => {
+ const varName = `has_${type}`;
+ if (varState._defaultGlobalVariables && varState._defaultGlobalVariables.has(varName)) {
+ const hasItem = itemCounts[type] > 0;
+ try {
+ this.engine.setVariable(varName, hasItem);
+ console.log(`β
Synced ${varName} = ${hasItem} for NPC ${npc.id}`);
+ } catch (err) {
+ console.warn(`β οΈ Could not sync ${varName}:`, err.message);
+ }
+ }
+ });
+ }
+
/**
* Continue the story and get the next text/choices
* @returns {Object} Story result { text, choices, tags, canContinue, hasEnded }
@@ -331,6 +379,16 @@ export default class PhoneChatConversation {
}
}
+ /**
+ * Clean up resources (event listeners, etc.)
+ */
+ cleanup() {
+ // Remove event listener
+ if (window.eventDispatcher && this._itemsChangedListener) {
+ window.eventDispatcher.off('npc_items_changed', this._itemsChangedListener);
+ }
+ }
+
/**
* Get conversation metadata (variables, state)
* @returns {Object} Metadata about the conversation
diff --git a/js/minigames/phone-chat/phone-chat-minigame.js b/js/minigames/phone-chat/phone-chat-minigame.js
index 9b4f3bc..c951623 100644
--- a/js/minigames/phone-chat/phone-chat-minigame.js
+++ b/js/minigames/phone-chat/phone-chat-minigame.js
@@ -207,6 +207,8 @@ export class PhoneChatMinigame extends MinigameScene {
// If NPC ID provided, open that conversation directly
if (this.currentNPCId) {
+ // Track NPC context for tag processing
+ window.currentConversationNPCId = this.currentNPCId;
this.openConversation(this.currentNPCId);
} else {
// Show contact list for this phone
@@ -299,6 +301,9 @@ export class PhoneChatMinigame extends MinigameScene {
// Update current NPC
this.currentNPCId = npcId;
+ // Track NPC context for tag processing
+ window.currentConversationNPCId = npcId;
+
// Initialize conversation modules
this.history = new PhoneChatHistory(npcId, this.npcManager);
this.conversation = new PhoneChatConversation(npcId, this.npcManager, this.inkEngine);
@@ -735,6 +740,9 @@ export class PhoneChatMinigame extends MinigameScene {
this.conversation = null;
this.history = null;
+ // Clear NPC context
+ window.currentConversationNPCId = null;
+
// Call parent cleanup
super.cleanup();
}
diff --git a/js/systems/ink/ink-engine.js b/js/systems/ink/ink-engine.js
index fd48d51..f2372a3 100644
--- a/js/systems/ink/ink-engine.js
+++ b/js/systems/ink/ink-engine.js
@@ -116,7 +116,13 @@ export default class InkEngine {
setVariable(name, value) {
if (!this.story) throw new Error('Story not loaded');
- // inkjs VariableState.SetGlobal expects a RuntimeObject; it's forgiving for primitives
- this.story.variablesState.SetGlobal(name, value);
+
+ // Let Ink handle the value type conversion through the indexer
+ // which properly wraps values in Runtime.Value objects
+ try {
+ this.story.variablesState[name] = value;
+ } catch (err) {
+ console.warn(`β οΈ Failed to set variable ${name}:`, err.message);
+ }
}
}
diff --git a/js/systems/npc-conversation-state.js b/js/systems/npc-conversation-state.js
index 5c30b2d..d0476e0 100644
--- a/js/systems/npc-conversation-state.js
+++ b/js/systems/npc-conversation-state.js
@@ -35,14 +35,30 @@ class NPCConversationStateManager {
// Always save the variables (favour, items earned, flags, etc.)
// These persist across conversations even when story ends
if (story.variablesState) {
- state.variables = { ...story.variablesState };
+ // Filter out has_* variables (derived from itemsHeld, will be re-synced on load)
+ const filteredVariables = {};
+ for (const [key, value] of Object.entries(story.variablesState)) {
+ // Skip dynamically-synced item inventory variables
+ if (!key.startsWith('has_lockpick') &&
+ !key.startsWith('has_workstation') &&
+ !key.startsWith('has_phone') &&
+ !key.startsWith('has_keycard')) {
+ filteredVariables[key] = value;
+ }
+ }
+ state.variables = filteredVariables;
console.log(`πΎ Saved variables for ${npcId}:`, state.variables);
}
// Only save full story state if story is still active OR if explicitly forced
if (!story.state.hasEnded || forceFullState) {
- state.storyState = story.state.ToJson();
- console.log(`πΎ Saved full story state for ${npcId} (active story)`);
+ try {
+ state.storyState = story.state.ToJson();
+ console.log(`πΎ Saved full story state for ${npcId} (active story)`);
+ } catch (serializeError) {
+ // If serialization fails (due to dynamic variables), just save variables
+ console.warn(`β οΈ Could not serialize full story state for ${npcId}, saving variables only:`, serializeError.message);
+ }
} else {
console.log(`πΎ Saved variables only for ${npcId} (story ended - will restart fresh)`);
}
diff --git a/js/systems/npc-game-bridge.js b/js/systems/npc-game-bridge.js
index 8a0e9c6..4a87584 100644
--- a/js/systems/npc-game-bridge.js
+++ b/js/systems/npc-game-bridge.js
@@ -100,99 +100,142 @@ export class NPCGameBridge {
}
/**
- * Give an item to the player
- * @param {string} itemType - Type of item to give
- * @param {Object} properties - Optional item properties
- * @returns {Object} Result object with success status
+ * Give an item from NPC's inventory to the player immediately
+ * @param {string} npcId - NPC identifier
+ * @param {string} itemType - Type of item to give (optional - gives first if null)
+ * @returns {Object} Result with success status
*/
- giveItem(itemType, properties = {}) {
- if (!itemType) {
- const result = { success: false, error: 'No itemType provided' };
- this._logAction('giveItem', { itemType, properties }, result);
+ giveItem(npcId, itemType = null) {
+ if (!npcId) {
+ const result = { success: false, error: 'No npcId provided' };
+ this._logAction('giveItem', { npcId, itemType }, result);
return result;
}
+ // Get NPC from manager
+ const npc = window.npcManager?.getNPC(npcId);
+ if (!npc) {
+ const result = { success: false, error: `NPC ${npcId} not found` };
+ this._logAction('giveItem', { npcId, itemType }, result);
+ return result;
+ }
+
+ if (!npc.itemsHeld || npc.itemsHeld.length === 0) {
+ const result = { success: false, error: `NPC ${npcId} has no items to give` };
+ this._logAction('giveItem', { npcId, itemType }, result);
+ return result;
+ }
+
+ // Find item in NPC's inventory
+ let itemIndex = -1;
+ if (itemType) {
+ // Find first item matching type
+ itemIndex = npc.itemsHeld.findIndex(item => item.type === itemType);
+ if (itemIndex === -1) {
+ const result = { success: false, error: `NPC ${npcId} doesn't have ${itemType}` };
+ this._logAction('giveItem', { npcId, itemType }, result);
+ return result;
+ }
+ } else {
+ // Give first item
+ itemIndex = 0;
+ }
+
+ const item = npc.itemsHeld[itemIndex];
+
if (!window.addToInventory) {
const result = { success: false, error: 'Inventory system not available' };
- this._logAction('giveItem', { itemType, properties }, result);
+ this._logAction('giveItem', { npcId, itemType }, result);
return result;
}
try {
- // Default names for common items
- const defaultNames = {
- 'lockpick': 'Lock Pick Kit',
- 'bluetooth_scanner': 'Bluetooth Scanner',
- 'fingerprint_kit': 'Fingerprint Kit',
- 'pin-cracker': 'PIN Cracker',
- 'workstation': 'Crypto Analysis Station',
- 'keycard': 'Access Keycard',
- 'key': 'Key'
- };
-
- // Default observations for common items
- const defaultObservations = {
- 'lockpick': 'A professional lock picking kit with various picks and tension wrenches',
- 'bluetooth_scanner': 'A device for scanning and connecting to nearby Bluetooth devices',
- 'fingerprint_kit': 'A forensic kit for collecting and analyzing fingerprints',
- 'pin-cracker': 'A tool for cracking numeric PIN codes',
- 'workstation': 'A powerful workstation for cryptographic analysis',
- 'keycard': 'An access keycard for secured areas',
- 'key': 'A key that opens a specific lock'
- };
-
- // Create a basic item structure
- const itemName = (properties.name && properties.name !== itemType)
- ? properties.name
- : (defaultNames[itemType] || itemType);
- const itemObservations = properties.observations || defaultObservations[itemType] || `A ${itemName} given by an NPC`;
-
- const item = {
- type: itemType,
- name: itemName,
- takeable: true,
- observations: itemObservations,
- scenarioData: {
- ...properties, // Spread properties first
- type: itemType, // Then override with correct values
- name: itemName,
- observations: itemObservations,
- takeable: true
- }
+ // Create sprite using container pattern
+ const tempSprite = {
+ scenarioData: item,
+ name: item.type,
+ objectId: `npc_gift_${npcId}_${item.type}_${Date.now()}`,
+ texture: { key: item.type }
};
- // Create a pseudo-sprite for the inventory system
- const sprite = {
- name: item.name,
- scenarioData: item.scenarioData,
- texture: { key: itemType },
- objectId: `npc_gift_${itemType}_${Date.now()}`
- };
+ // Add to player inventory
+ window.addToInventory(tempSprite);
- console.log('π NPCGameBridge: Creating item sprite:', {
- itemType,
- name: sprite.name,
- scenarioDataName: sprite.scenarioData.name,
- scenarioDataType: sprite.scenarioData.type,
- fullScenarioData: sprite.scenarioData
- });
+ // Remove from NPC's inventory
+ npc.itemsHeld.splice(itemIndex, 1);
- window.addToInventory(sprite);
-
- // Emit event
+ // Emit event to update Ink variables
if (window.eventDispatcher) {
- window.eventDispatcher.emit('item_given_by_npc', {
- itemType,
- source: 'npc'
- });
+ window.eventDispatcher.emit('npc_items_changed', { npcId });
}
- const result = { success: true, itemType, item };
- this._logAction('giveItem', { itemType, properties }, result);
+ const result = { success: true, item, npcId };
+ this._logAction('giveItem', { npcId, itemType }, result);
return result;
} catch (error) {
const result = { success: false, error: error.message };
- this._logAction('giveItem', { itemType, properties }, result);
+ this._logAction('giveItem', { npcId, itemType }, result);
+ return result;
+ }
+ }
+
+ /**
+ * Show NPC's inventory items in container UI
+ * @param {string} npcId - NPC identifier
+ * @param {string[]} filterTypes - Array of item types to show (null = all)
+ * @returns {Object} Result with success status
+ */
+ showNPCInventory(npcId, filterTypes = null) {
+ if (!npcId) {
+ const result = { success: false, error: 'No npcId provided' };
+ this._logAction('showNPCInventory', { npcId, filterTypes }, result);
+ return result;
+ }
+
+ const npc = window.npcManager?.getNPC(npcId);
+ if (!npc) {
+ const result = { success: false, error: `NPC ${npcId} not found` };
+ this._logAction('showNPCInventory', { npcId, filterTypes }, result);
+ return result;
+ }
+
+ if (!npc.itemsHeld || npc.itemsHeld.length === 0) {
+ const result = { success: false, error: `NPC ${npcId} has no items` };
+ this._logAction('showNPCInventory', { npcId, filterTypes }, result);
+ return result;
+ }
+
+ // Filter items if types specified
+ let itemsToShow = npc.itemsHeld;
+ if (filterTypes && filterTypes.length > 0) {
+ itemsToShow = npc.itemsHeld.filter(item =>
+ filterTypes.includes(item.type)
+ );
+ }
+
+ if (itemsToShow.length === 0) {
+ const result = { success: false, error: 'No matching items to show' };
+ this._logAction('showNPCInventory', { npcId, filterTypes }, result);
+ return result;
+ }
+
+ // Open container minigame in NPC mode
+ if (window.startContainerMinigame) {
+ window.startContainerMinigame({
+ name: `${npc.displayName}'s Items`,
+ contents: itemsToShow,
+ mode: 'npc',
+ npcId: npcId,
+ npcDisplayName: npc.displayName,
+ npcAvatar: npc.avatar
+ });
+
+ const result = { success: true, npcId, itemCount: itemsToShow.length };
+ this._logAction('showNPCInventory', { npcId, filterTypes }, result);
+ return result;
+ } else {
+ const result = { success: false, error: 'Container minigame not available' };
+ this._logAction('showNPCInventory', { npcId, filterTypes }, result);
return result;
}
}
@@ -414,7 +457,8 @@ if (typeof window !== 'undefined') {
// Register convenience methods globally for Ink
window.npcUnlockDoor = (roomId) => bridge.unlockDoor(roomId);
- window.npcGiveItem = (itemType, properties) => bridge.giveItem(itemType, properties);
+ window.npcGiveItem = (npcId, itemType) => bridge.giveItem(npcId, itemType);
+ window.npcShowInventory = (npcId, filterTypes) => bridge.showNPCInventory(npcId, filterTypes);
window.npcSetObjective = (text) => bridge.setObjective(text);
window.npcRevealSecret = (secretId, data) => bridge.revealSecret(secretId, data);
window.npcAddNote = (title, content) => bridge.addNote(title, content);
diff --git a/js/systems/npc-manager.js b/js/systems/npc-manager.js
index 3a7ba16..84181d7 100644
--- a/js/systems/npc-manager.js
+++ b/js/systems/npc-manager.js
@@ -65,7 +65,8 @@ export default class NPCManager {
metadata: {},
eventMappings: {},
phoneId: 'player_phone', // Default to player's phone
- npcType: 'phone' // Default to phone-based NPC
+ npcType: 'phone', // Default to phone-based NPC
+ itemsHeld: [] // Initialize empty inventory for NPC item giving
}, realOpts);
this.npcs.set(realId, entry);
diff --git a/planning_notes/rails-engine-migration/progress/CLIENT_SERVER_SEPARATION_PLAN.md b/planning_notes/rails-engine-migration/progress/CLIENT_SERVER_SEPARATION_PLAN.md
index a78f100..54a834b 100644
--- a/planning_notes/rails-engine-migration/progress/CLIENT_SERVER_SEPARATION_PLAN.md
+++ b/planning_notes/rails-engine-migration/progress/CLIENT_SERVER_SEPARATION_PLAN.md
@@ -1020,3 +1020,5 @@ Room Loading β Container System β Unlock System β Inventory System
**Confidence:** High - architecture already supports this model (see ARCHITECTURE_COMPARISON.md)
+
+
diff --git a/planning_notes/rails-engine-migration/progress/NPC_MIGRATION_OPTIONS.md b/planning_notes/rails-engine-migration/progress/NPC_MIGRATION_OPTIONS.md
index 6699c61..0149a6a 100644
--- a/planning_notes/rails-engine-migration/progress/NPC_MIGRATION_OPTIONS.md
+++ b/planning_notes/rails-engine-migration/progress/NPC_MIGRATION_OPTIONS.md
@@ -839,3 +839,5 @@ end
This approach balances security, UX, and development effort.
+
+
diff --git a/planning_notes/rails-engine-migration/progress/RAILS_ENGINE_MIGRATION_PLAN.md b/planning_notes/rails-engine-migration/progress/RAILS_ENGINE_MIGRATION_PLAN.md
index e7c6180..42e00e4 100644
--- a/planning_notes/rails-engine-migration/progress/RAILS_ENGINE_MIGRATION_PLAN.md
+++ b/planning_notes/rails-engine-migration/progress/RAILS_ENGINE_MIGRATION_PLAN.md
@@ -1971,3 +1971,5 @@ This comprehensive plan provides:
The architecture supports both standalone operation and mounting in host applications, making it flexible and maintainable.
+
+
diff --git a/planning_notes/rails-engine-migration/progress/README.md b/planning_notes/rails-engine-migration/progress/README.md
index 13f876c..e1c9f2f 100644
--- a/planning_notes/rails-engine-migration/progress/README.md
+++ b/planning_notes/rails-engine-migration/progress/README.md
@@ -621,3 +621,5 @@ For questions about this migration plan, contact the development team or file an
**Happy migrating! π**
+
+
diff --git a/scenarios/ink/helper-npc.ink b/scenarios/ink/helper-npc.ink
index e875151..37c3416 100644
--- a/scenarios/ink/helper-npc.ink
+++ b/scenarios/ink/helper-npc.ink
@@ -13,6 +13,12 @@ VAR asked_about_self = false
VAR asked_about_ceo = false
VAR asked_for_items = false
+// NPC item inventory variables (synced from itemsHeld array)
+VAR has_lockpick = false
+VAR has_workstation = false
+VAR has_phone = false
+VAR has_keycard = false
+
=== start ===
# speaker:npc
Hey there! I'm here to help you out if you need it. π
@@ -40,16 +46,11 @@ What can I do for you?
}
// Items - changes based on state
-{asked_about_self and not has_given_lockpick:
+{asked_about_self and (has_lockpick or has_workstation or has_phone or has_keycard):
+ [Do you have any items for me?]
-> give_items
}
-{has_given_lockpick:
- + [Got any other items for me?]
- -> other_items
-}
-
// Feedback option appears after using lockpick
{saw_lockpick_used:
+ [Thanks for the lockpick! It worked great.]
@@ -80,7 +81,7 @@ What would you like to do?
{has_unlocked_ceo:
I already unlocked the CEO's office for you! Just head on in.
-> hub
-- else:
+|- else:
The CEO's office? That's a tough one...
{trust_level >= 1:
Alright, I trust you enough. Let me unlock that door for you.
@@ -105,38 +106,26 @@ Let me know!
=== give_items ===
# speaker:npc
-{has_given_lockpick:
- I already gave you a lockpick set. Check your inventory - it should be there!
+{not has_lockpick and not has_workstation and not has_phone and not has_keycard:
+ Sorry, I don't have any items to give you right now.
-> hub
-- else:
- Let me see what I have...
+|- else:
{trust_level >= 2:
- Here's a lockpick set. Use it to open locked doors and containers! π
- ~ has_given_lockpick = true
+ Let me show you what I have for you!
+ #give_npc_inventory_items
~ asked_for_items = true
- #give_item:lockpick
- ~ trust_level = trust_level + 1
- Good luck out there!
-> hub
- else:
- I need to trust you more before I give you something like that.
- Build up some trust first - ask me questions or help me out!
+ I have some items, but I need to trust you more first.
+ Build up some trust - ask me questions!
-> hub
}
}
=== other_items ===
# speaker:npc
-{trust_level >= 4:
- I've got a keycard for restricted areas. Think you can use it responsibly?
- #give_item:keycard
- ~ trust_level = trust_level + 1
- Use it wisely!
- -> hub
-- else:
- That's all I have right now. The lockpick set is your best tool for now.
- -> hub
-}
+I think I gave you most of what I had. Check your inventory!
+-> hub
=== lockpick_feedback ===
Great! I'm glad it helped you out. That's what I'm here for.
@@ -150,8 +139,8 @@ What else do you need?
{has_unlocked_ceo:
The CEO's office has evidence you're looking for. Search the desk thoroughly.
Also, check any computers for sensitive files.
-- else:
- {has_given_lockpick:
+|- else:
+ {has_lockpick:
Try using that lockpick set on locked doors and containers around the building.
You never know what secrets people hide behind locked doors!
- else:
@@ -170,29 +159,29 @@ Good luck!
// Triggered when player picks up the lockpick
=== on_lockpick_pickup ===
-{has_given_lockpick:
+{has_lockpick:
Great! You found the lockpick I gave you. Try it on a locked door or container!
-- else:
- Nice find! That lockpick set looks professional. Could be very useful. π
+|- else:
+ Nice find! That lockpick set looks professional. Could be very useful.
}
-> hub
// Triggered when player completes any lockpicking minigame
=== on_lockpick_success ===
~ saw_lockpick_used = true
-{has_given_lockpick:
- Excellent! Glad I could help you get through that. π―
-- else:
- Nice work getting through that lock! π
+{has_lockpick:
+ Excellent! Glad I could help you get through that.
+|- else:
+ Nice work getting through that lock!
}
-> hub
// Triggered when player fails a lockpicking attempt
=== on_lockpick_failed ===
-{has_given_lockpick:
- Don't give up! Lockpicking takes practice. Try adjusting the tension. π§
+{has_lockpick:
+ Don't give up! Lockpicking takes practice. Try adjusting the tension.
Want me to help you with anything else?
-- else:
+|- else:
Tough break. Lockpicking isn't easy without the right tools...
I might be able to help with that if you ask.
}
@@ -202,18 +191,18 @@ Good luck!
=== on_door_unlocked ===
~ saw_door_unlock = true
{has_unlocked_ceo:
- Another door open! You're making great progress. πͺβ
-- else:
+ Another door open! You're making great progress.
+|- else:
Nice! You found a way through that door. Keep going!
}
-> hub
// Triggered when player tries a locked door
=== on_door_attempt ===
-That door's locked tight. You'll need to find a way to unlock it. π
+That door's locked tight. You'll need to find a way to unlock it.
{trust_level >= 2:
Want me to help you out? Just ask!
-- else:
+|- else:
{trust_level >= 1:
I might be able to help if you get to know me better first.
}
@@ -223,9 +212,9 @@ That door's locked tight. You'll need to find a way to unlock it. π
// Triggered when player interacts with the CEO desk
=== on_ceo_desk_interact ===
{has_unlocked_ceo:
- The CEO's desk - you made it! Nice work. π
+ The CEO's desk - you made it! Nice work.
That's where the important evidence is kept.
-- else:
+|- else:
Trying to get into the CEO's office? I might be able to help with that...
}
-> hub
@@ -233,20 +222,20 @@ That door's locked tight. You'll need to find a way to unlock it. π
// Triggered when player picks up any item
=== on_item_found ===
{trust_level >= 1:
- Good find! Every item could be important for your mission. π¦
+ Good find! Every item could be important for your mission.
}
-> hub
// Triggered when player enters any room (general progress check)
=== on_room_entered ===
{has_unlocked_ceo:
- Keep searching for that evidence! π
-- else:
+ Keep searching for that evidence!
+|- else:
{trust_level >= 1:
- You're making progress through the building. πΆ
+ You're making progress through the building.
Let me know if you need help with anything.
- else:
- Exploring new areas... πΆ
+ Exploring new areas...
}
}
-> hub
@@ -254,13 +243,13 @@ That door's locked tight. You'll need to find a way to unlock it. π
// Triggered when player discovers a new room for the first time
=== on_room_discovered ===
{trust_level >= 2:
- Great find! This new area might have what we need. πΊοΈβ¨
+ Great find! This new area might have what we need.
Search it thoroughly!
-- else:
+|- else:
{trust_level >= 1:
- Interesting! You've found a new area. Be careful exploring. πΊοΈ
+ Interesting! You've found a new area. Be careful exploring.
- else:
- A new room... wonder what's inside. πͺ
+ A new room... wonder what's inside.
}
}
-> hub
@@ -268,12 +257,11 @@ That door's locked tight. You'll need to find a way to unlock it. π
// Triggered when player enters the CEO office
=== on_ceo_office_entered ===
{has_unlocked_ceo:
- You're in! Remember, you're looking for evidence of the data breach. π΅οΈ
+ You're in! Remember, you're looking for evidence of the data breach.
Check the desk, computer, and any drawers.
-- else:
- Whoa, you got into the CEO's office! That's impressive! π
+|- else:
+ Whoa, you got into the CEO's office! That's impressive!
~ trust_level = trust_level + 1
Maybe I underestimated you. Impressive work!
}
-> hub
-
diff --git a/scenarios/ink/helper-npc.json b/scenarios/ink/helper-npc.json
index 0d7f694..7a940bc 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":["#","^speaker:npc","/#","^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},{"->":"hub"},null],"hub":[["ev",{"VAR?":"asked_about_self"},"!","/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^Who are you?","/str","/ev",{"*":".^.c-0","flg":20},{"->":"hub.0.5"},{"c-0":["\n","ev",true,"/ev",{"VAR=":"asked_about_self","re":true},{"->":"who_are_you"},{"#f":5}]}]}],"nop","\n","ev",{"VAR?":"asked_about_self"},{"VAR?":"has_unlocked_ceo"},"!","&&","/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^Can you help me get into the CEO's office?","/str","/ev",{"*":".^.c-0","flg":4},{"->":"hub.0.14"},{"c-0":["\n",{"->":"help_ceo_office"},null]}]}],"nop","\n","ev",{"VAR?":"has_unlocked_ceo"},"/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^Any other doors you need help with?","/str","/ev",{"*":".^.c-0","flg":4},{"->":"hub.0.20"},{"c-0":["\n",{"->":"other_doors"},null]}]}],"nop","\n","ev",{"VAR?":"asked_about_self"},{"VAR?":"has_given_lockpick"},"!","&&","/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^Do you have any items for me?","/str","/ev",{"*":".^.c-0","flg":4},{"->":"hub.0.29"},{"c-0":["\n",{"->":"give_items"},null]}]}],"nop","\n","ev",{"VAR?":"has_given_lockpick"},"/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^Got any other items for me?","/str","/ev",{"*":".^.c-0","flg":4},{"->":"hub.0.35"},{"c-0":["\n",{"->":"other_items"},null]}]}],"nop","\n","ev",{"VAR?":"saw_lockpick_used"},"/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^Thanks for the lockpick! It worked great.","/str","/ev",{"*":".^.c-0","flg":4},{"->":"hub.0.41"},{"c-0":["\n",{"->":"lockpick_feedback"},null]}]}],"nop","\n","ev",{"VAR?":"trust_level"},3,">=","/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^What hints do you have for me?","/str","/ev",{"*":".^.c-0","flg":4},{"->":"hub.0.49"},{"c-0":["\n",{"->":"give_hints"},null]}]}],"nop","\n","ev","str","^Thanks, I'm good for now.","/str","/ev",{"*":".^.c-0","flg":4},{"c-0":["^ ","#","^exit_conversation","/#","\n","#","^speaker:npc","/#","^Alright then. Let me know if you need anything else!","\n",{"->":"hub"},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 when you need them.","\n","ev",{"VAR?":"trust_level"},1,"+","/ev",{"VAR=":"trust_level","re":true},"^What would you like to do?","\n",{"->":"hub"},null],"help_ceo_office":["#","^speaker:npc","/#","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",{"->":"hub"},{"->":".^.^.^.8"},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 enough. Let me unlock that door for you.","\n","ev",true,"/ev",{"VAR=":"has_unlocked_ceo","re":true},"ev",true,"/ev",{"VAR=":"asked_about_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},"^What else can I help with?","\n",{"->":"hub"},{"->":".^.^.^.10"},null]}],[{"->":".^.b"},{"b":["\n","^I don't know you well enough yet. Ask me some questions first and we can build some trust.","\n",{"->":"hub"},{"->":".^.^.^.10"},null]}],"nop","\n",{"->":".^.^.^.8"},null]}],"nop","\n",null],"other_doors":["#","^speaker:npc","/#","^What other doors do you need help with? I can try to unlock them if you tell me which ones.","\n","ev",{"VAR?":"trust_level"},1,"+","/ev",{"VAR=":"trust_level","re":true},"^Let me know!","\n",{"->":"hub"},null],"give_items":["#","^speaker:npc","/#","ev",{"VAR?":"has_given_lockpick"},"/ev",[{"->":".^.b","c":true},{"b":["\n","^I already gave you a lockpick set. Check your inventory - it should be there!","\n",{"->":"hub"},{"->":".^.^.^.8"},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 to open locked doors and containers! π","\n","ev",true,"/ev",{"VAR=":"has_given_lockpick","re":true},"ev",true,"/ev",{"VAR=":"asked_for_items","re":true},"#","^give_item:lockpick","/#","ev",{"VAR?":"trust_level"},1,"+","/ev",{"VAR=":"trust_level","re":true},"^Good luck out there!","\n",{"->":"hub"},{"->":".^.^.^.10"},null]}],[{"->":".^.b"},{"b":["\n","^I need to trust you more before I give you something like that.","\n","^Build up some trust first - ask me questions or help me out!","\n",{"->":"hub"},{"->":".^.^.^.10"},null]}],"nop","\n",{"->":".^.^.^.8"},null]}],"nop","\n",null],"other_items":["#","^speaker:npc","/#","ev",{"VAR?":"trust_level"},4,">=","/ev",[{"->":".^.b","c":true},{"b":["\n","^I've got a keycard for restricted areas. Think you can use it responsibly?","\n","#","^give_item:keycard","/#","ev",{"VAR?":"trust_level"},1,"+","/ev",{"VAR=":"trust_level","re":true},"^Use it wisely!","\n",{"->":"hub"},{"->":".^.^.^.10"},null]}],[{"->":".^.b"},{"b":["\n","^That's all I have right now. The lockpick set is your best tool for now.","\n",{"->":"hub"},{"->":".^.^.^.10"},null]}],"nop","\n",null],"lockpick_feedback":["^Great! I'm glad it helped you out. That's what I'm here for.","\n","^You're doing excellent work on this mission.","\n","ev",{"VAR?":"trust_level"},1,"+","/ev",{"VAR=":"trust_level","re":true},"ev",false,"/ev",{"VAR=":"saw_lockpick_used","re":true},"^What else do you need?","\n",{"->":"hub"},null],"give_hints":["ev",{"VAR?":"has_unlocked_ceo"},"/ev",[{"->":".^.b","c":true},{"b":["\n","^The CEO's office has evidence you're looking for. Search the desk thoroughly.","\n","^Also, check any computers for sensitive files.","\n",{"->":".^.^.^.5"},null]}],[{"->":".^.b"},{"b":["\n","ev",{"VAR?":"has_given_lockpick"},"/ev",[{"->":".^.b","c":true},{"b":["\n","^Try using that lockpick set on locked doors and containers around the building.","\n","^You never know what secrets people hide behind locked doors!","\n",{"->":".^.^.^.6"},null]}],[{"->":".^.b"},{"b":["\n","^Explore every room carefully. Items are often hidden in places you'd least expect.","\n",{"->":".^.^.^.6"},null]}],"nop","\n",{"->":".^.^.^.5"},null]}],"nop","\n","^Good luck!","\n",{"->":"hub"},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",{"->":"hub"},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",{"->":"hub"},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","^Want me to help you with anything else?","\n",{"->":".^.^.^.5"},null]}],[{"->":".^.b"},{"b":["\n","^Tough break. Lockpicking isn't easy without the right tools...","\n","^I might be able to help with that if you ask.","\n",{"->":".^.^.^.5"},null]}],"nop","\n",{"->":"hub"},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",{"->":"hub"},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",{"->":".^.^.^.9"},null]}],[{"->":".^.b"},{"b":["\n","ev",{"VAR?":"trust_level"},1,">=","/ev",[{"->":".^.b","c":true},{"b":["\n","^I might be able to help if you get to know me better first.","\n",{"->":".^.^.^.7"},null]}],"nop","\n",{"->":".^.^.^.9"},null]}],"nop","\n",{"->":"hub"},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","^That's where the important evidence is kept.","\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",{"->":"hub"},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",{"->":"hub"},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","^Let me know if you need help with anything.","\n",{"->":".^.^.^.8"},null]}],[{"->":".^.b"},{"b":["\n","^Exploring new areas... πΆ","\n",{"->":".^.^.^.8"},null]}],"nop","\n",{"->":".^.^.^.5"},null]}],"nop","\n",{"->":"hub"},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","^Search it thoroughly!","\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",{"->":"hub"},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","^Check the desk, computer, and any drawers.","\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},"^Maybe I underestimated you. Impressive work!","\n",{"->":".^.^.^.5"},null]}],"nop","\n",{"->":"hub"},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"},false,{"VAR=":"asked_about_self"},false,{"VAR=":"asked_about_ceo"},false,{"VAR=":"asked_for_items"},"/ev","end",null]}],"listDefs":{}}
\ No newline at end of file
+{"inkVersion":21,"root":[[["done",{"#n":"g-0"}],null],"done",{"start":["#","^speaker:npc","/#","^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},{"->":"hub"},null],"hub":[["ev",{"VAR?":"asked_about_self"},"!","/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^Who are you?","/str","/ev",{"*":".^.c-0","flg":20},{"->":"hub.0.5"},{"c-0":["\n","ev",true,"/ev",{"VAR=":"asked_about_self","re":true},{"->":"who_are_you"},{"#f":5}]}]}],"nop","\n","ev",{"VAR?":"asked_about_self"},{"VAR?":"has_unlocked_ceo"},"!","&&","/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^Can you help me get into the CEO's office?","/str","/ev",{"*":".^.c-0","flg":4},{"->":"hub.0.14"},{"c-0":["\n",{"->":"help_ceo_office"},null]}]}],"nop","\n","ev",{"VAR?":"has_unlocked_ceo"},"/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^Any other doors you need help with?","/str","/ev",{"*":".^.c-0","flg":4},{"->":"hub.0.20"},{"c-0":["\n",{"->":"other_doors"},null]}]}],"nop","\n","ev",{"VAR?":"asked_about_self"},{"VAR?":"has_given_lockpick"},"!","&&","/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^Do you have any items for me?","/str","/ev",{"*":".^.c-0","flg":4},{"->":"hub.0.29"},{"c-0":["\n",{"->":"give_items"},null]}]}],"nop","\n","ev",{"VAR?":"has_given_lockpick"},"/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^Got any other items for me?","/str","/ev",{"*":".^.c-0","flg":4},{"->":"hub.0.35"},{"c-0":["\n",{"->":"other_items"},null]}]}],"nop","\n","ev",{"VAR?":"saw_lockpick_used"},"/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^Thanks for the lockpick! It worked great.","/str","/ev",{"*":".^.c-0","flg":4},{"->":"hub.0.41"},{"c-0":["\n",{"->":"lockpick_feedback"},null]}]}],"nop","\n","ev",{"VAR?":"trust_level"},3,">=","/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^What hints do you have for me?","/str","/ev",{"*":".^.c-0","flg":4},{"->":"hub.0.49"},{"c-0":["\n",{"->":"give_hints"},null]}]}],"nop","\n","ev","str","^Thanks, I'm good for now.","/str","/ev",{"*":".^.c-0","flg":4},{"c-0":["^ ","#","^exit_conversation","/#","\n","#","^speaker:npc","/#","^Alright then. Let me know if you need anything else!","\n",{"->":"hub"},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 when you need them.","\n","ev",{"VAR?":"trust_level"},1,"+","/ev",{"VAR=":"trust_level","re":true},"^What would you like to do?","\n",{"->":"hub"},null],"help_ceo_office":["#","^speaker:npc","/#","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",{"->":"hub"},{"->":".^.^.^.8"},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 enough. Let me unlock that door for you.","\n","ev",true,"/ev",{"VAR=":"has_unlocked_ceo","re":true},"ev",true,"/ev",{"VAR=":"asked_about_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},"^What else can I help with?","\n",{"->":"hub"},{"->":".^.^.^.10"},null]}],[{"->":".^.b"},{"b":["\n","^I don't know you well enough yet. Ask me some questions first and we can build some trust.","\n",{"->":"hub"},{"->":".^.^.^.10"},null]}],"nop","\n",{"->":".^.^.^.8"},null]}],"nop","\n",null],"other_doors":["#","^speaker:npc","/#","^What other doors do you need help with? I can try to unlock them if you tell me which ones.","\n","ev",{"VAR?":"trust_level"},1,"+","/ev",{"VAR=":"trust_level","re":true},"^Let me know!","\n",{"->":"hub"},null],"give_items":["#","^speaker:npc","/#","ev",{"VAR?":"trust_level"},2,">=","/ev",[{"->":".^.b","c":true},{"b":["\n","^Let me see what I have available...","\n","ev",{"VAR?":"has_lockpick"},{"VAR?":"has_workstation"},"||",{"VAR?":"has_phone"},"||",{"VAR?":"has_bluetooth_scanner"},"||",{"VAR?":"has_fingerprint_kit"},"||",{"VAR?":"has_pin_cracker"},"||",{"VAR?":"has_keycard"},"||",{"VAR?":"has_key"},"||","/ev",[{"->":".^.b","c":true},{"b":["\n","^Here's what I can offer you:","\n","ev",{"VAR?":"has_lockpick"},"/ev",[{"->":".^.b","c":true},{"b":["^ β’ Lock Pick Kit - for opening locked doors and containers π",{"->":".^.^.^.7"},null]}],"nop","\n","ev",{"VAR?":"has_workstation"},"/ev",[{"->":".^.b","c":true},{"b":["^ β’ Crypto Analysis Station - for cryptographic challenges π»",{"->":".^.^.^.13"},null]}],"nop","\n","ev",{"VAR?":"has_phone"},"/ev",[{"->":".^.b","c":true},{"b":["^ β’ Phone - with interesting contacts π±",{"->":".^.^.^.19"},null]}],"nop","\n","ev",{"VAR?":"has_bluetooth_scanner"},"/ev",[{"->":".^.b","c":true},{"b":["^ β’ Bluetooth Scanner - for finding nearby devices π‘",{"->":".^.^.^.25"},null]}],"nop","\n","ev",{"VAR?":"has_fingerprint_kit"},"/ev",[{"->":".^.b","c":true},{"b":["^ β’ Fingerprint Kit - for forensic analysis π",{"->":".^.^.^.31"},null]}],"nop","\n","ev",{"VAR?":"has_pin_cracker"},"/ev",[{"->":".^.b","c":true},{"b":["^ β’ PIN Cracker - for bypassing numeric locks π’",{"->":".^.^.^.37"},null]}],"nop","\n","ev",{"VAR?":"has_keycard"},"/ev",[{"->":".^.b","c":true},{"b":["^ β’ Keycard - for restricted areas π«",{"->":".^.^.^.43"},null]}],"nop","\n","ev",{"VAR?":"has_key"},"/ev",[{"->":".^.b","c":true},{"b":["^ β’ Key - might be useful π",{"->":".^.^.^.49"},null]}],"nop","\n","^What would you like?","\n","ev",{"VAR?":"has_lockpick"},"/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^I'll take the Lock Pick Kit","/str","/ev",{"*":".^.c-0","flg":4},{"->":".^.^.^.57"},{"c-0":["\n",{"->":"give_lockpick"},null]}]}],"nop","\n","ev",{"VAR?":"has_workstation"},"/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^I'll take the Crypto Analysis Station","/str","/ev",{"*":".^.c-0","flg":4},{"->":".^.^.^.63"},{"c-0":["\n",{"->":"give_workstation"},null]}]}],"nop","\n","ev",{"VAR?":"has_phone"},"/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^I'll take the Phone","/str","/ev",{"*":".^.c-0","flg":4},{"->":".^.^.^.69"},{"c-0":["\n",{"->":"give_phone"},null]}]}],"nop","\n","ev",{"VAR?":"has_bluetooth_scanner"},"/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^I'll take the Bluetooth Scanner","/str","/ev",{"*":".^.c-0","flg":4},{"->":".^.^.^.75"},{"c-0":["\n",{"->":"give_bluetooth_scanner"},null]}]}],"nop","\n","ev",{"VAR?":"has_fingerprint_kit"},"/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^I'll take the Fingerprint Kit","/str","/ev",{"*":".^.c-0","flg":4},{"->":".^.^.^.81"},{"c-0":["\n",{"->":"give_fingerprint_kit"},null]}]}],"nop","\n","ev",{"VAR?":"has_pin_cracker"},"/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^I'll take the PIN Cracker","/str","/ev",{"*":".^.c-0","flg":4},{"->":".^.^.^.87"},{"c-0":["\n",{"->":"give_pin_cracker"},null]}]}],"nop","\n","ev",{"VAR?":"has_keycard"},"/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^I'll take the Keycard","/str","/ev",{"*":".^.c-0","flg":4},{"->":".^.^.^.93"},{"c-0":["\n",{"->":"give_keycard"},null]}]}],"nop","\n","ev",{"VAR?":"has_key"},"/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^I'll take the Key","/str","/ev",{"*":".^.c-0","flg":4},{"->":".^.^.^.99"},{"c-0":["\n",{"->":"give_key"},null]}]}],"nop","\n","ev","str","^Actually, never mind","/str","/ev",{"*":".^.c-0","flg":4},{"->":".^.^.^.22"},{"c-0":["\n",{"->":"hub"},null]}]}],[{"->":".^.b"},{"b":["\n","^I don't have any items to give you right now. Sorry!","\n",{"->":"hub"},{"->":".^.^.^.22"},null]}],"nop","\n",{"->":".^.^.^.10"},null]}],[{"->":".^.b"},{"b":["\n","^I need to trust you more before I give you something like that.","\n","^Build up some trust first - ask me questions or help me out!","\n",{"->":"hub"},{"->":".^.^.^.10"},null]}],"nop","\n",null],"give_lockpick":["#","^speaker:npc","/#","^Here's a lockpick set. Use it to open locked doors and containers! π","\n","ev",true,"/ev",{"VAR=":"has_given_lockpick","re":true},"ev",true,"/ev",{"VAR=":"asked_for_items","re":true},"#","^give_item:lockpick","/#","ev",{"VAR?":"trust_level"},1,"+","/ev",{"VAR=":"trust_level","re":true},"^Good luck out there!","\n",{"->":"hub"},null],"give_workstation":["#","^speaker:npc","/#","^I've got a crypto analysis workstation for you. This should help with any cryptographic challenges you encounter.","\n","ev",true,"/ev",{"VAR=":"asked_for_items","re":true},"#","^give_item:workstation","/#","ev",{"VAR?":"trust_level"},1,"+","/ev",{"VAR=":"trust_level","re":true},"^Use it well!","\n",{"->":"hub"},null],"give_phone":["#","^speaker:npc","/#","^Here's a phone. It has some interesting contacts you might find useful.","\n","ev",true,"/ev",{"VAR=":"asked_for_items","re":true},"#","^give_item:phone","/#","ev",{"VAR?":"trust_level"},1,"+","/ev",{"VAR=":"trust_level","re":true},"^Keep in touch!","\n",{"->":"hub"},null],"give_bluetooth_scanner":["#","^speaker:npc","/#","^I have a Bluetooth scanner. Use it to find and connect to nearby devices.","\n","ev",true,"/ev",{"VAR=":"asked_for_items","re":true},"#","^give_item:bluetooth_scanner","/#","ev",{"VAR?":"trust_level"},1,"+","/ev",{"VAR=":"trust_level","re":true},"^Happy scanning!","\n",{"->":"hub"},null],"give_fingerprint_kit":["#","^speaker:npc","/#","^Here's a fingerprint kit. Useful for forensic analysis.","\n","ev",true,"/ev",{"VAR=":"asked_for_items","re":true},"#","^give_item:fingerprint_kit","/#","ev",{"VAR?":"trust_level"},1,"+","/ev",{"VAR=":"trust_level","re":true},"^Handle with care!","\n",{"->":"hub"},null],"give_pin_cracker":["#","^speaker:npc","/#","^I've got a PIN cracker tool. This can help you bypass numeric locks.","\n","ev",true,"/ev",{"VAR=":"asked_for_items","re":true},"#","^give_item:pin-cracker","/#","ev",{"VAR?":"trust_level"},1,"+","/ev",{"VAR=":"trust_level","re":true},"^Use responsibly!","\n",{"->":"hub"},null],"give_keycard":["#","^speaker:npc","/#","^Here's a keycard for restricted areas. Think you can use it responsibly?","\n","ev",true,"/ev",{"VAR=":"asked_for_items","re":true},"#","^give_item:keycard","/#","ev",{"VAR?":"trust_level"},1,"+","/ev",{"VAR=":"trust_level","re":true},"^Use it wisely!","\n",{"->":"hub"},null],"give_key":["#","^speaker:npc","/#","^I have a key that might be useful. Take it!","\n","ev",true,"/ev",{"VAR=":"asked_for_items","re":true},"#","^give_item:key","/#","ev",{"VAR?":"trust_level"},1,"+","/ev",{"VAR=":"trust_level","re":true},{"->":"hub"},null],"other_items":["#","^speaker:npc","/#","ev",{"VAR?":"trust_level"},4,">=","/ev",[{"->":".^.b","c":true},{"b":["\n","^Let me see what else I have available...","\n","ev",{"VAR?":"has_workstation"},{"VAR?":"has_phone"},"||",{"VAR?":"has_bluetooth_scanner"},"||",{"VAR?":"has_fingerprint_kit"},"||",{"VAR?":"has_pin_cracker"},"||",{"VAR?":"has_keycard"},"||",{"VAR?":"has_key"},"||","/ev",[{"->":".^.b","c":true},{"b":["\n","^Here's what else I can offer:","\n","ev",{"VAR?":"has_workstation"},"/ev",[{"->":".^.b","c":true},{"b":["^ β’ Crypto Analysis Station - for cryptographic challenges π»",{"->":".^.^.^.7"},null]}],"nop","\n","ev",{"VAR?":"has_phone"},"/ev",[{"->":".^.b","c":true},{"b":["^ β’ Phone - with interesting contacts π±",{"->":".^.^.^.13"},null]}],"nop","\n","ev",{"VAR?":"has_bluetooth_scanner"},"/ev",[{"->":".^.b","c":true},{"b":["^ β’ Bluetooth Scanner - for finding nearby devices π‘",{"->":".^.^.^.19"},null]}],"nop","\n","ev",{"VAR?":"has_fingerprint_kit"},"/ev",[{"->":".^.b","c":true},{"b":["^ β’ Fingerprint Kit - for forensic analysis π",{"->":".^.^.^.25"},null]}],"nop","\n","ev",{"VAR?":"has_pin_cracker"},"/ev",[{"->":".^.b","c":true},{"b":["^ β’ PIN Cracker - for bypassing numeric locks π’",{"->":".^.^.^.31"},null]}],"nop","\n","ev",{"VAR?":"has_keycard"},"/ev",[{"->":".^.b","c":true},{"b":["^ β’ Keycard - for restricted areas π«",{"->":".^.^.^.37"},null]}],"nop","\n","ev",{"VAR?":"has_key"},"/ev",[{"->":".^.b","c":true},{"b":["^ β’ Key - might be useful π",{"->":".^.^.^.43"},null]}],"nop","\n","^What would you like?","\n","ev",{"VAR?":"has_workstation"},"/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^I'll take the Crypto Analysis Station","/str","/ev",{"*":".^.c-0","flg":4},{"->":".^.^.^.51"},{"c-0":["\n",{"->":"give_workstation"},null]}]}],"nop","\n","ev",{"VAR?":"has_phone"},"/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^I'll take the Phone","/str","/ev",{"*":".^.c-0","flg":4},{"->":".^.^.^.57"},{"c-0":["\n",{"->":"give_phone"},null]}]}],"nop","\n","ev",{"VAR?":"has_bluetooth_scanner"},"/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^I'll take the Bluetooth Scanner","/str","/ev",{"*":".^.c-0","flg":4},{"->":".^.^.^.63"},{"c-0":["\n",{"->":"give_bluetooth_scanner"},null]}]}],"nop","\n","ev",{"VAR?":"has_fingerprint_kit"},"/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^I'll take the Fingerprint Kit","/str","/ev",{"*":".^.c-0","flg":4},{"->":".^.^.^.69"},{"c-0":["\n",{"->":"give_fingerprint_kit"},null]}]}],"nop","\n","ev",{"VAR?":"has_pin_cracker"},"/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^I'll take the PIN Cracker","/str","/ev",{"*":".^.c-0","flg":4},{"->":".^.^.^.75"},{"c-0":["\n",{"->":"give_pin_cracker"},null]}]}],"nop","\n","ev",{"VAR?":"has_keycard"},"/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^I'll take the Keycard","/str","/ev",{"*":".^.c-0","flg":4},{"->":".^.^.^.81"},{"c-0":["\n",{"->":"give_keycard"},null]}]}],"nop","\n","ev",{"VAR?":"has_key"},"/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^I'll take the Key","/str","/ev",{"*":".^.c-0","flg":4},{"->":".^.^.^.87"},{"c-0":["\n",{"->":"give_key"},null]}]}],"nop","\n","ev","str","^Actually, never mind","/str","/ev",{"*":".^.c-0","flg":4},{"->":".^.^.^.20"},{"c-0":["\n",{"->":"hub"},null]}]}],[{"->":".^.b"},{"b":["\n","^That's all I have right now. Sorry!","\n",{"->":"hub"},{"->":".^.^.^.20"},null]}],"nop","\n",{"->":".^.^.^.10"},null]}],[{"->":".^.b"},{"b":["\n","^That's all I have right now. The lockpick set is your best tool for now.","\n",{"->":"hub"},{"->":".^.^.^.10"},null]}],"nop","\n",null],"lockpick_feedback":["^Great! I'm glad it helped you out. That's what I'm here for.","\n","^You're doing excellent work on this mission.","\n","ev",{"VAR?":"trust_level"},1,"+","/ev",{"VAR=":"trust_level","re":true},"ev",false,"/ev",{"VAR=":"saw_lockpick_used","re":true},"^What else do you need?","\n",{"->":"hub"},null],"give_hints":["ev",{"VAR?":"has_unlocked_ceo"},"/ev",[{"->":".^.b","c":true},{"b":["\n","^The CEO's office has evidence you're looking for. Search the desk thoroughly.","\n","^Also, check any computers for sensitive files.","\n",{"->":".^.^.^.5"},null]}],[{"->":".^.b"},{"b":["\n","ev",{"VAR?":"has_given_lockpick"},"/ev",[{"->":".^.b","c":true},{"b":["\n","^Try using that lockpick set on locked doors and containers around the building.","\n","^You never know what secrets people hide behind locked doors!","\n",{"->":".^.^.^.6"},null]}],[{"->":".^.b"},{"b":["\n","^Explore every room carefully. Items are often hidden in places you'd least expect.","\n",{"->":".^.^.^.6"},null]}],"nop","\n",{"->":".^.^.^.5"},null]}],"nop","\n","^Good luck!","\n",{"->":"hub"},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",{"->":"hub"},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",{"->":"hub"},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","^Want me to help you with anything else?","\n",{"->":".^.^.^.5"},null]}],[{"->":".^.b"},{"b":["\n","^Tough break. Lockpicking isn't easy without the right tools...","\n","^I might be able to help with that if you ask.","\n",{"->":".^.^.^.5"},null]}],"nop","\n",{"->":"hub"},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",{"->":"hub"},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",{"->":".^.^.^.9"},null]}],[{"->":".^.b"},{"b":["\n","ev",{"VAR?":"trust_level"},1,">=","/ev",[{"->":".^.b","c":true},{"b":["\n","^I might be able to help if you get to know me better first.","\n",{"->":".^.^.^.7"},null]}],"nop","\n",{"->":".^.^.^.9"},null]}],"nop","\n",{"->":"hub"},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","^That's where the important evidence is kept.","\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",{"->":"hub"},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",{"->":"hub"},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","^Let me know if you need help with anything.","\n",{"->":".^.^.^.8"},null]}],[{"->":".^.b"},{"b":["\n","^Exploring new areas... πΆ","\n",{"->":".^.^.^.8"},null]}],"nop","\n",{"->":".^.^.^.5"},null]}],"nop","\n",{"->":"hub"},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","^Search it thoroughly!","\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",{"->":"hub"},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","^Check the desk, computer, and any drawers.","\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},"^Maybe I underestimated you. Impressive work!","\n",{"->":".^.^.^.5"},null]}],"nop","\n",{"->":"hub"},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"},false,{"VAR=":"asked_about_self"},false,{"VAR=":"asked_about_ceo"},false,{"VAR=":"asked_for_items"},false,{"VAR=":"has_phone"},false,{"VAR=":"has_lockpick"},false,{"VAR=":"has_workstation"},false,{"VAR=":"has_bluetooth_scanner"},false,{"VAR=":"has_fingerprint_kit"},false,{"VAR=":"has_pin_cracker"},false,{"VAR=":"has_keycard"},false,{"VAR=":"has_key"},"/ev","end",null]}],"listDefs":{}}
\ No newline at end of file
diff --git a/scenarios/ink/test2.ink b/scenarios/ink/test2.ink
index da46fcd..d29f6cd 100644
--- a/scenarios/ink/test2.ink
+++ b/scenarios/ink/test2.ink
@@ -10,42 +10,42 @@ Woop! Welcome! This is a group conversation test. Let me introduce you to my col
=== group_meeting ===
# speaker:npc:test_npc_back
Agent, meet my colleague from the back office. BACK
-+ [Continue] -> colleague_introduction
+-> colleague_introduction
=== colleague_introduction ===
# speaker:npc:test_npc_front
Nice to meet you! I'm the lead technician here. FRONT.
-+ [Ask about their work] -> player_question
+-> player_question
=== player_question ===
# speaker:player
What kind of work do you both do here?
-+ [Listen] -> front_npc_explains
+-> front_npc_explains
=== front_npc_explains ===
# speaker:npc:test_npc_back
Well, I handle the front desk operations and guest interactions. But my colleague here...
-+ [Continue listening] -> colleague_responds
+-> colleague_responds
=== colleague_responds ===
# speaker:npc:test_npc_front
I manage all the backend systems and security infrastructure. Together, we keep everything running smoothly.
-+ [Respond] -> player_follow_up
+-> player_follow_up
=== player_follow_up ===
# speaker:player
That sounds like a well-coordinated operation!
-+ [Listen more] -> front_npc_agrees
+-> front_npc_agrees
=== front_npc_agrees ===
# speaker:npc:test_npc_back
It really is! We've been working together for several years now. Communication is key.
-+ [Hear more] -> colleague_adds
+-> colleague_adds
=== colleague_adds ===
# speaker:npc:test_npc_front
Exactly. And we're always looking for talented people like you to join our team.
-+ [Respond] -> player_closing
+-> player_closing
=== player_closing ===
# speaker:player
diff --git a/scenarios/npc-sprite-test2.json b/scenarios/npc-sprite-test2.json
index d7ef4f4..3488321 100644
--- a/scenarios/npc-sprite-test2.json
+++ b/scenarios/npc-sprite-test2.json
@@ -22,7 +22,7 @@
"npcs": [
{
"id": "test_npc_front",
- "displayName": "Front NPC",
+ "displayName": "Helper NPC",
"npcType": "person",
"position": { "x": 5, "y": 3 },
"spriteSheet": "hacker-red",
@@ -32,7 +32,29 @@
"idleFrameEnd": 23
},
"storyPath": "scenarios/ink/helper-npc.json",
- "currentKnot": "start"
+ "currentKnot": "start",
+ "itemsHeld": [
+ {
+ "type": "phone",
+ "name": "Your Phone",
+ "takeable": true,
+ "phoneId": "player_phone",
+ "npcIds": ["neye_eve", "gossip_girl", "helper_npc"],
+ "observations": "Your personal phone with some interesting contacts"
+ },
+ {
+ "type": "workstation",
+ "name": "Crypto Analysis Station",
+ "takeable": true,
+ "observations": "A powerful workstation for cryptographic analysis"
+ },
+ {
+ "type": "lockpick",
+ "name": "Lock Pick Kit",
+ "takeable": true,
+ "observations": "A professional lock picking kit with various picks and tension wrenches"
+ }
+ ]
},
{
"id": "test_npc_back",
@@ -47,9 +69,9 @@
"storyPath": "scenarios/ink/test2.json",
"currentKnot": "hub",
"timedConversation": {
- "delay": 3000,
+ "delay": 100,
"targetKnot": "group_meeting",
- "background": "assets/mini-games/desktop-wallpaper.png"
+ "background": "assets/backgrounds/hq1.png"
}
}
]