diff --git a/index.html b/index.html index 178d006..065f38e 100644 --- a/index.html +++ b/index.html @@ -1560,8 +1560,8 @@ this.load.image('fingerprint_kit', 'assets/objects/fingerprint_kit.png'); this.load.image('lockpick', 'assets/objects/lockpick.png'); - //this.load.json('gameScenarioJSON', 'assets/scenarios/biometric_breach.json'); - this.load.json('gameScenarioJSON', 'assets/scenarios/ceo_exfil.json'); + this.load.json('gameScenarioJSON', 'assets/scenarios/biometric_breach.json'); + //this.load.json('gameScenarioJSON', 'assets/scenarios/ceo_exfil.json'); gameScenario = this.cache.json.get('gameScenarioJSON'); } @@ -4184,761 +4184,6 @@ return `fp_unknown_${Date.now()}`; } - // Add dusting minigame - function startDustingMinigame(item) { - // Create iframe container - const iframe = document.createElement('div'); - iframe.style.cssText = ` - position: fixed; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 75%; - height: 75%; - background: rgba(0, 0, 0, 0.9); - border: 1px solid #444; - z-index: 1000; - padding: 20px; - border-radius: 5px; - display: flex; - flex-direction: column; - overflow: hidden; - `; - - // Create game container - const gameContainer = document.createElement('div'); - gameContainer.style.cssText = ` - width: 80%; - height: 80%; - max-width: 600px; - max-height: 600px; - display: grid; - grid-template-columns: repeat(30, 1fr); - grid-template-rows: repeat(30, 1fr); - gap: 1px; - background: #1a1a1a; - padding: 5px; - margin: 70px auto 20px auto; - border-radius: 5px; - box-shadow: 0 0 15px rgba(0, 0, 0, 0.5) inset; - position: relative; - overflow: hidden; - `; - - // Add background texture/pattern for a more realistic surface - const gridBackground = document.createElement('div'); - gridBackground.style.cssText = ` - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - opacity: 0.3; - pointer-events: none; - z-index: 0; - `; - - // Create the grid pattern using encoded SVG - const svgGrid = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'%3E%3Crect width='100' height='100' fill='%23111'/%3E%3Cpath d='M0 50h100M50 0v100' stroke='%23222' stroke-width='0.5'/%3E%3Cpath d='M25 0v100M75 0v100M0 25h100M0 75h100' stroke='%23191919' stroke-width='0.3'/%3E%3C/svg%3E`; - - gridBackground.style.backgroundImage = `url('${svgGrid}')`; - gameContainer.appendChild(gridBackground); - - // Add instructions and header with difficulty selection - const header = document.createElement('div'); - header.style.cssText = ` - position: absolute; - top: 0; - left: 0; - width: 100%; - background: rgba(30, 30, 30, 0.9); - padding: 10px; - display: flex; - flex-direction: column; - align-items: center; - `; - - header.innerHTML = ` -

Fingerprint Dusting

-

- Drag to dust the surface and reveal fingerprints. Avoid over-dusting! -

