diff --git a/assets/rooms/room_reception2.json b/assets/rooms/room_reception2.json index 896bbef..d816d86 100644 --- a/assets/rooms/room_reception2.json +++ b/assets/rooms/room_reception2.json @@ -145,7 +145,7 @@ "gid":230, "height":17, "id":47, - "name":"", + "name":"phone", "rotation":0, "type":"", "visible":true, diff --git a/assets/rooms/room_reception2.tmj b/assets/rooms/room_reception2.tmj index 757bb24..e2f4262 100644 --- a/assets/rooms/room_reception2.tmj +++ b/assets/rooms/room_reception2.tmj @@ -153,7 +153,7 @@ "gid":230, "height":17, "id":47, - "name":"", + "name":"phone", "rotation":0, "type":"", "visible":true, diff --git a/assets/scenarios/ceo_exfil.json b/assets/scenarios/ceo_exfil.json index 03baf51..3efe4b0 100644 --- a/assets/scenarios/ceo_exfil.json +++ b/assets/scenarios/ceo_exfil.json @@ -1,5 +1,5 @@ { - "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.", + "scenario_brief": "old version. 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", "rooms": { "reception": { diff --git a/css/container-minigame.css b/css/container-minigame.css index 484fd0b..3168e3e 100644 --- a/css/container-minigame.css +++ b/css/container-minigame.css @@ -225,7 +225,6 @@ .desktop-taskbar { background: rgba(0, 0, 0, 0.8); - border-top: 2px solid #333; padding: 10px 20px; display: flex; justify-content: space-between; @@ -271,9 +270,6 @@ align-items: center; gap: 20px; padding: 20px; - background: rgba(255, 255, 255, 0.05); - border-radius: 10px; - border: 1px solid rgba(255, 255, 255, 0.1); } .container-image { @@ -374,10 +370,11 @@ image-rendering: -moz-crisp-edges; image-rendering: crisp-edges; transition: transform 0.2s ease; + transform: scale(2); } .container-content-item:hover { - transform: scale(1.1); + transform: scale(2.2); } .container-content-tooltip { diff --git a/css/inventory.css b/css/inventory.css index 7ce7985..55da451 100644 --- a/css/inventory.css +++ b/css/inventory.css @@ -80,7 +80,7 @@ color: white; padding: 4px 8px; border-radius: 4px; - font-size: 14px; + font-size: 18px; white-space: nowrap; pointer-events: none; opacity: 0; diff --git a/css/lockpicking.css b/css/lockpicking.css index f06f91c..ea26f85 100644 --- a/css/lockpicking.css +++ b/css/lockpicking.css @@ -27,3 +27,73 @@ .lockpick-feedback:empty { display: none; } + +/* Lockpicking item section styling */ +.lockpicking-item-section { + display: flex; + align-items: center; + gap: 20px; + padding: 20px; + background: rgba(0, 0, 0, 0.3); + margin-bottom: 20px; +} + +.lockpicking-item-image { + min-width: 80px; + min-height: 80px; + object-fit: contain; + image-rendering: pixelated; + image-rendering: -moz-crisp-edges; + image-rendering: crisp-edges; + border: 2px solid rgba(255, 255, 255, 0.3); + background: rgba(0, 0, 0, 0.3); + flex-shrink: 0; +} + +.lockpicking-item-info h4 { + font-family: 'Press Start 2P', monospace; + font-size: 20px; + margin: 0 0 10px 0; + color: #3498db; +} + +.lockpicking-item-info p { + font-size: 16px; + margin: 0; + color: #ecf0f1; + line-height: 1.4; +} + +/* Responsive adjustments */ +@media (max-width: 768px) { + .lockpicking-item-section { + flex-direction: column; + text-align: center; + gap: 15px; + } + + .lockpicking-item-image { + width: 70px; + height: 70px; + } + + .lockpicking-item-info h4 { + font-size: 16px; + } + + .lockpicking-item-info p { + font-size: 14px; + } +} + +/* Phaser game container styling - prevent margin/padding shifts */ +#phaser-game-container { + margin: 0 !important; + padding: 0 !important; +} + +#phaser-game-container canvas { + margin: 0 !important; + padding: 0 !important; + display: block; +} diff --git a/css/password-minigame.css b/css/password-minigame.css index d19a856..ce617ea 100644 --- a/css/password-minigame.css +++ b/css/password-minigame.css @@ -10,6 +10,38 @@ position: relative; } +.password-image-section { + display: flex; + align-items: center; + gap: 20px; + padding: 20px; +} + +.password-image { + width: 80px; + height: 80px; + object-fit: contain; + image-rendering: pixelated; + image-rendering: -moz-crisp-edges; + image-rendering: crisp-edges; + border: 2px solid rgba(255, 255, 255, 0.3); + background: rgba(0, 0, 0, 0.3); +} + +.password-info h4 { + font-family: 'Press Start 2P', monospace; + font-size: 20px; + margin: 0 0 10px 0; + color: #3498db; +} + +.password-info p { + font-size: 20px; + margin: 0; + color: #ecf0f1; + line-height: 1.4; +} + .password-input-container { display: flex; flex-direction: column; @@ -529,4 +561,16 @@ padding: 10px 20px; font-size: 18px; } + + .password-image-section { + flex-direction: column; + align-items: center; + gap: 10px; + padding: 10px; + } + + .password-image { + width: 60px; + height: 60px; + } } diff --git a/css/phone.css b/css/phone.css index 73a6815..e6154e1 100644 --- a/css/phone.css +++ b/css/phone.css @@ -1,4 +1,38 @@ /* Phone Messages Minigame Styles */ + +.phone-image-section { + display: flex; + align-items: center; + gap: 20px; + padding: 20px; + margin-bottom: 20px; +} + +.phone-image { + width: 80px; + height: 80px; + object-fit: contain; + image-rendering: pixelated; + image-rendering: -moz-crisp-edges; + image-rendering: crisp-edges; + border: 2px solid rgba(255, 255, 255, 0.3); + background: rgba(0, 0, 0, 0.3); +} + +.phone-info h4 { + font-family: 'Press Start 2P', monospace; + font-size: 20px; + margin: 0 0 10px 0; + color: #3498db; +} + +.phone-info p { + font-size: 20px; + margin: 0; + color: #ecf0f1; + line-height: 1.4; +} + .phone-messages-container { display: flex; flex-direction: column; @@ -7,7 +41,6 @@ max-width: 400px; margin: 0 auto; background: #a0a0ad; - border: 4px solid #333; clip-path: polygon( 0px calc(100% - 10px), 2px calc(100% - 10px), @@ -469,3 +502,4 @@ padding: 5px; font-weight: bold; } + diff --git a/js/minigames/container/container-minigame.js b/js/minigames/container/container-minigame.js index 94e5e93..fe77de1 100644 --- a/js/minigames/container/container-minigame.js +++ b/js/minigames/container/container-minigame.js @@ -92,7 +92,6 @@ export class ContainerMinigame extends MinigameScene {
${this.isTakeable ? '' : ''} -
`; @@ -100,6 +99,15 @@ export class ContainerMinigame extends MinigameScene { createDesktopUI() { this.gameContainer.innerHTML = ` +
+ ${this.containerItem.scenarioData.name} +
+

${this.containerItem.scenarioData.name}

+

${this.containerItem.scenarioData.observations || ''}

+
+
@@ -115,13 +123,8 @@ export class ContainerMinigame extends MinigameScene { ` : ''}
-
- ${this.containerItem.scenarioData.name} - ${this.containerItem.scenarioData.observations || ''} -
${this.isTakeable ? '' : ''} -
diff --git a/js/minigames/lockpicking/lockpicking-game-phaser.js b/js/minigames/lockpicking/lockpicking-game-phaser.js index 54709cf..8c5cd3c 100644 --- a/js/minigames/lockpicking/lockpicking-game-phaser.js +++ b/js/minigames/lockpicking/lockpicking-game-phaser.js @@ -251,9 +251,37 @@ export class LockpickingMinigamePhaser extends MinigameScene {

Apply tension and hold click on pins to lift them to the shear line

`; + // Create the lockable item display section if item info is provided + this.createLockableItemDisplay(); + this.setupPhaserGame(); } + createLockableItemDisplay() { + // Create display for the locked item (door, chest, etc.) + const itemName = this.params?.itemName || this.lockable || 'Locked Item'; + const itemImage = this.params?.itemImage || null; + const itemObservations = this.params?.itemObservations || ''; + + if (!itemImage) return; // Only create if image is provided + + // Create container for the item display + const itemDisplayDiv = document.createElement('div'); + itemDisplayDiv.className = 'lockpicking-item-section'; + itemDisplayDiv.innerHTML = ` + ${itemName} +
+

${itemName}

+

${itemObservations}

+
+ `; + + // Insert before the game container + this.gameContainer.parentElement.insertBefore(itemDisplayDiv, this.gameContainer); + } + setupPhaserGame() { // Create a container for the Phaser game this.gameContainer.innerHTML = ` @@ -3108,6 +3136,7 @@ export class LockpickingMinigamePhaser extends MinigameScene { if (!this.lockState.tensionApplied) { this.updateFeedback("Apply tension first before picking pins"); + this.flashWrenchRed(); } }); @@ -3231,6 +3260,7 @@ export class LockpickingMinigamePhaser extends MinigameScene { if (!this.lockState.tensionApplied) { this.updateFeedback("Apply tension first before picking pins"); + this.flashWrenchRed(); } } } @@ -4418,4 +4448,37 @@ export class LockpickingMinigamePhaser extends MinigameScene { } return array; } + + flashWrenchRed() { + // Flash the tension wrench red to indicate tension is needed + if (!this.wrenchGraphics) return; + + const originalFillStyle = this.lockState.tensionApplied ? 0x00ff00 : 0x888888; + + // Store original state + const originalClear = this.wrenchGraphics.clear.bind(this.wrenchGraphics); + + // Flash red 3 times + for (let i = 0; i < 3; i++) { + this.scene.time.delayedCall(i * 150, () => { + this.wrenchGraphics.clear(); + this.wrenchGraphics.fillStyle(0xff0000); // Red + + // Long vertical arm + this.wrenchGraphics.fillRect(0, -120, 10, 170); + // Short horizontal arm + this.wrenchGraphics.fillRect(0, 40, 37.5, 10); + }); + + this.scene.time.delayedCall(i * 150 + 75, () => { + this.wrenchGraphics.clear(); + this.wrenchGraphics.fillStyle(originalFillStyle); // Back to original color + + // Long vertical arm + this.wrenchGraphics.fillRect(0, -120, 10, 170); + // Short horizontal arm + this.wrenchGraphics.fillRect(0, 40, 37.5, 10); + }); + } + } } \ No newline at end of file diff --git a/js/minigames/password/password-minigame.js b/js/minigames/password/password-minigame.js index 873ed89..10aa9ff 100644 --- a/js/minigames/password/password-minigame.js +++ b/js/minigames/password/password-minigame.js @@ -51,8 +51,43 @@ export class PasswordMinigame extends MinigameScene { setupPasswordInterface() { // Create the password entry interface + // Check if we can get the device image from sprite or params + const getImageData = () => { + // Try to get sprite data from params (from lockable object passed through minigame framework) + const sprite = this.params.sprite || this.params.lockable; + if (sprite && sprite.name && sprite.scenarioData) { + return { + imageFile: sprite.name, + deviceName: sprite.scenarioData.name || sprite.name, + observations: sprite.scenarioData.observations || '' + }; + } + // Fallback to explicit params if provided + if (this.params.deviceImage) { + return { + imageFile: this.params.deviceImage, + deviceName: this.params.deviceName || this.params.title || 'Device', + observations: this.params.observations || '' + }; + } + return null; + }; + + const imageData = getImageData(); + this.gameContainer.innerHTML = `
+ ${imageData ? ` +
+ ${imageData.deviceName} +
+

${imageData.deviceName}

+

${imageData.observations}

+
+
+ ` : ''}
diff --git a/js/minigames/phone/phone-messages-minigame.js b/js/minigames/phone/phone-messages-minigame.js index 3a5311e..884574c 100644 --- a/js/minigames/phone/phone-messages-minigame.js +++ b/js/minigames/phone/phone-messages-minigame.js @@ -328,7 +328,8 @@ export class PhoneMessagesMinigame extends MinigameScene { const notebookBtn = document.createElement('button'); notebookBtn.className = 'minigame-button'; notebookBtn.id = 'minigame-notebook'; - notebookBtn.innerHTML = 'Notebook Add to Notebook'; + notebookBtn.innerHTML = 'Notebook Add to Notebook'; + this.controlsElement.appendChild(notebookBtn); // Change cancel button text to "Close" @@ -347,7 +348,42 @@ export class PhoneMessagesMinigame extends MinigameScene { setupPhoneInterface() { // Create the phone interface + // Check if we can get the device image from sprite or params + const getImageData = () => { + // Try to get sprite data from params (from lockable object passed through minigame framework) + const sprite = this.params.sprite || this.params.lockable; + if (sprite && sprite.name && sprite.scenarioData) { + return { + imageFile: sprite.name, + deviceName: sprite.scenarioData.name || sprite.name, + observations: sprite.scenarioData.observations || '' + }; + } + // Fallback to explicit params if provided + if (this.params.deviceImage) { + return { + imageFile: this.params.deviceImage, + deviceName: this.params.deviceName || this.params.title || 'Device', + observations: this.params.observations || '' + }; + } + return null; + }; + + const imageData = getImageData(); + this.gameContainer.innerHTML = ` + ${imageData ? ` +
+ ${imageData.deviceName} +
+

${imageData.deviceName}

+

${imageData.observations}

+
+
+ ` : ''}
@@ -393,10 +429,7 @@ export class PhoneMessagesMinigame extends MinigameScene {
- -
- -
+ `; // Get references to important elements @@ -406,7 +439,6 @@ export class PhoneMessagesMinigame extends MinigameScene { this.messageTime = document.getElementById('message-time'); this.messageContent = document.getElementById('message-content'); this.messageActions = document.getElementById('message-actions'); - this.phoneObservations = document.getElementById('phone-observations'); // Control buttons this.prevBtn = document.getElementById('prev-btn'); @@ -422,9 +454,7 @@ export class PhoneMessagesMinigame extends MinigameScene { // Populate messages this.populateMessages(); - - // Populate observations - this.populateObservations(); + } populateMessages() { @@ -444,13 +474,11 @@ export class PhoneMessagesMinigame extends MinigameScene { messageElement.className = `message-item ${message.type || 'text'}`; messageElement.dataset.index = index; - const icon = message.type === 'voice' ? '🎤' : '💬'; const preview = message.type === 'voice' ? (message.text ? message.text.substring(0, 50) + '...' : 'Voice message') : (message.text || 'No text content'); messageElement.innerHTML = ` -
${icon}
${message.sender || 'Unknown'}
${preview}
@@ -463,22 +491,7 @@ export class PhoneMessagesMinigame extends MinigameScene { }); } - populateObservations() { - // Get observations from the original object data - const observations = this.params?.observations || this.phoneData?.observations; - - if (observations) { - this.phoneObservations.innerHTML = ` -
-

Observations:

-

${observations}

-
- `; - } else { - this.phoneObservations.innerHTML = ''; - } - } - + setupEventListeners() { // Message list clicks this.addEventListener(this.messagesList, 'click', (event) => { @@ -788,7 +801,7 @@ export class PhoneMessagesMinigame extends MinigameScene { if (message.type === 'voice') { // For voice messages, show audio icon and transcript - content += `🎵 [Audio Message]\n`; + content += `[Audio Message]\n`; content += `Transcript: ${message.voice || message.text || 'No transcript available'}\n\n`; } else { // For text messages, show the content diff --git a/js/systems/interactions.js b/js/systems/interactions.js index 7c77ded..a1479dc 100644 --- a/js/systems/interactions.js +++ b/js/systems/interactions.js @@ -292,6 +292,7 @@ export function handleObjectInteraction(sprite) { title: data.name || 'Phone Messages', messages: messages, observations: data.observations, + lockable: sprite, onComplete: (success, result) => { console.log('Phone messages minigame completed:', success, result); } @@ -318,6 +319,7 @@ export function handleObjectInteraction(sprite) { fileContent: data.text, fileType: data.fileType || 'text', observations: data.observations, + lockable: sprite, source: data.source || 'Unknown Source', onComplete: (success, result) => { console.log('Text file minigame completed:', success, result); diff --git a/js/systems/minigame-starters.js b/js/systems/minigame-starters.js index c0b20da..1bec994 100644 --- a/js/systems/minigame-starters.js +++ b/js/systems/minigame-starters.js @@ -35,10 +35,49 @@ export function startLockpickingMinigame(lockable, scene, difficulty = 'medium', window.MinigameFramework.init(scene); } + // Extract item information from lockable object (handles both items and doors) + let itemName, itemImage, itemObservations; + + // Check if this is a door (has doorProperties) or an item + if (lockable?.doorProperties) { + // This is a door - get the connected room name + const connectedRoomId = lockable.doorProperties.connectedRoom; + const currentRoomId = lockable.doorProperties.roomId; + const gameScenario = window.gameScenario; + const connectedRoom = gameScenario?.rooms?.[connectedRoomId]; + const currentRoom = gameScenario?.rooms?.[currentRoomId]; + const isLocked = lockable.doorProperties.locked; + + // Use door_sign if available (player-visible sign on the door) + const doorSignOrName = connectedRoom?.door_sign || connectedRoom?.name; + + // Format item name with locked status + if (doorSignOrName) { + // Has door_sign or room name - show it + itemName = isLocked ? `Locked ${doorSignOrName}` : doorSignOrName; + itemObservations = `Door to ${doorSignOrName}`; + } else { + // No door_sign and undiscovered room - use generic names + itemName = 'Locked door'; + const currentRoomName = currentRoom?.name || currentRoomId; + itemObservations = `A door leading out of ${currentRoomName}`; + } + + itemImage = 'assets/tiles/door.png'; // Use default door image + } else { + // This is a regular item - use scenarioData + itemName = lockable?.scenarioData?.name || lockable?.name || 'Locked Item'; + itemImage = lockable?.name ? `assets/objects/${lockable.name}.png` : null; + itemObservations = lockable?.scenarioData?.observations || ''; + } + // Start the lockpicking minigame (Phaser version) window.MinigameFramework.startMinigame('lockpicking', null, { lockable: lockable, difficulty: difficulty, + itemName: itemName, + itemImage: itemImage, + itemObservations: itemObservations, cancelText: 'Close', onComplete: (success, result) => { if (success) { @@ -158,6 +197,42 @@ export function startKeySelectionMinigame(lockable, type, playerKeys, requiredKe console.log(`Using default lock configuration for ${lockId}:`, lockConfig); } + // Extract item information from lockable object (handles both items and doors) + let itemName, itemImage, itemObservations; + + // Check if this is a door (has doorProperties) or an item + if (lockable?.doorProperties) { + // This is a door - get the connected room name + const connectedRoomId = lockable.doorProperties.connectedRoom; + const currentRoomId = lockable.doorProperties.roomId; + const gameScenario = window.gameScenario; + const connectedRoom = gameScenario?.rooms?.[connectedRoomId]; + const currentRoom = gameScenario?.rooms?.[currentRoomId]; + const isLocked = lockable.doorProperties.locked; + + // Use door_sign if available (player-visible sign on the door) + const doorSignOrName = connectedRoom?.door_sign || connectedRoom?.name; + + // Format item name with locked status + if (doorSignOrName) { + // Has door_sign or room name - show it + itemName = isLocked ? `${doorSignOrName}` : doorSignOrName; + itemObservations = `Door to ${doorSignOrName}`; + } else { + // No door_sign and undiscovered room - use generic names + itemName = 'Locked door'; + const currentRoomName = currentRoom?.name || currentRoomId; + itemObservations = `A door leading out of ${currentRoomName}`; + } + + itemImage = 'assets/tiles/door.png'; // Use default door image + } else { + // This is a regular item - use scenarioData + itemName = lockable?.scenarioData?.name || lockable?.name || 'Locked Item'; + itemImage = lockable?.name ? `assets/objects/${lockable.name}.png` : null; + itemObservations = lockable?.scenarioData?.observations || ''; + } + // Start the key selection minigame window.MinigameFramework.startMinigame('lockpicking', null, { keyMode: true, @@ -167,6 +242,9 @@ export function startKeySelectionMinigame(lockable, type, playerKeys, requiredKe pinCount: lockConfig.pinCount, predefinedPinHeights: lockConfig.pinHeights, // Pass the predefined pin heights difficulty: lockConfig.difficulty, + itemName: itemName, + itemImage: itemImage, + itemObservations: itemObservations, cancelText: 'Close', onComplete: (success, result) => { if (success) { @@ -299,6 +377,7 @@ export function startPasswordMinigame(lockable, type, correctPassword, callback, maxAttempts: options.maxAttempts || 3, postitNote: options.postitNote || '', showPostit: options.showPostit || false, + lockable: lockable, onComplete: (success, result) => { if (success) { console.log('PASSWORD MINIGAME SUCCESS'); diff --git a/scenarios/ceo_exfil.json b/scenarios/ceo_exfil.json index ca9fb76..71a2e10 100644 --- a/scenarios/ceo_exfil.json +++ b/scenarios/ceo_exfil.json @@ -1,5 +1,5 @@ { - "scenario_brief": "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.", + "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", "rooms": { "reception": { @@ -95,6 +95,7 @@ "lockType": "key", "requires": "office1_key:40,35,38,32,10", "difficulty": "easy", + "door_sign": "4A Hot Desks", "connections": { "north": ["office2", "office3"],