From 1cec7fca8fe2aa5db072d57e526c498f8237f289 Mon Sep 17 00:00:00 2001 From: Damian-I Date: Wed, 19 Feb 2025 00:05:22 +0000 Subject: [PATCH 01/14] Added fingerprint kit item --- assets/objects/fingerprint_kit.png | Bin 0 -> 5698 bytes assets/rooms/room_office.json | 18 ++++++++++++++++-- assets/scenarios/ceo_exfil.json | 6 ++++++ index.html | 1 + 4 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 assets/objects/fingerprint_kit.png diff --git a/assets/objects/fingerprint_kit.png b/assets/objects/fingerprint_kit.png new file mode 100644 index 0000000000000000000000000000000000000000..db802b0011aa1e5879449ac69057e4144289cbc2 GIT binary patch literal 5698 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 JEpuMC<-Z-#(_{bu literal 0 HcmV?d00001 diff --git a/assets/rooms/room_office.json b/assets/rooms/room_office.json index bad8649..827f1dd 100644 --- a/assets/rooms/room_office.json +++ b/assets/rooms/room_office.json @@ -316,7 +316,21 @@ "width":48, "x":100.927703875072, "y":101.732793522267 - }], + }, + { + "height":48, + "id":15, + "name":"fingerprint_kit", + "rotation":0, + "type":"", + "visible":true, + "width":48, + "x":390, + "y":144 + } + + + ], "opacity":1, "type":"objectgroup", "visible":true, @@ -324,7 +338,7 @@ "y":0 }], "nextlayerid":12, - "nextobjectid":15, + "nextobjectid":16, "orientation":"orthogonal", "renderorder":"right-down", "tiledversion":"1.11.0", diff --git a/assets/scenarios/ceo_exfil.json b/assets/scenarios/ceo_exfil.json index 701d051..eab90b8 100644 --- a/assets/scenarios/ceo_exfil.json +++ b/assets/scenarios/ceo_exfil.json @@ -71,6 +71,12 @@ "readable": true, "text": "URGENT: Multiple unauthorized access attempts detected from CEO's office IP address", "observations": "A concerning IT department memo" + }, + { + "type": "fingerprint_kit", + "name": "Fingerprint Kit", + "takeable": true, + "observations": "A kit used for collecting fingerprints from surfaces" } ] }, diff --git a/index.html b/index.html index 290608d..18bef05 100644 --- a/index.html +++ b/index.html @@ -228,6 +228,7 @@ this.load.image('workstation', 'assets/objects/workstation.png'); this.load.image('bluetooth_scanner', 'assets/objects/bluetooth_scanner.png'); this.load.image('tablet', 'assets/objects/tablet.png'); + this.load.image('fingerprint_kit', 'assets/objects/fingerprint_kit.png'); this.load.json('gameScenarioJSON', 'assets/scenarios/ceo_exfil.json'); From 79e518efe32e2b4209e2fdb11d982c99646ed663 Mon Sep 17 00:00:00 2001 From: Damian-I Date: Wed, 19 Feb 2025 00:31:36 +0000 Subject: [PATCH 02/14] Added clickable blackboard in the form of an object. Need to add logic (maybe change method for blackboard) --- assets/rooms/room_office.json | 14 +++++++++++++- assets/scenarios/ceo_exfil.json | 9 +++++++++ index.html | 6 ++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/assets/rooms/room_office.json b/assets/rooms/room_office.json index 827f1dd..d4b15c1 100644 --- a/assets/rooms/room_office.json +++ b/assets/rooms/room_office.json @@ -317,6 +317,18 @@ "x":100.927703875072, "y":101.732793522267 }, + { + "gid":417, + "height":48, + "id":16, + "name":"blackboard", + "rotation":0, + "type":"", + "visible":true, + "width":48, + "x":329, + "y":90 + }, { "height":48, "id":15, @@ -325,7 +337,7 @@ "type":"", "visible":true, "width":48, - "x":390, + "x":432, "y":144 } diff --git a/assets/scenarios/ceo_exfil.json b/assets/scenarios/ceo_exfil.json index eab90b8..213f3c1 100644 --- a/assets/scenarios/ceo_exfil.json +++ b/assets/scenarios/ceo_exfil.json @@ -72,6 +72,15 @@ "text": "URGENT: Multiple unauthorized access attempts detected from CEO's office IP address", "observations": "A concerning IT department memo" }, + { + "type": "blackboard", + "name": "Office Blackboard", + "takeable": false, + "hasFingerprint": true, + "fingerprintOwner": "ceo", + "fingerprintQuality": 0.8, + "observations": "A blackboard with some recent writing. The CEO was seen using it earlier." + }, { "type": "fingerprint_kit", "name": "Fingerprint Kit", diff --git a/index.html b/index.html index 18bef05..5ad4dbf 100644 --- a/index.html +++ b/index.html @@ -1483,6 +1483,12 @@ } } + // Add blackboard interaction + if (data.scenarioData?.type === "blackboard") { + alert("A blackboard with some recent writing. There might be fingerprints on the surface."); + return; + } + alert(message); } From 491bc140227d27d989003075da348a8812bf410c Mon Sep 17 00:00:00 2001 From: Damian-I Date: Mon, 24 Feb 2025 23:13:12 +0000 Subject: [PATCH 03/14] Removed blackboard, changed the computer in office1 to be used for fingerprints. Also added a hint in the description of the computer that it has a fingerprint --- assets/rooms/room_office.json | 14 +------------- assets/scenarios/ceo_exfil.json | 14 ++++---------- index.html | 6 ------ 3 files changed, 5 insertions(+), 29 deletions(-) diff --git a/assets/rooms/room_office.json b/assets/rooms/room_office.json index d4b15c1..827f1dd 100644 --- a/assets/rooms/room_office.json +++ b/assets/rooms/room_office.json @@ -317,18 +317,6 @@ "x":100.927703875072, "y":101.732793522267 }, - { - "gid":417, - "height":48, - "id":16, - "name":"blackboard", - "rotation":0, - "type":"", - "visible":true, - "width":48, - "x":329, - "y":90 - }, { "height":48, "id":15, @@ -337,7 +325,7 @@ "type":"", "visible":true, "width":48, - "x":432, + "x":390, "y":144 } diff --git a/assets/scenarios/ceo_exfil.json b/assets/scenarios/ceo_exfil.json index 213f3c1..bbffaef 100644 --- a/assets/scenarios/ceo_exfil.json +++ b/assets/scenarios/ceo_exfil.json @@ -62,7 +62,10 @@ "name": "Office Computer", "takeable": false, "requires": "password", - "observations": "A computer with a cybersecurity alert on screen" + "hasFingerprint": true, + "fingerprintOwner": "ceo", + "fingerprintQuality": 0.9, + "observations": "A computer with a cybersecurity alert on screen. There might be fingerprints on the keyboard." }, { "type": "notes", @@ -72,15 +75,6 @@ "text": "URGENT: Multiple unauthorized access attempts detected from CEO's office IP address", "observations": "A concerning IT department memo" }, - { - "type": "blackboard", - "name": "Office Blackboard", - "takeable": false, - "hasFingerprint": true, - "fingerprintOwner": "ceo", - "fingerprintQuality": 0.8, - "observations": "A blackboard with some recent writing. The CEO was seen using it earlier." - }, { "type": "fingerprint_kit", "name": "Fingerprint Kit", diff --git a/index.html b/index.html index 5ad4dbf..18bef05 100644 --- a/index.html +++ b/index.html @@ -1483,12 +1483,6 @@ } } - // Add blackboard interaction - if (data.scenarioData?.type === "blackboard") { - alert("A blackboard with some recent writing. There might be fingerprints on the surface."); - return; - } - alert(message); } From 6f560af427525f8a5a794b9bb0e04bdf9613840b Mon Sep 17 00:00:00 2001 From: Damian-I Date: Mon, 24 Feb 2025 23:40:18 +0000 Subject: [PATCH 04/14] Add fingerprint collection mechanics to game interaction system - Implement fingerprint collection logic for interactive objects - Add `collectFingerprint()` function to handle fingerprint sample generation --- index.html | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/index.html b/index.html index 18bef05..cf27574 100644 --- a/index.html +++ b/index.html @@ -1419,6 +1419,22 @@ const data = sprite.scenarioData; + // Check for fingerprint collection possibility + if (data.hasFingerprint) { + // Check if player has fingerprint kit + const hasKit = inventory.items.some(item => + item && item.scenarioData && + item.scenarioData.type === 'fingerprint_kit' + ); + + if (hasKit) { + const sample = collectFingerprint(sprite); + if (sample) { + return; // Exit after collecting fingerprint + } + } + } + // Check if this is an unlocked container that hasn't been collected yet if (data.isUnlockedButNotCollected && data.contents) { let message = `You found the following items:\n`; @@ -2144,6 +2160,36 @@ }); } + function collectFingerprint(item) { + if (!item.scenarioData?.hasFingerprint) { + alert("No fingerprints found on this surface."); + return null; + } + + const sample = { + id: `${item.scenarioData.fingerprintOwner}_${Date.now()}`, + type: "fingerprint", + owner: item.scenarioData.fingerprintOwner, + quality: item.scenarioData.fingerprintQuality, + data: generateFingerprintData(item) + }; + + gameState.biometricSamples.push(sample); + alert(`Successfully collected a fingerprint sample from ${item.scenarioData.name}`); + console.log("Collected fingerprint sample:", sample); + return sample; + } + + function generateFingerprintData(item) { + // Placeholder implementation + // Replace with actual fingerprint data generation logic + return "fingerprint_data"; + } + + function hasItemInInventory(itemType) { + return gameState.inventory.items.some(item => item.scenarioData?.type === itemType); + } + \ No newline at end of file From 5869b4addefae14e028cb6760402ddef47aaa580 Mon Sep 17 00:00:00 2001 From: Damian-I Date: Mon, 24 Feb 2025 23:47:07 +0000 Subject: [PATCH 05/14] Fixed bug by initializing game state object with biometric samples and inventory tracking - Create `gameState` object to centralize game state management - Include biometric samples and inventory as initial state properties --- index.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/index.html b/index.html index cf27574..dce4ddb 100644 --- a/index.html +++ b/index.html @@ -195,6 +195,11 @@ let lastBluetoothScan = 0; // Track last scan time const BLUETOOTH_SCAN_INTERVAL = 500; // Scan every 500ms + const gameState = { + biometricSamples: [], + inventory: inventory + }; + // preloads the assets function preload() { // Show loading text From ffb841e1291feb441aefdc2fea2a3b9a01e97b56 Mon Sep 17 00:00:00 2001 From: Damian-I Date: Tue, 25 Feb 2025 00:04:26 +0000 Subject: [PATCH 06/14] Fingerprint scanner mechanics with lockout and validation - Add visual feedback for biometric scan success and failure - Create advanced collection mechanism with quality tracking and animation - Introduce scanner state management with failed attempt tracking --- index.html | 201 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 181 insertions(+), 20 deletions(-) diff --git a/index.html b/index.html index dce4ddb..d75685f 100644 --- a/index.html +++ b/index.html @@ -200,6 +200,20 @@ inventory: inventory }; + // Add these constants near the top with other constants + const SCANNER_LOCKOUT_TIME = 30000; // 30 seconds lockout + const MAX_FAILED_ATTEMPTS = 3; + + // Add this to track failed attempts + const scannerState = { + failedAttempts: {}, // tracks failures by scanner ID + lockoutTimers: {} // tracks lockout end times + }; + + // Add these constants near the top with other constants + const SAMPLE_COLLECTION_TIME = 2000; // 2 seconds for collection animation + const SAMPLE_COLLECTION_COLOR = 0x00ff00; // Green for collection effect + // preloads the assets function preload() { // Show loading text @@ -1424,6 +1438,12 @@ const data = sprite.scenarioData; + // Add inside handleObjectInteraction before the fingerprint check + if (data.biometricType === 'fingerprint') { + handleBiometricScan(sprite, player); + return; + } + // Check for fingerprint collection possibility if (data.hasFingerprint) { // Check if player has fingerprint kit @@ -2165,36 +2185,177 @@ }); } + // Add helper function to generate fingerprint data + function generateFingerprintData(item) { + // In a real implementation, this would generate unique fingerprint patterns + // For now, we'll just create a unique identifier + return `fp_${item.scenarioData.fingerprintOwner}_${Date.now()}`; + } + + // Add helper function to check if player has required collection tools + function hasItemInInventory(itemType) { + return inventory.items.some(item => + item && item.scenarioData && + item.scenarioData.type === itemType + ); + } + + // Add this function after the other utility functions + function handleBiometricScan(scanner, player) { + const scannerId = scanner.scenarioData.id || scanner.name; + + // Check if scanner is locked out + if (scannerState.lockoutTimers[scannerId] && + Date.now() < scannerState.lockoutTimers[scannerId]) { + const remainingTime = Math.ceil((scannerState.lockoutTimers[scannerId] - Date.now()) / 1000); + alert(`Scanner locked out. Try again in ${remainingTime} seconds.`); + return false; + } + + if (!scanner.scenarioData?.biometricType === 'fingerprint') { + console.warn('Invalid scanner type'); + return false; + } + + // Check if player has valid fingerprint sample + const validSample = gameState.biometricSamples.find(sample => + sample.type === 'fingerprint' && + scanner.scenarioData.acceptedSamples.includes(sample.owner) + ); + + if (!validSample) { + handleScannerFailure(scannerId); + alert("No valid fingerprint sample found."); + return false; + } + + // Check sample quality + const qualityThreshold = 0.7; + if (validSample.quality < qualityThreshold) { + handleScannerFailure(scannerId); + alert("Fingerprint sample quality too poor for scanning."); + return false; + } + + // Success case - reset failed attempts + scannerState.failedAttempts[scannerId] = 0; + alert("Biometric scan successful!"); + + // Add visual feedback + const successEffect = scanner.scene.add.circle( + scanner.x, + scanner.y, + 32, + 0x00ff00, + 0.5 + ); + scanner.scene.tweens.add({ + targets: successEffect, + alpha: 0, + scale: 2, + duration: 1000, + onComplete: () => successEffect.destroy() + }); + + // If the scanner is protecting something, unlock it + if (scanner.scenarioData.unlocks) { + const targetObject = rooms[currentRoom].objects[scanner.scenarioData.unlocks]; + if (targetObject) { + targetObject.scenarioData.locked = false; + targetObject.scenarioData.isUnlockedButNotCollected = true; + } + } + + return true; + } + + // Add this new function to handle scanner failures + function handleScannerFailure(scannerId) { + // Initialize failed attempts if not exists + if (!scannerState.failedAttempts[scannerId]) { + scannerState.failedAttempts[scannerId] = 0; + } + + // Increment failed attempts + scannerState.failedAttempts[scannerId]++; + + // Check if we should lockout + if (scannerState.failedAttempts[scannerId] >= MAX_FAILED_ATTEMPTS) { + scannerState.lockoutTimers[scannerId] = Date.now() + SCANNER_LOCKOUT_TIME; + alert(`Too many failed attempts. Scanner locked for ${SCANNER_LOCKOUT_TIME/1000} seconds.`); + } else { + const remainingAttempts = MAX_FAILED_ATTEMPTS - scannerState.failedAttempts[scannerId]; + alert(`Scan failed. ${remainingAttempts} attempts remaining before lockout.`); + } + } + + // Modify collectFingerprint to include visual feedback function collectFingerprint(item) { if (!item.scenarioData?.hasFingerprint) { alert("No fingerprints found on this surface."); return null; } - const sample = { - id: `${item.scenarioData.fingerprintOwner}_${Date.now()}`, - type: "fingerprint", - owner: item.scenarioData.fingerprintOwner, - quality: item.scenarioData.fingerprintQuality, - data: generateFingerprintData(item) - }; + // Create collection effect + const scene = item.scene; + const collectionEffect = scene.add.circle( + item.x, + item.y, + 40, + SAMPLE_COLLECTION_COLOR, + 0.3 + ); - gameState.biometricSamples.push(sample); - alert(`Successfully collected a fingerprint sample from ${item.scenarioData.name}`); - console.log("Collected fingerprint sample:", sample); - return sample; - } + // Add scanning animation + scene.tweens.add({ + targets: collectionEffect, + scale: { from: 1, to: 1.5 }, + alpha: { from: 0.3, to: 0 }, + duration: SAMPLE_COLLECTION_TIME, + repeat: 0, + yoyo: false, + onComplete: () => { + collectionEffect.destroy(); + + // Create the sample after animation + const sample = { + id: `${item.scenarioData.fingerprintOwner}_${Date.now()}`, + type: "fingerprint", + owner: item.scenarioData.fingerprintOwner, + quality: item.scenarioData.fingerprintQuality, + data: generateFingerprintData(item) + }; + + if (!gameState.biometricSamples) { + gameState.biometricSamples = []; + } + + gameState.biometricSamples.push(sample); + + // Show collection success message with sample details + const qualityPercentage = Math.round(sample.quality * 100); + alert(`Successfully collected a fingerprint sample from ${item.scenarioData.name}\nSample Quality: ${qualityPercentage}%`); + console.log("Collected fingerprint sample:", sample); + } + }); - function generateFingerprintData(item) { - // Placeholder implementation - // Replace with actual fingerprint data generation logic - return "fingerprint_data"; - } + // Add scanning particles + const particles = scene.add.particles(item.x, item.y, 'particle', { + speed: 100, + scale: { start: 0.2, end: 0 }, + blendMode: 'ADD', + lifespan: 1000, + quantity: 1, + frequency: 50 + }); + + // Clean up particles after collection + scene.time.delayedCall(SAMPLE_COLLECTION_TIME, () => { + particles.destroy(); + }); - function hasItemInInventory(itemType) { - return gameState.inventory.items.some(item => item.scenarioData?.type === itemType); + return true; } - \ No newline at end of file From 4ab5aa90059b6d88770a1e20bc5b22ccc93a2124 Mon Sep 17 00:00:00 2001 From: Damian-I Date: Tue, 25 Feb 2025 00:11:35 +0000 Subject: [PATCH 07/14] Add biometric samples UI with interaction and visualization - Create comprehensive UI for displaying collected biometric samples - Implement keyboard controls (B key) to show/hide samples interface - Add visual quality indicators and detailed sample information - Enhance object interaction with scanner highlighting and pulsing effects - Integrate new UI with existing game state management --- index.html | 283 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 283 insertions(+) diff --git a/index.html b/index.html index d75685f..3d783a9 100644 --- a/index.html +++ b/index.html @@ -214,6 +214,25 @@ const SAMPLE_COLLECTION_TIME = 2000; // 2 seconds for collection animation const SAMPLE_COLLECTION_COLOR = 0x00ff00; // Green for collection effect + // Add these constants for the UI + const SAMPLE_UI_STYLES = { + backgroundColor: 'rgba(0, 0, 0, 0.8)', + padding: '10px', + color: 'white', + fontFamily: 'Arial, sans-serif', + fontSize: '14px', + border: '1px solid #444', + borderRadius: '5px', + position: 'fixed', + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', + zIndex: 1000, + maxHeight: '80vh', + overflowY: 'auto', + display: 'none' + }; + // preloads the assets function preload() { // Show loading text @@ -473,6 +492,9 @@ // Optimize physics settings this.physics.world.setFPS(60); this.physics.world.step(1/60); + + // Add this to your scene's create function + initializeSamplesUI(); } function update() { @@ -2356,6 +2378,267 @@ return true; } + + // Add this function to check for object interactions + function checkObjectInteractions() { + // Skip if not enough time has passed since last check + const currentTime = performance.now(); + if (this.lastInteractionCheck && + currentTime - this.lastInteractionCheck < INTERACTION_CHECK_INTERVAL) { + return; + } + this.lastInteractionCheck = currentTime; + + const playerRoom = currentPlayerRoom; + if (!playerRoom || !rooms[playerRoom].objects) return; + + // Cache player position + const px = player.x; + const py = player.y; + + // Get only objects within viewport bounds plus some margin + const camera = this.cameras.main; + const margin = INTERACTION_RANGE; + const viewBounds = { + left: camera.scrollX - margin, + right: camera.scrollX + camera.width + margin, + top: camera.scrollY - margin, + bottom: camera.scrollY + camera.height + margin + }; + + Object.values(rooms[playerRoom].objects).forEach(obj => { + // Skip inactive objects and those outside viewport + if (!obj.active || + obj.x < viewBounds.left || + obj.x > viewBounds.right || + obj.y < viewBounds.top || + obj.y > viewBounds.bottom) { + return; + } + + // Use squared distance for performance + const dx = px - obj.x; + const dy = py - obj.y; + const distanceSq = dx * dx + dy * dy; + + if (distanceSq <= INTERACTION_RANGE_SQ) { + if (!obj.isHighlighted) { + obj.isHighlighted = true; + obj.setTint(0xdddddd); // Simple highlight without tween + } + } else if (obj.isHighlighted) { + obj.isHighlighted = false; + obj.clearTint(); + } + }); + } + + // Add this function to setup scanner interactions + function setupScannerInteractions() { + Object.values(rooms).forEach(room => { + if (!room.objects) return; + + Object.values(room.objects).forEach(obj => { + if (obj.scenarioData?.biometricType === 'fingerprint') { + // Add visual indicator for scanner + const indicator = obj.scene.add.circle( + obj.x, + obj.y, + 20, + 0x0000ff, + 0.3 + ); + + // Add pulsing effect + obj.scene.tweens.add({ + targets: indicator, + alpha: { from: 0.3, to: 0.1 }, + scale: { from: 1, to: 1.2 }, + duration: 1000, + yoyo: true, + repeat: -1 + }); + + // Store reference to indicator + obj.scannerIndicator = indicator; + + // Add hover effect + obj.on('pointerover', function() { + if (this.scannerIndicator) { + this.scannerIndicator.setAlpha(0.5); + } + }); + + obj.on('pointerout', function() { + if (this.scannerIndicator) { + this.scannerIndicator.setAlpha(0.3); + } + }); + } + }); + }); + } + + // Add this to your scene initialization + function initializeBiometricSystem() { + // Initialize gameState if not exists + if (!window.gameState) { + window.gameState = { + biometricSamples: [] + }; + } + + // Initialize scanner state + if (!window.scannerState) { + window.scannerState = { + failedAttempts: {}, + lockoutTimers: {} + }; + } + + // Setup scanner visuals and interactions + setupScannerInteractions(); + + // Add periodic interaction checks + this.time.addEvent({ + delay: 100, // Check every 100ms + callback: checkObjectInteractions, + callbackScope: this, + loop: true + }); + } + + // Add function to create and manage the samples UI + function createSamplesUI() { + // Create container for samples UI if it doesn't exist + let samplesUI = document.getElementById('biometric-samples-ui'); + if (!samplesUI) { + samplesUI = document.createElement('div'); + samplesUI.id = 'biometric-samples-ui'; + + // Apply styles + Object.assign(samplesUI.style, SAMPLE_UI_STYLES); + + // Add close button + const closeButton = document.createElement('button'); + closeButton.textContent = '×'; + closeButton.style.cssText = ` + position: absolute; + right: 10px; + top: 10px; + background: none; + border: none; + color: white; + font-size: 20px; + cursor: pointer; + `; + closeButton.onclick = () => hideSamplesUI(); + samplesUI.appendChild(closeButton); + + document.body.appendChild(samplesUI); + } + return samplesUI; + } + + // Function to show samples UI + function showSamplesUI() { + const samplesUI = createSamplesUI(); + samplesUI.style.display = 'block'; + + // Clear existing content + while (samplesUI.children.length > 1) { // Keep close button + samplesUI.removeChild(samplesUI.lastChild); + } + + // Add title + const title = document.createElement('h2'); + title.textContent = 'Collected Biometric Samples'; + title.style.cssText = 'margin-top: 0; color: #fff; text-align: center;'; + samplesUI.appendChild(title); + + // Add samples + if (!gameState.biometricSamples || gameState.biometricSamples.length === 0) { + const noSamples = document.createElement('p'); + noSamples.textContent = 'No samples collected yet.'; + noSamples.style.textAlign = 'center'; + samplesUI.appendChild(noSamples); + return; + } + + gameState.biometricSamples.forEach(sample => { + const sampleElement = document.createElement('div'); + sampleElement.style.cssText = ` + margin: 10px 0; + padding: 10px; + background: rgba(255, 255, 255, 0.1); + border-radius: 5px; + `; + + const qualityPercentage = Math.round(sample.quality * 100); + sampleElement.innerHTML = ` + Type: ${sample.type}
+ Owner: ${sample.owner}
+ Quality: ${qualityPercentage}%
+ ID: ${sample.id}
+ `; + + // Add quality bar + const qualityBar = document.createElement('div'); + qualityBar.style.cssText = ` + width: 100%; + height: 5px; + background: #333; + margin-top: 5px; + border-radius: 2px; + `; + + const qualityFill = document.createElement('div'); + qualityFill.style.cssText = ` + width: ${qualityPercentage}%; + height: 100%; + background: ${getQualityColor(sample.quality)}; + border-radius: 2px; + transition: width 0.3s ease; + `; + + qualityBar.appendChild(qualityFill); + sampleElement.appendChild(qualityBar); + samplesUI.appendChild(sampleElement); + }); + } + + // Helper function to hide samples UI + function hideSamplesUI() { + const samplesUI = document.getElementById('biometric-samples-ui'); + if (samplesUI) { + samplesUI.style.display = 'none'; + } + } + + // Helper function to get color based on quality + function getQualityColor(quality) { + if (quality >= 0.8) return '#00ff00'; + if (quality >= 0.6) return '#ffff00'; + return '#ff0000'; + } + + // Add keyboard shortcut to view samples (press 'B') + function setupSamplesUIControls() { + document.addEventListener('keydown', (event) => { + if (event.key.toLowerCase() === 'b') { + showSamplesUI(); + } + if (event.key === 'Escape') { + hideSamplesUI(); + } + }); + } + + // Add this to your scene's create function + function initializeSamplesUI() { + createSamplesUI(); + setupSamplesUIControls(); + } \ No newline at end of file From 62d71889c41cff740ffb154f4745b888cc20356f Mon Sep 17 00:00:00 2001 From: Damian-I Date: Tue, 25 Feb 2025 00:28:44 +0000 Subject: [PATCH 08/14] Add spoofing and dusting mechanics to biometric sample system - Implement spoofing functionality for biometric samples with quality degradation - Create interactive dusting minigame for revealing fingerprints - Add spoofing kit item to office room and scenario - Introduce visual indicators for spoofed samples - Implement progress-based spoofing process with UI feedback Bug with spoofing logic --- assets/rooms/room_office.json | 11 ++ assets/scenarios/ceo_exfil.json | 6 + index.html | 241 ++++++++++++++++++++++++++++++++ 3 files changed, 258 insertions(+) diff --git a/assets/rooms/room_office.json b/assets/rooms/room_office.json index 827f1dd..f94e3ec 100644 --- a/assets/rooms/room_office.json +++ b/assets/rooms/room_office.json @@ -327,6 +327,17 @@ "width":48, "x":390, "y":144 + }, + { + "height": 48, + "id": 16, + "name": "spoofing_kit", + "rotation": 0, + "type": "", + "visible": true, + "width": 48, + "x": 340, + "y": 144 } diff --git a/assets/scenarios/ceo_exfil.json b/assets/scenarios/ceo_exfil.json index bbffaef..d91af48 100644 --- a/assets/scenarios/ceo_exfil.json +++ b/assets/scenarios/ceo_exfil.json @@ -80,6 +80,12 @@ "name": "Fingerprint Kit", "takeable": true, "observations": "A kit used for collecting fingerprints from surfaces" + }, + { + "type": "spoofing_kit", + "name": "Fingerprint Spoofing Kit", + "takeable": true, + "observations": "A specialized kit containing silicone, gelatin, and other materials for creating artificial fingerprints" } ] }, diff --git a/index.html b/index.html index 3d783a9..230c3d9 100644 --- a/index.html +++ b/index.html @@ -233,6 +233,19 @@ display: 'none' }; + // Add these constants for spoofing + const SPOOFING_TIME = 3000; // 3 seconds to create spoof + const SPOOF_QUALITY_MULTIPLIER = 0.8; // Spoofed prints are slightly lower quality + + // Add these constants for the dusting minigame + const DUST_COLORS = { + NONE: 0x000000, + LIGHT: 0x444444, + MEDIUM: 0x888888, + HEAVY: 0xcccccc, + REVEALED: 0x00ff00 + }; + // preloads the assets function preload() { // Show loading text @@ -2580,6 +2593,7 @@ Owner: ${sample.owner}
Quality: ${qualityPercentage}%
ID: ${sample.id}
+ ${sample.isSpoofed ? 'SPOOFED SAMPLE
' : ''} `; // Add quality bar @@ -2603,6 +2617,62 @@ qualityBar.appendChild(qualityFill); sampleElement.appendChild(qualityBar); + + // Add spoof button if not already spoofed + if (!sample.isSpoofed && hasItemInInventory('spoofing_kit')) { + const spoofButton = document.createElement('button'); + spoofButton.textContent = 'Create Spoof'; + spoofButton.style.cssText = ` + margin-top: 10px; + padding: 5px 10px; + background: #444; + border: none; + color: white; + border-radius: 3px; + cursor: pointer; + `; + spoofButton.onclick = async () => { + spoofButton.disabled = true; + spoofButton.textContent = 'Creating spoof...'; + + // Add progress bar + const progressBar = document.createElement('div'); + progressBar.style.cssText = ` + width: 100%; + height: 2px; + background: #333; + margin-top: 5px; + `; + const progress = document.createElement('div'); + progress.style.cssText = ` + width: 0%; + height: 100%; + background: #ff9900; + transition: width 0.1s linear; + `; + progressBar.appendChild(progress); + sampleElement.appendChild(progressBar); + + // Animate progress + let currentProgress = 0; + const interval = setInterval(() => { + currentProgress += 2; + progress.style.width = `${currentProgress}%`; + }, SPOOFING_TIME / 50); + + // Create spoof after delay + setTimeout(() => { + clearInterval(interval); + const spoofedSample = createSpoofedSample(sample); + if (spoofedSample) { + gameState.biometricSamples.push(spoofedSample); + showSamplesUI(); // Refresh UI + } + }, SPOOFING_TIME); + }; + sampleElement.appendChild(spoofButton); + } + samplesUI.appendChild(sampleElement); }); } @@ -2639,6 +2709,177 @@ createSamplesUI(); setupSamplesUIControls(); } + + // Add spoofing functionality + function createSpoofedSample(originalSample) { + if (!originalSample) { + alert("No sample to spoof from!"); + return null; + } + + // Check if player has required items + const hasSpoofingKit = hasItemInInventory('spoofing_kit'); + if (!hasSpoofingKit) { + alert("You need a spoofing kit to create fake fingerprints!"); + return null; + } + + // Create spoofed sample with slightly degraded quality + const spoofedSample = { + id: `spoofed_${originalSample.owner}_${Date.now()}`, + type: originalSample.type, + owner: originalSample.owner, + quality: originalSample.quality * SPOOF_QUALITY_MULTIPLIER, + data: generateFingerprintData(originalSample), + isSpoofed: true + }; + + return spoofedSample; + } + + // Add dusting minigame + function startDustingMinigame(item) { + const scene = item.scene; + const gameWidth = 400; + const gameHeight = 300; + + // Create container for minigame + const container = scene.add.container( + scene.cameras.main.centerX - gameWidth/2, + scene.cameras.main.centerY - gameHeight/2 + ); + + // Add background + const bg = scene.add.rectangle(0, 0, gameWidth, gameHeight, 0x222222); + container.add(bg); + + // Create grid of cells + const cellSize = 20; + const gridWidth = Math.floor(gameWidth / cellSize); + const gridHeight = Math.floor(gameHeight / cellSize); + const cells = []; + + // Generate random fingerprint pattern + const fingerprintCells = new Set(); + const centerX = Math.floor(gridWidth / 2); + const centerY = Math.floor(gridHeight / 2); + for (let i = 0; i < 50; i++) { + const x = centerX + Math.floor(Math.random() * 6 - 3); + const y = centerY + Math.floor(Math.random() * 6 - 3); + fingerprintCells.add(`${x},${y}`); + } + + // Create grid cells + for (let y = 0; y < gridHeight; y++) { + for (let x = 0; x < gridWidth; x++) { + const cell = scene.add.rectangle( + x * cellSize, + y * cellSize, + cellSize - 1, + cellSize - 1, + DUST_COLORS.NONE + ); + cell.setOrigin(0, 0); + cell.setInteractive(); + cell.dustLevel = 0; + cell.hasFingerprint = fingerprintCells.has(`${x},${y}`); + cells.push(cell); + container.add(cell); + + // Add dusting interaction + cell.on('pointermove', function(pointer) { + if (pointer.isDown) { + this.dustLevel = Math.min(3, this.dustLevel + 1); + this.setFillStyle(getDustColor(this.dustLevel, this.hasFingerprint)); + checkDustingProgress(); + } + }); + } + } + + // Add instructions + const instructions = scene.add.text( + gameWidth/2, + -30, + 'Click and drag to dust for fingerprints.\nReveal the pattern without over-dusting!', + { + fontSize: '16px', + fill: '#ffffff', + align: 'center' + } + ); + instructions.setOrigin(0.5); + container.add(instructions); + + // Add progress tracking + let revealedPrints = 0; + let totalPrints = fingerprintCells.size; + let overDusted = 0; + + // Add progress display + const progressText = scene.add.text( + gameWidth/2, + gameHeight + 20, + `Revealed: 0/${totalPrints} | Over-dusted: 0`, + { + fontSize: '16px', + fill: '#ffffff', + align: 'center' + } + ); + progressText.setOrigin(0.5); + container.add(progressText); + + // Add close button + const closeButton = scene.add.text( + gameWidth - 10, + -20, + 'X', + { + fontSize: '20px', + fill: '#ffffff' + } + ); + closeButton.setOrigin(1, 0); + closeButton.setInteractive(); + closeButton.on('pointerdown', () => { + container.destroy(); + }); + container.add(closeButton); + + function getDustColor(level, hasFingerprint) { + if (level === 0) return DUST_COLORS.NONE; + if (level === 1) return DUST_COLORS.LIGHT; + if (level === 2) return hasFingerprint ? DUST_COLORS.REVEALED : DUST_COLORS.MEDIUM; + return DUST_COLORS.HEAVY; + } + + function checkDustingProgress() { + revealedPrints = 0; + overDusted = 0; + + cells.forEach(cell => { + if (cell.hasFingerprint && cell.dustLevel === 2) { + revealedPrints++; + } + if (cell.dustLevel === 3) { + overDusted++; + } + }); + + progressText.setText( + `Revealed: ${revealedPrints}/${totalPrints} | Over-dusted: ${overDusted}` + ); + + // Check win condition + if (revealedPrints === totalPrints && overDusted < 10) { + setTimeout(() => { + container.destroy(); + collectFingerprint(item); + }, 1000); + } + } + } \ No newline at end of file From 3850c36a9727b4d2416e1e07dcd777130e772ea7 Mon Sep 17 00:00:00 2001 From: Damian-I Date: Tue, 25 Feb 2025 00:33:46 +0000 Subject: [PATCH 09/14] Fixed bug in spoofing, now works --- index.html | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/index.html b/index.html index 230c3d9..3691b6e 100644 --- a/index.html +++ b/index.html @@ -2709,6 +2709,21 @@ createSamplesUI(); setupSamplesUIControls(); } + + function generateFingerprintData(sample) { + // For spoofed samples, we generate from the original sample data + if (sample.data) { + return `spoofed_${sample.data}`; + } + + // For original samples from items, we use the item's data + if (sample.scenarioData?.fingerprintOwner) { + return `fp_${sample.scenarioData.fingerprintOwner}_${Date.now()}`; + } + + // Fallback unique identifier + return `fp_unknown_${Date.now()}`; + } // Add spoofing functionality function createSpoofedSample(originalSample) { @@ -2880,6 +2895,7 @@ } } } + \ No newline at end of file From 21f2e514ac63467cc8fbe3ec4b8362d4780eed41 Mon Sep 17 00:00:00 2001 From: Damian-I Date: Tue, 25 Feb 2025 01:26:25 +0000 Subject: [PATCH 10/14] Added working* minigame to fingerprint dusting. *Might be too tiedious, need to fix dragging --- index.html | 313 +++++++++++++++++++++++++---------------------------- 1 file changed, 146 insertions(+), 167 deletions(-) diff --git a/index.html b/index.html index 3691b6e..5db867b 100644 --- a/index.html +++ b/index.html @@ -2331,64 +2331,14 @@ return null; } - // Create collection effect - const scene = item.scene; - const collectionEffect = scene.add.circle( - item.x, - item.y, - 40, - SAMPLE_COLLECTION_COLOR, - 0.3 - ); + // Check if player has required items + if (!hasItemInInventory('fingerprint_kit')) { + alert("You need a fingerprint kit to collect samples!"); + return null; + } - // Add scanning animation - scene.tweens.add({ - targets: collectionEffect, - scale: { from: 1, to: 1.5 }, - alpha: { from: 0.3, to: 0 }, - duration: SAMPLE_COLLECTION_TIME, - repeat: 0, - yoyo: false, - onComplete: () => { - collectionEffect.destroy(); - - // Create the sample after animation - const sample = { - id: `${item.scenarioData.fingerprintOwner}_${Date.now()}`, - type: "fingerprint", - owner: item.scenarioData.fingerprintOwner, - quality: item.scenarioData.fingerprintQuality, - data: generateFingerprintData(item) - }; - - if (!gameState.biometricSamples) { - gameState.biometricSamples = []; - } - - gameState.biometricSamples.push(sample); - - // Show collection success message with sample details - const qualityPercentage = Math.round(sample.quality * 100); - alert(`Successfully collected a fingerprint sample from ${item.scenarioData.name}\nSample Quality: ${qualityPercentage}%`); - console.log("Collected fingerprint sample:", sample); - } - }); - - // Add scanning particles - const particles = scene.add.particles(item.x, item.y, 'particle', { - speed: 100, - scale: { start: 0.2, end: 0 }, - blendMode: 'ADD', - lifespan: 1000, - quantity: 1, - frequency: 50 - }); - - // Clean up particles after collection - scene.time.delayedCall(SAMPLE_COLLECTION_TIME, () => { - particles.destroy(); - }); - + // Start the dusting minigame + startDustingMinigame(item); return true; } @@ -2754,146 +2704,175 @@ // Add dusting minigame function startDustingMinigame(item) { - const scene = item.scene; - const gameWidth = 400; - const gameHeight = 300; + // Create iframe container + const iframe = document.createElement('div'); + iframe.style.cssText = ` + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 60%; + height: 60%; + background: rgba(0, 0, 0, 0.9); + border: 1px solid #444; + z-index: 1000; + padding: 20px; + border-radius: 5px; + `; - // Create container for minigame - const container = scene.add.container( - scene.cameras.main.centerX - gameWidth/2, - scene.cameras.main.centerY - gameHeight/2 - ); + // Create game container + const gameContainer = document.createElement('div'); + gameContainer.style.cssText = ` + width: 100%; + height: calc(100% - 60px); + display: grid; + grid-template-columns: repeat(20, minmax(0, 1fr)); + grid-template-rows: repeat(20, minmax(0, 1fr)); + gap: 1px; + background: #222; + padding: 10px; + margin-top: 40px; + `; - // Add background - const bg = scene.add.rectangle(0, 0, gameWidth, gameHeight, 0x222222); - container.add(bg); + // Add instructions + const instructions = document.createElement('div'); + instructions.textContent = 'Click and drag to dust for fingerprints.\nReveal the pattern without over-dusting!'; + instructions.style.cssText = ` + position: absolute; + top: 10px; + left: 50%; + transform: translateX(-50%); + color: white; + text-align: center; + font-size: 16px; + `; - // Create grid of cells - const cellSize = 20; - const gridWidth = Math.floor(gameWidth / cellSize); - const gridHeight = Math.floor(gameHeight / cellSize); - const cells = []; + // Add progress display + const progressText = document.createElement('div'); + progressText.style.cssText = ` + position: absolute; + bottom: 10px; + left: 50%; + transform: translateX(-50%); + color: white; + text-align: center; + font-size: 16px; + `; - // Generate random fingerprint pattern + // Generate fingerprint pattern + const gridSize = 20; const fingerprintCells = new Set(); - const centerX = Math.floor(gridWidth / 2); - const centerY = Math.floor(gridHeight / 2); + const centerX = Math.floor(gridSize / 2); + const centerY = Math.floor(gridSize / 2); for (let i = 0; i < 50; i++) { const x = centerX + Math.floor(Math.random() * 6 - 3); const y = centerY + Math.floor(Math.random() * 6 - 3); fingerprintCells.add(`${x},${y}`); } - // Create grid cells - for (let y = 0; y < gridHeight; y++) { - for (let x = 0; x < gridWidth; x++) { - const cell = scene.add.rectangle( - x * cellSize, - y * cellSize, - cellSize - 1, - cellSize - 1, - DUST_COLORS.NONE - ); - cell.setOrigin(0, 0); - cell.setInteractive(); - cell.dustLevel = 0; - cell.hasFingerprint = fingerprintCells.has(`${x},${y}`); - cells.push(cell); - container.add(cell); - - // Add dusting interaction - cell.on('pointermove', function(pointer) { - if (pointer.isDown) { - this.dustLevel = Math.min(3, this.dustLevel + 1); - this.setFillStyle(getDustColor(this.dustLevel, this.hasFingerprint)); - checkDustingProgress(); - } - }); - } - } - - // Add instructions - const instructions = scene.add.text( - gameWidth/2, - -30, - 'Click and drag to dust for fingerprints.\nReveal the pattern without over-dusting!', - { - fontSize: '16px', - fill: '#ffffff', - align: 'center' - } - ); - instructions.setOrigin(0.5); - container.add(instructions); - - // Add progress tracking + // Track progress let revealedPrints = 0; let totalPrints = fingerprintCells.size; let overDusted = 0; - // Add progress display - const progressText = scene.add.text( - gameWidth/2, - gameHeight + 20, - `Revealed: 0/${totalPrints} | Over-dusted: 0`, - { - fontSize: '16px', - fill: '#ffffff', - align: 'center' + // Create grid cells + for (let y = 0; y < gridSize; y++) { + for (let x = 0; x < gridSize; x++) { + const cell = document.createElement('div'); + cell.style.cssText = ` + width: 100%; + height: 100%; + background: black; + position: relative; + cursor: pointer; + `; + cell.dataset.x = x; + cell.dataset.y = y; + cell.dataset.dustLevel = '0'; + cell.dataset.hasFingerprint = fingerprintCells.has(`${x},${y}`); + + let isDragging = false; + + // Add dusting interaction + cell.addEventListener('mousedown', () => isDragging = true); + cell.addEventListener('mouseup', () => isDragging = false); + cell.addEventListener('mouseleave', () => isDragging = false); + cell.addEventListener('mousemove', (e) => { + if (!isDragging) return; + + const dustLevel = parseInt(cell.dataset.dustLevel); + if (dustLevel < 3) { + cell.dataset.dustLevel = (dustLevel + 1).toString(); + updateCellColor(cell); + checkProgress(); + } + }); + + gameContainer.appendChild(cell); } - ); - progressText.setOrigin(0.5); - container.add(progressText); - - // Add close button - const closeButton = scene.add.text( - gameWidth - 10, - -20, - 'X', - { - fontSize: '20px', - fill: '#ffffff' - } - ); - closeButton.setOrigin(1, 0); - closeButton.setInteractive(); - closeButton.on('pointerdown', () => { - container.destroy(); - }); - container.add(closeButton); - - function getDustColor(level, hasFingerprint) { - if (level === 0) return DUST_COLORS.NONE; - if (level === 1) return DUST_COLORS.LIGHT; - if (level === 2) return hasFingerprint ? DUST_COLORS.REVEALED : DUST_COLORS.MEDIUM; - return DUST_COLORS.HEAVY; } - function checkDustingProgress() { + function updateCellColor(cell) { + const dustLevel = parseInt(cell.dataset.dustLevel); + const hasFingerprint = cell.dataset.hasFingerprint === 'true'; + + if (dustLevel === 0) cell.style.background = 'black'; + else if (dustLevel === 1) cell.style.background = '#444'; + else if (dustLevel === 2) cell.style.background = hasFingerprint ? '#0f0' : '#888'; + else cell.style.background = '#ccc'; + } + + function checkProgress() { revealedPrints = 0; overDusted = 0; - cells.forEach(cell => { - if (cell.hasFingerprint && cell.dustLevel === 2) { - revealedPrints++; - } - if (cell.dustLevel === 3) { - overDusted++; - } + gameContainer.childNodes.forEach(cell => { + const dustLevel = parseInt(cell.dataset.dustLevel); + const hasFingerprint = cell.dataset.hasFingerprint === 'true'; + + if (hasFingerprint && dustLevel === 2) revealedPrints++; + if (dustLevel === 3) overDusted++; }); - progressText.setText( - `Revealed: ${revealedPrints}/${totalPrints} | Over-dusted: ${overDusted}` - ); + progressText.textContent = `Revealed: ${revealedPrints}/${totalPrints} | Over-dusted: ${overDusted}`; - // Check win condition if (revealedPrints === totalPrints && overDusted < 10) { setTimeout(() => { - container.destroy(); + document.body.removeChild(iframe); + scene.input.mouse.enabled = true; collectFingerprint(item); }, 1000); } } + + // Add close button + const closeButton = document.createElement('button'); + closeButton.textContent = 'X'; + closeButton.style.cssText = ` + position: absolute; + right: 10px; + top: 10px; + background: none; + border: none; + color: white; + font-size: 20px; + cursor: pointer; + `; + closeButton.onclick = () => { + document.body.removeChild(iframe); + scene.input.mouse.enabled = true; + }; + + // Assemble the interface + iframe.appendChild(closeButton); + iframe.appendChild(instructions); + iframe.appendChild(gameContainer); + iframe.appendChild(progressText); + document.body.appendChild(iframe); + + // Disable game movement + const scene = item.scene; + scene.input.mouse.enabled = false; } From 503b5ca7a38c9fddaf06a22d059f3897c62acd97 Mon Sep 17 00:00:00 2001 From: Damian-I Date: Tue, 25 Feb 2025 01:28:36 +0000 Subject: [PATCH 11/14] Fixed the no dragging issue --- index.html | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/index.html b/index.html index 5db867b..69408c3 100644 --- a/index.html +++ b/index.html @@ -2791,27 +2791,30 @@ cell.dataset.dustLevel = '0'; cell.dataset.hasFingerprint = fingerprintCells.has(`${x},${y}`); - let isDragging = false; - - // Add dusting interaction - cell.addEventListener('mousedown', () => isDragging = true); - cell.addEventListener('mouseup', () => isDragging = false); - cell.addEventListener('mouseleave', () => isDragging = false); - cell.addEventListener('mousemove', (e) => { - if (!isDragging) return; - - const dustLevel = parseInt(cell.dataset.dustLevel); - if (dustLevel < 3) { - cell.dataset.dustLevel = (dustLevel + 1).toString(); - updateCellColor(cell); - checkProgress(); - } - }); - gameContainer.appendChild(cell); } } + // Add dragging interaction at container level + let isDragging = false; + gameContainer.addEventListener('mousedown', () => isDragging = true); + gameContainer.addEventListener('mouseup', () => isDragging = false); + gameContainer.addEventListener('mouseleave', () => isDragging = false); + gameContainer.addEventListener('mousemove', (e) => { + if (!isDragging) return; + + // Get the cell element under the cursor + const cell = document.elementFromPoint(e.clientX, e.clientY); + if (cell && cell.dataset.dustLevel !== undefined) { + const dustLevel = parseInt(cell.dataset.dustLevel); + if (dustLevel < 3) { + cell.dataset.dustLevel = (dustLevel + 1).toString(); + updateCellColor(cell); + checkProgress(); + } + } + }); + function updateCellColor(cell) { const dustLevel = parseInt(cell.dataset.dustLevel); const hasFingerprint = cell.dataset.hasFingerprint === 'true'; From 17a89ea2efe6ccd22f8f0299dfcb6e080441a5b3 Mon Sep 17 00:00:00 2001 From: Damian-I Date: Tue, 25 Feb 2025 01:32:46 +0000 Subject: [PATCH 12/14] Fingerprint minigame working --- index.html | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/index.html b/index.html index 69408c3..a19f0e0 100644 --- a/index.html +++ b/index.html @@ -2797,6 +2797,8 @@ // Add dragging interaction at container level let isDragging = false; + let lastDustTime = {}; // Track last dust time for each cell + gameContainer.addEventListener('mousedown', () => isDragging = true); gameContainer.addEventListener('mouseup', () => isDragging = false); gameContainer.addEventListener('mouseleave', () => isDragging = false); @@ -2806,11 +2808,21 @@ // Get the cell element under the cursor const cell = document.elementFromPoint(e.clientX, e.clientY); if (cell && cell.dataset.dustLevel !== undefined) { + const cellId = `${cell.dataset.x},${cell.dataset.y}`; + const currentTime = Date.now(); const dustLevel = parseInt(cell.dataset.dustLevel); - if (dustLevel < 3) { - cell.dataset.dustLevel = (dustLevel + 1).toString(); - updateCellColor(cell); - checkProgress(); + + // Only allow dusting every 100ms for each cell + if (!lastDustTime[cellId] || currentTime - lastDustTime[cellId] > 100) { + if (dustLevel < 3) { + // Increment dust level with 33% chance after level 1 + if (dustLevel < 1 || Math.random() < 0.33) { + cell.dataset.dustLevel = (dustLevel + 1).toString(); + updateCellColor(cell); + checkProgress(); + } + lastDustTime[cellId] = currentTime; + } } } }); From 1765662a5ea4a231b83364059728f8c18f42460d Mon Sep 17 00:00:00 2001 From: Damian-I Date: Tue, 25 Feb 2025 02:00:44 +0000 Subject: [PATCH 13/14] Fixed minigame bugs, fingerprint is added when minigame is completed --- index.html | 107 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 92 insertions(+), 15 deletions(-) diff --git a/index.html b/index.html index a19f0e0..c1b45cd 100644 --- a/index.html +++ b/index.html @@ -2736,15 +2736,22 @@ // Add instructions const instructions = document.createElement('div'); - instructions.textContent = 'Click and drag to dust for fingerprints.\nReveal the pattern without over-dusting!'; + instructions.innerHTML = ` +

Fingerprint Dusting

+

+ Drag to dust the surface and reveal fingerprints.
+ 🔍 Gray = Light dusting
+ 🟢 Green = Fingerprint found!
+ ⚠️ White = Over-dusted (avoid this)
+ Find all fingerprints with minimal over-dusting. +

+ `; instructions.style.cssText = ` position: absolute; top: 10px; left: 50%; transform: translateX(-50%); - color: white; - text-align: center; - font-size: 16px; + width: 90%; `; // Add progress display @@ -2764,10 +2771,23 @@ const fingerprintCells = new Set(); const centerX = Math.floor(gridSize / 2); const centerY = Math.floor(gridSize / 2); + + // Create a larger pattern (about 50 cells) spread around the center for (let i = 0; i < 50; i++) { - const x = centerX + Math.floor(Math.random() * 6 - 3); - const y = centerY + Math.floor(Math.random() * 6 - 3); - fingerprintCells.add(`${x},${y}`); + const x = centerX + Math.floor(Math.random() * 10 - 5); // Increased spread + const y = centerY + Math.floor(Math.random() * 10 - 5); // Increased spread + if (x >= 0 && x < gridSize && y >= 0 && y < gridSize) { + fingerprintCells.add(`${x},${y}`); + } + } + + // If we didn't get enough cells, add more until we reach target + while (fingerprintCells.size < 50) { + const x = centerX + Math.floor(Math.random() * 12 - 6); + const y = centerY + Math.floor(Math.random() * 12 - 6); + if (x >= 0 && x < gridSize && y >= 0 && y < gridSize) { + fingerprintCells.add(`${x},${y}`); + } } // Track progress @@ -2831,10 +2851,18 @@ const dustLevel = parseInt(cell.dataset.dustLevel); const hasFingerprint = cell.dataset.hasFingerprint === 'true'; - if (dustLevel === 0) cell.style.background = 'black'; - else if (dustLevel === 1) cell.style.background = '#444'; - else if (dustLevel === 2) cell.style.background = hasFingerprint ? '#0f0' : '#888'; - else cell.style.background = '#ccc'; + if (dustLevel === 0) { + cell.style.background = 'black'; + } + else if (dustLevel === 1) { + cell.style.background = '#444'; + } + else if (dustLevel === 2) { + cell.style.background = hasFingerprint ? '#0f0' : '#888'; + } + else { + cell.style.background = '#ccc'; + } } function checkProgress() { @@ -2849,14 +2877,63 @@ if (dustLevel === 3) overDusted++; }); - progressText.textContent = `Revealed: ${revealedPrints}/${totalPrints} | Over-dusted: ${overDusted}`; + const requiredPrints = Math.ceil(totalPrints * 0.4); // 40% requirement + progressText.innerHTML = ` +
Found: ${revealedPrints}/${requiredPrints} required prints
+
+ Over-dusted: ${overDusted}/10 max +
+ `; - if (revealedPrints === totalPrints && overDusted < 10) { + // Check win condition with 60% requirement + if (revealedPrints >= requiredPrints && overDusted < 10) { + // Show success message + const successMessage = document.createElement('div'); + successMessage.style.cssText = ` + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: rgba(0, 0, 0, 0.9); + padding: 20px; + border-radius: 5px; + color: #0f0; + font-size: 20px; + text-align: center; + z-index: 1001; + `; + successMessage.textContent = "Fingerprint successfully collected!"; + iframe.appendChild(successMessage); + + // Disable further interaction + isDragging = false; + gameContainer.style.pointerEvents = 'none'; + setTimeout(() => { + // Add fingerprint to gameState + if (!gameState.biometricSamples) { + gameState.biometricSamples = []; + } + + const sample = { + id: generateFingerprintData(item), + type: 'fingerprint', + owner: item.scenarioData.fingerprintOwner, + quality: Math.random() * 0.3 + 0.7, // Random quality between 0.7 and 1.0 + data: generateFingerprintData(item) + }; + + gameState.biometricSamples.push(sample); + + // Remove the minigame document.body.removeChild(iframe); scene.input.mouse.enabled = true; - collectFingerprint(item); - }, 1000); + + // Mark item as collected + if (item.scenarioData) { + item.scenarioData.hasFingerprint = false; + } + }, 1500); } } From 4339e95767bf996362bcc0fb2d15639a30f636d1 Mon Sep 17 00:00:00 2001 From: Damian-I Date: Tue, 25 Feb 2025 02:09:36 +0000 Subject: [PATCH 14/14] Increased over-dusted threshold from 10 to 25, made the minigame fail when threshold was surpassed. POTENTIALLY IMPLEMENT: limit attempts to amount based on fingerprint quality if failed minigame --- index.html | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/index.html b/index.html index c1b45cd..606f074 100644 --- a/index.html +++ b/index.html @@ -2880,13 +2880,44 @@ const requiredPrints = Math.ceil(totalPrints * 0.4); // 40% requirement progressText.innerHTML = `
Found: ${revealedPrints}/${requiredPrints} required prints
-
- Over-dusted: ${overDusted}/10 max +
+ Over-dusted: ${overDusted}/25 max
`; - // Check win condition with 60% requirement - if (revealedPrints >= requiredPrints && overDusted < 10) { + // Check fail condition first + if (overDusted >= 25) { + // Show failure message + const failureMessage = document.createElement('div'); + failureMessage.style.cssText = ` + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: rgba(0, 0, 0, 0.9); + padding: 20px; + border-radius: 5px; + color: #f00; + font-size: 20px; + text-align: center; + z-index: 1001; + `; + failureMessage.textContent = "Too many over-dusted areas!"; + iframe.appendChild(failureMessage); + + // Disable further interaction + isDragging = false; + gameContainer.style.pointerEvents = 'none'; + + setTimeout(() => { + document.body.removeChild(iframe); + scene.input.mouse.enabled = true; + }, 1500); + return; + } + + // Check win condition (existing code) + if (revealedPrints >= requiredPrints && overDusted < 25) { // Show success message const successMessage = document.createElement('div'); successMessage.style.cssText = `