- `; - - // Add progress display - const progressText = document.createElement('div'); - progressText.style.cssText = ` - position: absolute; - bottom: 15px; - left: 50%; - transform: translateX(-50%); - color: white; - text-align: center; - font-size: 16px; - background: rgba(0, 0, 0, 0.6); - padding: 5px 15px; - border-radius: 15px; - z-index: 10; - width: 80%; - max-width: 500px; - `; - - // Add tool selection - const toolsContainer = document.createElement('div'); - toolsContainer.style.cssText = ` - position: absolute; - bottom: 15px; - left: 15px; - display: flex; - gap: 10px; - z-index: 10; - flex-wrap: wrap; - max-width: 30%; - `; - - const tools = [ - { name: 'Fine', size: 1, color: '#3498db', radius: 0 }, // Only affects current cell - { name: 'Medium', size: 2, color: '#2ecc71', radius: 1 }, // Affects current cell and adjacent - { name: 'Wide', size: 3, color: '#e67e22', radius: 2 } // Affects current cell and 2 cells around - ]; - - let currentTool = tools[1]; // Start with medium brush - - tools.forEach(tool => { - const toolButton = document.createElement('button'); - toolButton.className = tool.name === currentTool.name ? 'tool-button active' : 'tool-button'; - toolButton.textContent = tool.name; - toolButton.style.cssText = ` - background: ${tool.color}; - color: white; - border: none; - border-radius: 3px; - padding: 5px 10px; - cursor: pointer; - opacity: ${tool.name === currentTool.name ? '1' : '0.7'}; - `; - - toolButton.addEventListener('click', () => { - document.querySelectorAll('.tool-button').forEach(btn => { - btn.classList.remove('active'); - btn.style.opacity = '0.7'; - }); - toolButton.classList.add('active'); - toolButton.style.opacity = '1'; - currentTool = tool; - }); - - toolsContainer.appendChild(toolButton); - }); - - // Generate fingerprint pattern - const gridSize = 30; // Increased from 20 to 30 - let fingerprintCells = new Set(); - const centerX = Math.floor(gridSize / 2); - const centerY = Math.floor(gridSize / 2); - - // Game state variables - let difficultySettings = { - easy: { - requiredCoverage: 0.3, // 30% of prints - maxOverDusted: 50, // Increased due to more cells - fingerprints: 60, // Increased proportionally - pattern: 'simple' - }, - medium: { - requiredCoverage: 0.4, // 40% of prints - maxOverDusted: 40, // Increased due to more cells - fingerprints: 75, // Increased proportionally - pattern: 'medium' - }, - hard: { - requiredCoverage: 0.5, // 50% of prints - maxOverDusted: 25, // Increased due to more cells - fingerprints: 90, // Increased proportionally - pattern: 'complex' - } - }; - - let currentDifficulty = item.scenarioData.fingerprintDifficulty; - - // Generate fingerprint pattern based on difficulty - function generateFingerprint(difficulty) { - const pattern = difficultySettings[difficulty].pattern; - const numPrints = difficultySettings[difficulty].fingerprints; - const newFingerprintCells = new Set(); - - if (pattern === 'simple') { - // Simple oval-like pattern - for (let i = 0; i < numPrints; i++) { - const angle = (i / numPrints) * Math.PI * 2; - const distance = 5 + Math.random() * 3; - const x = Math.floor(centerX + Math.cos(angle) * distance); - const y = Math.floor(centerY + Math.sin(angle) * distance); - - if (x >= 0 && x < gridSize && y >= 0 && y < gridSize) { - newFingerprintCells.add(`${x},${y}`); - - // Add a few adjacent cells to make it less sparse - for (let j = 0; j < 2; j++) { - const nx = x + Math.floor(Math.random() * 3) - 1; - const ny = y + Math.floor(Math.random() * 3) - 1; - if (nx >= 0 && nx < gridSize && ny >= 0 && ny < gridSize) { - newFingerprintCells.add(`${nx},${ny}`); - } - } - } - } - } else if (pattern === 'medium') { - // Medium complexity - spiral pattern with variations - for (let i = 0; i < numPrints; i++) { - const t = i / numPrints * 5; - const distance = 2 + t * 0.8; - const noise = Math.random() * 2 - 1; - const x = Math.floor(centerX + Math.cos(t * Math.PI * 2) * (distance + noise)); - const y = Math.floor(centerY + Math.sin(t * Math.PI * 2) * (distance + noise)); - - if (x >= 0 && x < gridSize && y >= 0 && y < gridSize) { - newFingerprintCells.add(`${x},${y}`); - } - } - - // Add whorls and arches - for (let i = 0; i < 20; i++) { - const angle = (i / 20) * Math.PI * 2; - const distance = 7; - const x = Math.floor(centerX + Math.cos(angle) * distance); - const y = Math.floor(centerY + Math.sin(angle) * distance); - - if (x >= 0 && x < gridSize && y >= 0 && y < gridSize) { - newFingerprintCells.add(`${x},${y}`); - - // Add ridge-like pattern - for (let j = 1; j <= 3; j++) { - const ridgeX = Math.floor(centerX + Math.cos(angle) * (distance - j)); - const ridgeY = Math.floor(centerY + Math.sin(angle) * (distance - j)); - if (ridgeX >= 0 && ridgeX < gridSize && ridgeY >= 0 && ridgeY < gridSize) { - newFingerprintCells.add(`${ridgeX},${ridgeY}`); - } - } - } - } - } else { - // Complex pattern - detailed whorls and ridge patterns - for (let i = 0; i < numPrints; i++) { - // Main loop - create a complex whorl pattern - const t = i / numPrints * 8; - const distance = 2 + t * 0.6; - const noise = Math.sin(t * 5) * 1.5; - const x = Math.floor(centerX + Math.cos(t * Math.PI * 2) * (distance + noise)); - const y = Math.floor(centerY + Math.sin(t * Math.PI * 2) * (distance + noise)); - - if (x >= 0 && x < gridSize && y >= 0 && y < gridSize) { - newFingerprintCells.add(`${x},${y}`); - } - - // Add bifurcations and ridge endings - if (i % 5 === 0) { - const bifAngle = t * Math.PI * 2 + Math.PI/4; - const bx = Math.floor(x + Math.cos(bifAngle) * 1); - const by = Math.floor(y + Math.sin(bifAngle) * 1); - if (bx >= 0 && bx < gridSize && by >= 0 && by < gridSize) { - newFingerprintCells.add(`${bx},${by}`); - } - } - } - - // Add delta patterns - for (let d = 0; d < 3; d++) { - const deltaAngle = (d / 3) * Math.PI * 2; - const deltaX = Math.floor(centerX + Math.cos(deltaAngle) * 8); - const deltaY = Math.floor(centerY + Math.sin(deltaAngle) * 8); - - for (let r = 0; r < 5; r++) { - for (let a = 0; a < 3; a++) { - const rayAngle = deltaAngle + (a - 1) * Math.PI/4; - const rx = Math.floor(deltaX + Math.cos(rayAngle) * r); - const ry = Math.floor(deltaY + Math.sin(rayAngle) * r); - if (rx >= 0 && rx < gridSize && ry >= 0 && ry < gridSize) { - newFingerprintCells.add(`${rx},${ry}`); - } - } - } - } - } - - // Ensure we have at least the minimum number of cells - while (newFingerprintCells.size < numPrints) { - 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) { - newFingerprintCells.add(`${x},${y}`); - } - } - - return newFingerprintCells; - } - - // Initialize with medium difficulty - fingerprintCells = generateFingerprint(currentDifficulty); - - // Create particle container for dust effects - const particleContainer = document.createElement('div'); - particleContainer.style.cssText = ` - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - pointer-events: none; - z-index: 5; - overflow: hidden; - `; - - // Create function to add dust particles - function createDustParticles(x, y, intensity, color) { - const numParticles = Math.floor(5 + intensity * 5); // 5-10 particles based on intensity - - for (let i = 0; i < numParticles; i++) { - const particle = document.createElement('div'); - const size = Math.random() * 3 + 1; // 1-4px - const angle = Math.random() * Math.PI * 2; - const distance = Math.random() * 20 * intensity; - const duration = Math.random() * 1000 + 500; // 500-1500ms - - particle.style.cssText = ` - position: absolute; - width: ${size}px; - height: ${size}px; - background: ${color}; - border-radius: 50%; - opacity: ${Math.random() * 0.3 + 0.3}; - top: ${y}px; - left: ${x}px; - transform: translate(-50%, -50%); - pointer-events: none; - z-index: 6; - `; - - particleContainer.appendChild(particle); - - // Animate the particle - const animation = particle.animate([ - { - transform: 'translate(-50%, -50%)', - opacity: particle.style.opacity - }, - { - transform: `translate( - calc(-50% + ${Math.cos(angle) * distance}px), - calc(-50% + ${Math.sin(angle) * distance}px) - )`, - opacity: 0 - } - ], { - duration: duration, - easing: 'cubic-bezier(0.25, 1, 0.5, 1)' - }); - - animation.onfinish = () => { - particle.remove(); - }; - } - } - - // Track progress - let revealedPrints = 0; - let totalPrints = fingerprintCells.size; - let overDusted = 0; - let requiredPrints = Math.ceil(totalPrints * difficultySettings[currentDifficulty].requiredCoverage); - - // 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; - transition: background-color 0.1s; - cursor: pointer; - `; - cell.dataset.x = x; - cell.dataset.y = y; - cell.dataset.dustLevel = '0'; - cell.dataset.hasFingerprint = fingerprintCells.has(`${x},${y}`) ? 'true' : 'false'; - - gameContainer.appendChild(cell); - } - } - - // Add dragging interaction - 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); - 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) { - // Get current cell coordinates - const centerX = parseInt(cell.dataset.x); - const centerY = parseInt(cell.dataset.y); - - // Get a list of cells to dust based on the brush radius - const cellsToDust = []; - const radius = currentTool.radius; - - // Add the current cell and cells within radius - for (let y = centerY - radius; y <= centerY + radius; y++) { - for (let x = centerX - radius; x <= centerX + radius; x++) { - // Skip cells outside the grid - if (x < 0 || x >= gridSize || y < 0 || y >= gridSize) continue; - - // For medium brush, use a diamond pattern (taxicab distance) - if (currentTool.size === 2) { - // Manhattan distance: |x1-x2| + |y1-y2| - const distance = Math.abs(x - centerX) + Math.abs(y - centerY); - if (distance > radius) continue; // Skip if too far away - } - // For wide brush, use a circle pattern (Euclidean distance) - else if (currentTool.size === 3) { - // Euclidean distance: √[(x1-x2)² + (y1-y2)²] - const distance = Math.sqrt(Math.pow(x - centerX, 2) + Math.pow(y - centerY, 2)); - if (distance > radius) continue; // Skip if too far away - } - - // Find this cell in the DOM - const targetCell = gameContainer.querySelector(`[data-x="${x}"][data-y="${y}"]`); - if (targetCell) { - cellsToDust.push(targetCell); - } - } - } - - // Get cell position for particles (center cell) - const cellRect = cell.getBoundingClientRect(); - const particleContainerRect = particleContainer.getBoundingClientRect(); - const cellCenterX = (cellRect.left + cellRect.width / 2) - particleContainerRect.left; - const cellCenterY = (cellRect.top + cellRect.height / 2) - particleContainerRect.top; - - // Process all cells to dust - cellsToDust.forEach(targetCell => { - const cellId = `${targetCell.dataset.x},${targetCell.dataset.y}`; - const currentTime = Date.now(); - const dustLevel = parseInt(targetCell.dataset.dustLevel); - - // Tool intensity affects dusting rate and particle effects - const toolIntensity = currentTool.size / 3; // 0.33 to 1 - - // Only allow dusting every 50-150ms for each cell (based on tool size) - const cooldown = 150 - (toolIntensity * 100); // 50ms for wide brush, 150ms for fine - - if (!lastDustTime[cellId] || currentTime - lastDustTime[cellId] > cooldown) { - if (dustLevel < 3) { - // Increment dust level with a probability based on tool intensity - const dustProbability = toolIntensity * 0.5 + 0.1; // 0.1-0.6 chance based on tool - - if (dustLevel < 1 || Math.random() < dustProbability) { - targetCell.dataset.dustLevel = (dustLevel + 1).toString(); - updateCellColor(targetCell); - - // Create dust particles for the current cell or at a position calculated for surrounding cells - if (targetCell === cell) { - // Center cell - use the already calculated position - const hasFingerprint = targetCell.dataset.hasFingerprint === 'true'; - let particleColor = dustLevel === 1 ? '#666' : (hasFingerprint ? '#1aff1a' : '#aaa'); - createDustParticles(cellCenterX, cellCenterY, toolIntensity, particleColor); - } else { - // For surrounding cells, calculate their relative position from the center cell - const targetCellRect = targetCell.getBoundingClientRect(); - const targetCellX = (targetCellRect.left + targetCellRect.width / 2) - particleContainerRect.left; - const targetCellY = (targetCellRect.top + targetCellRect.height / 2) - particleContainerRect.top; - - const hasFingerprint = targetCell.dataset.hasFingerprint === 'true'; - let particleColor = dustLevel === 1 ? '#666' : (hasFingerprint ? '#1aff1a' : '#aaa'); - - // Create fewer particles for surrounding cells - const reducedIntensity = toolIntensity * 0.6; - createDustParticles(targetCellX, targetCellY, reducedIntensity, particleColor); - } - } - lastDustTime[cellId] = currentTime; - } - } - }); - - // Update progress after dusting - checkProgress(); - } - }); - - function updateCellColor(cell) { - const dustLevel = parseInt(cell.dataset.dustLevel); - const hasFingerprint = cell.dataset.hasFingerprint === 'true'; - - if (dustLevel === 0) { - cell.style.background = 'black'; - cell.style.boxShadow = 'none'; - } - else if (dustLevel === 1) { - cell.style.background = '#444'; - cell.style.boxShadow = 'inset 0 0 3px rgba(255,255,255,0.2)'; - } - else if (dustLevel === 2) { - if (hasFingerprint) { - cell.style.background = '#0f0'; - cell.style.boxShadow = 'inset 0 0 5px rgba(0,255,0,0.5), 0 0 5px rgba(0,255,0,0.3)'; - } else { - cell.style.background = '#888'; - cell.style.boxShadow = 'inset 0 0 4px rgba(255,255,255,0.3)'; - } - } - else { - cell.style.background = '#ccc'; - cell.style.boxShadow = 'inset 0 0 5px rgba(255,255,255,0.5)'; - } - } - - function checkProgress() { - revealedPrints = 0; - overDusted = 0; - - gameContainer.childNodes.forEach(cell => { - if (cell.dataset) { // Check if it's a cell element - const dustLevel = parseInt(cell.dataset.dustLevel || '0'); - const hasFingerprint = cell.dataset.hasFingerprint === 'true'; - - if (hasFingerprint && dustLevel === 2) revealedPrints++; - if (dustLevel === 3) overDusted++; - } - }); - - // Update progress display - progressText.innerHTML = ` -
- Found: ${revealedPrints}/${requiredPrints} required prints - - Over-dusted: ${overDusted}/${difficultySettings[currentDifficulty].maxOverDusted} max - -
-
-
-
- `; - - // Check fail condition first - if (overDusted >= difficultySettings[currentDifficulty].maxOverDusted) { - showFailure("Too many over-dusted areas!"); - return; - } - - // Check win condition - if (revealedPrints >= requiredPrints) { - showSuccess(); - } - } - - function showSuccess() { - // Calculate quality based on dusting precision - const dustPenalty = overDusted / difficultySettings[currentDifficulty].maxOverDusted; // 0-1 - const coverageBonus = revealedPrints / totalPrints; // 0-1 - - // Higher quality for more coverage and less over-dusting - const quality = 0.7 + (coverageBonus * 0.25) - (dustPenalty * 0.15); - - // 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; - box-shadow: 0 0 20px rgba(0, 255, 0, 0.3); - `; - - const qualityPercentage = Math.round(quality * 100); - const qualityRating = qualityPercentage >= 95 ? 'Perfect' : - qualityPercentage >= 85 ? 'Excellent' : - qualityPercentage >= 75 ? 'Good' : 'Acceptable'; - - successMessage.innerHTML = ` -
Fingerprint successfully collected!
-
Quality: ${qualityRating} (${qualityPercentage}%)
-
- Prints revealed: ${revealedPrints}/${totalPrints}
- Over-dusted areas: ${overDusted}
- Difficulty: ${currentDifficulty.charAt(0).toUpperCase() + currentDifficulty.slice(1)} -
- `; - - 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: quality, // Quality between 0.7 and ~1.0 - data: generateFingerprintData(item), - timestamp: Date.now() - }; - - gameState.biometricSamples.push(sample); - - // Remove the minigame - document.body.removeChild(iframe); - scene.input.mouse.enabled = true; - - // Mark item as collected - if (item.scenarioData) { - item.scenarioData.hasFingerprint = false; - } - - // Update the biometrics panel and count - updateBiometricsPanel(); - updateBiometricsCount(); - - // Show notification - gameAlert(`Collected ${sample.owner}'s fingerprint sample (${qualityRating} quality)`, 'success', 'Sample Acquired', 3000); - }, 2000); - } - - function showFailure(reason) { - // 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; - box-shadow: 0 0 20px rgba(255, 0, 0, 0.3); - `; - failureMessage.innerHTML = ` -
${reason}
-
Try again with more careful dusting.
- `; - - iframe.appendChild(failureMessage); - - // Disable further interaction - isDragging = false; - gameContainer.style.pointerEvents = 'none'; - - setTimeout(() => { - document.body.removeChild(iframe); - scene.input.mouse.enabled = true; - }, 2000); - } - - // Handle difficulty buttons - iframe.addEventListener('click', (e) => { - if (e.target.classList.contains('difficulty-button')) { - // Get difficulty from button - const newDifficulty = e.target.id.split('-')[0]; // easy, medium, or hard - - // Only change if different - if (newDifficulty !== currentDifficulty) { - currentDifficulty = newDifficulty; - - // Update active button - document.querySelectorAll('.difficulty-button').forEach(btn => { - btn.classList.remove('active'); - btn.style.opacity = '0.7'; - }); - e.target.classList.add('active'); - e.target.style.opacity = '1'; - - // Reset the game with new difficulty - resetGame(); - } - } - }); - - function resetGame() { - // Reset game state - fingerprintCells = generateFingerprint(currentDifficulty); - totalPrints = fingerprintCells.size; - requiredPrints = Math.ceil(totalPrints * difficultySettings[currentDifficulty].requiredCoverage); - revealedPrints = 0; - overDusted = 0; - - // Reset cells - gameContainer.childNodes.forEach(node => { - if (node.dataset && node.dataset.x !== undefined) { - const x = parseInt(node.dataset.x); - const y = parseInt(node.dataset.y); - node.dataset.hasFingerprint = fingerprintCells.has(`${x},${y}`) ? 'true' : 'false'; - node.dataset.dustLevel = '0'; - updateCellColor(node); - } - }); - // Update progress - checkProgress(); - } - - // 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; - z-index: 10; - `; - closeButton.onclick = () => { - document.body.removeChild(iframe); - scene.input.mouse.enabled = true; - }; - - // Assemble the interface - iframe.appendChild(closeButton); - iframe.appendChild(header); - iframe.appendChild(gameContainer); - iframe.appendChild(particleContainer); - iframe.appendChild(progressText); - iframe.appendChild(toolsContainer); - document.body.appendChild(iframe); - - // Start the game - checkProgress(); - - // Disable game movement - const scene = item.scene; - scene.input.mouse.enabled = false; - } // Minigame Framework const MinigameFramework = { @@ -6422,6 +5667,807 @@ ); } + // Dusting Minigame Scene implementation + class DustingMinigame extends MinigameFramework.MinigameScene { + constructor(container, params) { + super(container, params); + + this.item = params.item; + + // Game state variables + this.difficultySettings = { + easy: { + requiredCoverage: 0.3, // 30% of prints + maxOverDusted: 50, // Increased due to more cells + fingerprints: 60, // Increased proportionally + pattern: 'simple' + }, + medium: { + requiredCoverage: 0.4, // 40% of prints + maxOverDusted: 40, // Increased due to more cells + fingerprints: 75, // Increased proportionally + pattern: 'medium' + }, + hard: { + requiredCoverage: 0.5, // 50% of prints + maxOverDusted: 25, // Increased due to more cells + fingerprints: 90, // Increased proportionally + pattern: 'complex' + } + }; + + this.currentDifficulty = this.item.scenarioData.fingerprintDifficulty; + this.gridSize = 30; + this.fingerprintCells = new Set(); + this.revealedPrints = 0; + this.overDusted = 0; + this.isDragging = false; + this.lastDustTime = {}; + + // Tools configuration + this.tools = [ + { name: 'Fine', size: 1, color: '#3498db', radius: 0 }, // Only affects current cell + { name: 'Medium', size: 2, color: '#2ecc71', radius: 1 }, // Affects current cell and adjacent + { name: 'Wide', size: 3, color: '#e67e22', radius: 2 } // Affects current cell and 2 cells around + ]; + this.currentTool = this.tools[1]; // Start with medium brush + } + + init() { + super.init(); + + console.log("Dusting minigame container:", this.container); + + // Fix: Apply proper display style (flex instead of relying on the framework) + this.container.style.display = 'flex'; + + // Apply CSS styles to the container + this.container.style.cssText = ` + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 75%; + height: 75%; + background: rgba(0, 0, 0, 0.9); + border: 1px solid #444; + z-index: 1002; /* Important: Higher than the framework overlay */ + padding: 20px; + border-radius: 5px; + display: flex; + flex-direction: column; + overflow: hidden; + `; + + // Create header with instructions + const header = document.createElement('div'); + header.style.cssText = ` + position: absolute; + top: 0; + left: 0; + width: 100%; + background: rgba(30, 30, 30, 0.9); + padding: 10px; + display: flex; + flex-direction: column; + align-items: center; + z-index: 5; + `; + + header.innerHTML = ` +

Fingerprint Dusting

+

+ Drag to dust the surface and reveal fingerprints. Avoid over-dusting! +

+ `; + + // Create game container + const gameContainer = document.createElement('div'); + gameContainer.style.cssText = ` + width: 80%; + height: 80%; + max-width: 600px; + max-height: 600px; + display: grid; + grid-template-columns: repeat(30, 1fr); + grid-template-rows: repeat(30, 1fr); + gap: 1px; + background: #1a1a1a; + padding: 5px; + margin: 70px auto 20px auto; + border-radius: 5px; + box-shadow: 0 0 15px rgba(0, 0, 0, 0.5) inset; + position: relative; + overflow: hidden; + cursor: crosshair; + `; + + // Add background texture/pattern for a more realistic surface + const gridBackground = document.createElement('div'); + gridBackground.style.cssText = ` + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + opacity: 0.3; + pointer-events: none; + z-index: 0; + `; + + // Create the grid pattern using encoded SVG + const svgGrid = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'%3E%3Crect width='100' height='100' fill='%23111'/%3E%3Cpath d='M0 50h100M50 0v100' stroke='%23222' stroke-width='0.5'/%3E%3Cpath d='M25 0v100M75 0v100M0 25h100M0 75h100' stroke='%23191919' stroke-width='0.3'/%3E%3C/svg%3E`; + + gridBackground.style.backgroundImage = `url('${svgGrid}')`; + gameContainer.appendChild(gridBackground); + + // Add progress display + const progressText = document.createElement('div'); + progressText.style.cssText = ` + position: absolute; + bottom: 15px; + left: 50%; + transform: translateX(-50%); + color: white; + text-align: center; + font-size: 16px; + background: rgba(0, 0, 0, 0.6); + padding: 5px 15px; + border-radius: 15px; + z-index: 10; + width: 80%; + max-width: 500px; + `; + + // Add tool selection + const toolsContainer = document.createElement('div'); + toolsContainer.style.cssText = ` + position: absolute; + bottom: 15px; + left: 15px; + display: flex; + gap: 10px; + z-index: 10; + flex-wrap: wrap; + max-width: 30%; + `; + + this.tools.forEach(tool => { + const toolButton = document.createElement('button'); + toolButton.className = tool.name === this.currentTool.name ? 'tool-button active' : 'tool-button'; + toolButton.textContent = tool.name; + toolButton.style.cssText = ` + background: ${tool.color}; + color: white; + border: none; + border-radius: 3px; + padding: 5px 10px; + cursor: pointer; + opacity: ${tool.name === this.currentTool.name ? '1' : '0.7'}; + `; + + toolButton.addEventListener('click', () => { + document.querySelectorAll('.tool-button').forEach(btn => { + btn.classList.remove('active'); + btn.style.opacity = '0.7'; + }); + toolButton.classList.add('active'); + toolButton.style.opacity = '1'; + this.currentTool = tool; + }); + + toolsContainer.appendChild(toolButton); + }); + + // Generate fingerprint pattern + this.fingerprintCells = this.generateFingerprint(this.currentDifficulty); + + // Create particle container for dust effects + const particleContainer = document.createElement('div'); + particleContainer.style.cssText = ` + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + z-index: 5; + overflow: hidden; + `; + + // Total prints and required prints calculations + this.totalPrints = this.fingerprintCells.size; + this.requiredPrints = Math.ceil(this.totalPrints * this.difficultySettings[this.currentDifficulty].requiredCoverage); + + // Create grid cells + for (let y = 0; y < this.gridSize; y++) { + for (let x = 0; x < this.gridSize; x++) { + const cell = document.createElement('div'); + cell.style.cssText = ` + width: 100%; + height: 100%; + background: black; + position: relative; + transition: background-color 0.1s; + cursor: pointer; + `; + cell.dataset.x = x; + cell.dataset.y = y; + cell.dataset.dustLevel = '0'; + cell.dataset.hasFingerprint = this.fingerprintCells.has(`${x},${y}`) ? 'true' : 'false'; + + gameContainer.appendChild(cell); + } + } + + // Store references to elements we'll need to update + this.gameContainer = gameContainer; + this.progressText = progressText; + this.particleContainer = particleContainer; + + // Add dragging interaction - bind these to the instance + this.mouseDownHandler = this.handleMouseDown.bind(this); + this.mouseUpHandler = this.handleMouseUp.bind(this); + this.mouseLeaveHandler = this.handleMouseLeave.bind(this); + this.mouseMoveHandler = this.handleMouseMove.bind(this); + + gameContainer.addEventListener('mousedown', this.mouseDownHandler); + document.addEventListener('mouseup', this.mouseUpHandler); + gameContainer.addEventListener('mouseleave', this.mouseLeaveHandler); + gameContainer.addEventListener('mousemove', this.mouseMoveHandler); + + // 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; + z-index: 10; + `; + closeButton.onclick = () => { + this.complete(false); + }; + + // Assemble the interface + this.container.appendChild(closeButton); + this.container.appendChild(header); + this.container.appendChild(gameContainer); + this.container.appendChild(particleContainer); + this.container.appendChild(progressText); + this.container.appendChild(toolsContainer); + + // Check initial progress + this.checkProgress(); + + // Log to console that the game is initialized + console.log("Dusting minigame initialized"); + } + + // Mouse event handlers + handleMouseDown(e) { + console.log("Mouse down"); + this.isDragging = true; + // Immediately start dusting at this position + this.handleMouseMove(e); + } + + handleMouseUp() { + console.log("Mouse up"); + this.isDragging = false; + } + + handleMouseLeave() { + console.log("Mouse leave"); + this.isDragging = false; + } + + handleMouseMove(e) { + if (!this.isDragging) return; + + console.log("Mouse move while dragging"); + + // Get the cell element under the cursor + const cell = document.elementFromPoint(e.clientX, e.clientY); + if (!cell || !cell.dataset || cell.dataset.dustLevel === undefined) return; + + // Get current cell coordinates + const centerX = parseInt(cell.dataset.x); + const centerY = parseInt(cell.dataset.y); + + // Get a list of cells to dust based on the brush radius + const cellsToDust = []; + const radius = this.currentTool.radius; + + // Add the current cell and cells within radius + for (let y = centerY - radius; y <= centerY + radius; y++) { + for (let x = centerX - radius; x <= centerX + radius; x++) { + // Skip cells outside the grid + if (x < 0 || x >= this.gridSize || y < 0 || y >= this.gridSize) continue; + + // For medium brush, use a diamond pattern (taxicab distance) + if (this.currentTool.size === 2) { + // Manhattan distance: |x1-x2| + |y1-y2| + const distance = Math.abs(x - centerX) + Math.abs(y - centerY); + if (distance > radius) continue; // Skip if too far away + } + // For wide brush, use a circle pattern (Euclidean distance) + else if (this.currentTool.size === 3) { + // Euclidean distance: √[(x1-x2)² + (y1-y2)²] + const distance = Math.sqrt(Math.pow(x - centerX, 2) + Math.pow(y - centerY, 2)); + if (distance > radius) continue; // Skip if too far away + } + + // Find this cell in the DOM + const targetCell = this.gameContainer.querySelector(`[data-x="${x}"][data-y="${y}"]`); + if (targetCell) { + cellsToDust.push(targetCell); + } + } + } + + // Get cell position for particles (center cell) + const cellRect = cell.getBoundingClientRect(); + const particleContainerRect = this.particleContainer.getBoundingClientRect(); + const cellCenterX = (cellRect.left + cellRect.width / 2) - particleContainerRect.left; + const cellCenterY = (cellRect.top + cellRect.height / 2) - particleContainerRect.top; + + // Process all cells to dust + cellsToDust.forEach(targetCell => { + const cellId = `${targetCell.dataset.x},${targetCell.dataset.y}`; + const currentTime = Date.now(); + const dustLevel = parseInt(targetCell.dataset.dustLevel); + + // Tool intensity affects dusting rate and particle effects + const toolIntensity = this.currentTool.size / 3; // 0.33 to 1 + + // Only allow dusting every 50-150ms for each cell (based on tool size) + const cooldown = 150 - (toolIntensity * 100); // 50ms for wide brush, 150ms for fine + + if (!this.lastDustTime[cellId] || currentTime - this.lastDustTime[cellId] > cooldown) { + if (dustLevel < 3) { + // Increment dust level with a probability based on tool intensity + const dustProbability = toolIntensity * 0.5 + 0.1; // 0.1-0.6 chance based on tool + + if (dustLevel < 1 || Math.random() < dustProbability) { + targetCell.dataset.dustLevel = (dustLevel + 1).toString(); + this.updateCellColor(targetCell); + + // Create dust particles for the current cell or at a position calculated for surrounding cells + if (targetCell === cell) { + // Center cell - use the already calculated position + const hasFingerprint = targetCell.dataset.hasFingerprint === 'true'; + let particleColor = dustLevel === 1 ? '#666' : (hasFingerprint ? '#1aff1a' : '#aaa'); + this.createDustParticles(cellCenterX, cellCenterY, toolIntensity, particleColor); + } else { + // For surrounding cells, calculate their relative position from the center cell + const targetCellRect = targetCell.getBoundingClientRect(); + const targetCellX = (targetCellRect.left + targetCellRect.width / 2) - particleContainerRect.left; + const targetCellY = (targetCellRect.top + targetCellRect.height / 2) - particleContainerRect.top; + + const hasFingerprint = targetCell.dataset.hasFingerprint === 'true'; + let particleColor = dustLevel === 1 ? '#666' : (hasFingerprint ? '#1aff1a' : '#aaa'); + + // Create fewer particles for surrounding cells + const reducedIntensity = toolIntensity * 0.6; + this.createDustParticles(targetCellX, targetCellY, reducedIntensity, particleColor); + } + } + this.lastDustTime[cellId] = currentTime; + } + } + }); + + // Update progress after dusting + this.checkProgress(); + } + + start() { + super.start(); + console.log("Dusting minigame started"); + // Disable game movement + if (this.params.scene) { + this.params.scene.input.mouse.enabled = false; + } + } + + cleanup() { + // Remove event listeners + this.gameContainer.removeEventListener('mousedown', this.mouseDownHandler); + document.removeEventListener('mouseup', this.mouseUpHandler); + this.gameContainer.removeEventListener('mouseleave', this.mouseLeaveHandler); + this.gameContainer.removeEventListener('mousemove', this.mouseMoveHandler); + + // Re-enable game movement + if (this.params.scene) { + this.params.scene.input.mouse.enabled = true; + } + + console.log("Dusting minigame cleaned up"); + } + + createDustParticles(x, y, intensity, color) { + const numParticles = Math.floor(5 + intensity * 5); // 5-10 particles based on intensity + + for (let i = 0; i < numParticles; i++) { + const particle = document.createElement('div'); + const size = Math.random() * 3 + 1; // 1-4px + const angle = Math.random() * Math.PI * 2; + const distance = Math.random() * 20 * intensity; + const duration = Math.random() * 1000 + 500; // 500-1500ms + + particle.style.cssText = ` + position: absolute; + width: ${size}px; + height: ${size}px; + background: ${color}; + border-radius: 50%; + opacity: ${Math.random() * 0.3 + 0.3}; + top: ${y}px; + left: ${x}px; + transform: translate(-50%, -50%); + pointer-events: none; + z-index: 6; + `; + + this.particleContainer.appendChild(particle); + + // Animate the particle + const animation = particle.animate([ + { + transform: 'translate(-50%, -50%)', + opacity: particle.style.opacity + }, + { + transform: `translate( + calc(-50% + ${Math.cos(angle) * distance}px), + calc(-50% + ${Math.sin(angle) * distance}px) + )`, + opacity: 0 + } + ], { + duration: duration, + easing: 'cubic-bezier(0.25, 1, 0.5, 1)' + }); + + animation.onfinish = () => { + particle.remove(); + }; + } + } + + updateCellColor(cell) { + const dustLevel = parseInt(cell.dataset.dustLevel); + const hasFingerprint = cell.dataset.hasFingerprint === 'true'; + + if (dustLevel === 0) { + cell.style.background = 'black'; + cell.style.boxShadow = 'none'; + } + else if (dustLevel === 1) { + cell.style.background = '#444'; + cell.style.boxShadow = 'inset 0 0 3px rgba(255,255,255,0.2)'; + } + else if (dustLevel === 2) { + if (hasFingerprint) { + cell.style.background = '#0f0'; + cell.style.boxShadow = 'inset 0 0 5px rgba(0,255,0,0.5), 0 0 5px rgba(0,255,0,0.3)'; + } else { + cell.style.background = '#888'; + cell.style.boxShadow = 'inset 0 0 4px rgba(255,255,255,0.3)'; + } + } + else { + cell.style.background = '#ccc'; + cell.style.boxShadow = 'inset 0 0 5px rgba(255,255,255,0.5)'; + } + } + + checkProgress() { + this.revealedPrints = 0; + this.overDusted = 0; + + this.gameContainer.childNodes.forEach(cell => { + if (cell.dataset) { // Check if it's a cell element + const dustLevel = parseInt(cell.dataset.dustLevel || '0'); + const hasFingerprint = cell.dataset.hasFingerprint === 'true'; + + if (hasFingerprint && dustLevel === 2) this.revealedPrints++; + if (dustLevel === 3) this.overDusted++; + } + }); + + // Update progress display + this.progressText.innerHTML = ` +
+ Found: ${this.revealedPrints}/${this.requiredPrints} required prints + + Over-dusted: ${this.overDusted}/${this.difficultySettings[this.currentDifficulty].maxOverDusted} max + +
+
+
+
+ `; + + // Check fail condition first + if (this.overDusted >= this.difficultySettings[this.currentDifficulty].maxOverDusted) { + this.showFailure("Too many over-dusted areas!"); + return; + } + + // Check win condition + if (this.revealedPrints >= this.requiredPrints) { + this.showSuccess(); + } + } + + showSuccess() { + // Calculate quality based on dusting precision + const dustPenalty = this.overDusted / this.difficultySettings[this.currentDifficulty].maxOverDusted; // 0-1 + const coverageBonus = this.revealedPrints / this.totalPrints; // 0-1 + + // Higher quality for more coverage and less over-dusting + const quality = 0.7 + (coverageBonus * 0.25) - (dustPenalty * 0.15); + + // 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; + box-shadow: 0 0 20px rgba(0, 255, 0, 0.3); + `; + + const qualityPercentage = Math.round(quality * 100); + const qualityRating = qualityPercentage >= 95 ? 'Perfect' : + qualityPercentage >= 85 ? 'Excellent' : + qualityPercentage >= 75 ? 'Good' : 'Acceptable'; + + successMessage.innerHTML = ` +
Fingerprint successfully collected!
+
Quality: ${qualityRating} (${qualityPercentage}%)
+
+ Prints revealed: ${this.revealedPrints}/${this.totalPrints}
+ Over-dusted areas: ${this.overDusted}
+ Difficulty: ${this.currentDifficulty.charAt(0).toUpperCase() + this.currentDifficulty.slice(1)} +
+ `; + + this.container.appendChild(successMessage); + + // Disable further interaction + this.isDragging = false; + this.gameContainer.style.pointerEvents = 'none'; + + setTimeout(() => { + this.complete(true, { + quality: quality, + rating: qualityRating + }); + }, 2000); + } + + showFailure(reason) { + // 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; + box-shadow: 0 0 20px rgba(255, 0, 0, 0.3); + `; + failureMessage.innerHTML = ` +
${reason}
+
Try again with more careful dusting.
+ `; + + this.container.appendChild(failureMessage); + + // Disable further interaction + this.isDragging = false; + this.gameContainer.style.pointerEvents = 'none'; + + setTimeout(() => { + this.complete(false); + }, 2000); + } + + generateFingerprint(difficulty) { + const pattern = this.difficultySettings[difficulty].pattern; + const numPrints = this.difficultySettings[difficulty].fingerprints; + const newFingerprintCells = new Set(); + const centerX = Math.floor(this.gridSize / 2); + const centerY = Math.floor(this.gridSize / 2); + + if (pattern === 'simple') { + // Simple oval-like pattern + for (let i = 0; i < numPrints; i++) { + const angle = (i / numPrints) * Math.PI * 2; + const distance = 5 + Math.random() * 3; + const x = Math.floor(centerX + Math.cos(angle) * distance); + const y = Math.floor(centerY + Math.sin(angle) * distance); + + if (x >= 0 && x < this.gridSize && y >= 0 && y < this.gridSize) { + newFingerprintCells.add(`${x},${y}`); + + // Add a few adjacent cells to make it less sparse + for (let j = 0; j < 2; j++) { + const nx = x + Math.floor(Math.random() * 3) - 1; + const ny = y + Math.floor(Math.random() * 3) - 1; + if (nx >= 0 && nx < this.gridSize && ny >= 0 && ny < this.gridSize) { + newFingerprintCells.add(`${nx},${ny}`); + } + } + } + } + } else if (pattern === 'medium') { + // Medium complexity - spiral pattern with variations + for (let i = 0; i < numPrints; i++) { + const t = i / numPrints * 5; + const distance = 2 + t * 0.8; + const noise = Math.random() * 2 - 1; + const x = Math.floor(centerX + Math.cos(t * Math.PI * 2) * (distance + noise)); + const y = Math.floor(centerY + Math.sin(t * Math.PI * 2) * (distance + noise)); + + if (x >= 0 && x < this.gridSize && y >= 0 && y < this.gridSize) { + newFingerprintCells.add(`${x},${y}`); + } + } + + // Add whorls and arches + for (let i = 0; i < 20; i++) { + const angle = (i / 20) * Math.PI * 2; + const distance = 7; + const x = Math.floor(centerX + Math.cos(angle) * distance); + const y = Math.floor(centerY + Math.sin(angle) * distance); + + if (x >= 0 && x < this.gridSize && y >= 0 && y < this.gridSize) { + newFingerprintCells.add(`${x},${y}`); + + // Add ridge-like pattern + for (let j = 1; j <= 3; j++) { + const ridgeX = Math.floor(centerX + Math.cos(angle) * (distance - j)); + const ridgeY = Math.floor(centerY + Math.sin(angle) * (distance - j)); + if (ridgeX >= 0 && ridgeX < this.gridSize && ridgeY >= 0 && ridgeY < this.gridSize) { + newFingerprintCells.add(`${ridgeX},${ridgeY}`); + } + } + } + } + } else { + // Complex pattern - detailed whorls and ridge patterns + for (let i = 0; i < numPrints; i++) { + // Main loop - create a complex whorl pattern + const t = i / numPrints * 8; + const distance = 2 + t * 0.6; + const noise = Math.sin(t * 5) * 1.5; + const x = Math.floor(centerX + Math.cos(t * Math.PI * 2) * (distance + noise)); + const y = Math.floor(centerY + Math.sin(t * Math.PI * 2) * (distance + noise)); + + if (x >= 0 && x < this.gridSize && y >= 0 && y < this.gridSize) { + newFingerprintCells.add(`${x},${y}`); + } + + // Add bifurcations and ridge endings + if (i % 5 === 0) { + const bifAngle = t * Math.PI * 2 + Math.PI/4; + const bx = Math.floor(x + Math.cos(bifAngle) * 1); + const by = Math.floor(y + Math.sin(bifAngle) * 1); + if (bx >= 0 && bx < this.gridSize && by >= 0 && by < this.gridSize) { + newFingerprintCells.add(`${bx},${by}`); + } + } + } + + // Add delta patterns + for (let d = 0; d < 3; d++) { + const deltaAngle = (d / 3) * Math.PI * 2; + const deltaX = Math.floor(centerX + Math.cos(deltaAngle) * 8); + const deltaY = Math.floor(centerY + Math.sin(deltaAngle) * 8); + + for (let r = 0; r < 5; r++) { + for (let a = 0; a < 3; a++) { + const rayAngle = deltaAngle + (a - 1) * Math.PI/4; + const rx = Math.floor(deltaX + Math.cos(rayAngle) * r); + const ry = Math.floor(deltaY + Math.sin(rayAngle) * r); + if (rx >= 0 && rx < this.gridSize && ry >= 0 && ry < this.gridSize) { + newFingerprintCells.add(`${rx},${ry}`); + } + } + } + } + } + + // Ensure we have at least the minimum number of cells + while (newFingerprintCells.size < numPrints) { + const x = centerX + Math.floor(Math.random() * 12 - 6); + const y = centerY + Math.floor(Math.random() * 12 - 6); + if (x >= 0 && x < this.gridSize && y >= 0 && y < this.gridSize) { + newFingerprintCells.add(`${x},${y}`); + } + } + + return newFingerprintCells; + } + } + + // Register the dusting minigame with the framework + MinigameFramework.registerScene('dusting', DustingMinigame); + + // Replacement for the startDustingMinigame function + function startDustingMinigame(item) { + // Initialize the framework if not already done + if (!MinigameFramework.mainGameScene) { + MinigameFramework.init(item.scene); + } + + // Start the dusting minigame + MinigameFramework.startMinigame('dusting', { + item: item, + scene: item.scene, + onComplete: (success, result) => { + if (success) { + debugLog('DUSTING SUCCESS', result, 1); + + // Add fingerprint to gameState + if (!gameState.biometricSamples) { + gameState.biometricSamples = []; + } + + const sample = { + id: generateFingerprintData(item), + type: 'fingerprint', + owner: item.scenarioData.fingerprintOwner, + quality: result.quality, // Quality between 0.7 and ~1.0 + data: generateFingerprintData(item), + timestamp: Date.now() + }; + + gameState.biometricSamples.push(sample); + + // Mark item as collected + if (item.scenarioData) { + item.scenarioData.hasFingerprint = false; + } + + // Update the biometrics panel and count + updateBiometricsPanel(); + updateBiometricsCount(); + + // Show notification + gameAlert(`Collected ${sample.owner}'s fingerprint sample (${result.rating} quality)`, 'success', 'Sample Acquired', 3000); + } else { + debugLog('DUSTING FAILED', null, 2); + gameAlert(`Failed to collect the fingerprint sample.`, 'error', 'Dusting Failed', 3000); + } + } + }); + } + + // Bluetooth scanner system +// ... existing code ... \ No newline at end of file