mirror of
https://github.com/cliffe/BreakEscape.git
synced 2026-02-20 13:50:46 +00:00
feat: Enhance key selection with additional key presets and improve random key generation logic
This commit is contained in:
@@ -256,10 +256,15 @@ export class KeyOperations {
|
|||||||
// Create a visual representation of a key for the selection UI by building the actual key and scaling it down
|
// Create a visual representation of a key for the selection UI by building the actual key and scaling it down
|
||||||
const keyContainer = this.parent.scene.add.container(0, 0);
|
const keyContainer = this.parent.scene.add.container(0, 0);
|
||||||
|
|
||||||
// Save the original key data before temporarily changing it
|
// Save the original key data and pin count before temporarily changing them
|
||||||
const originalKeyData = this.parent.keyData;
|
const originalKeyData = this.parent.keyData;
|
||||||
|
const originalPinCount = this.parent.pinCount;
|
||||||
|
|
||||||
// Temporarily set the key data to create the key
|
// Temporarily set the key data and pin count to create this specific key
|
||||||
|
this.parent.keyData = keyData;
|
||||||
|
this.parent.pinCount = keyData.pinCount || 5;
|
||||||
|
|
||||||
|
// Create the key with this specific key data
|
||||||
this.createKey();
|
this.createKey();
|
||||||
|
|
||||||
// Get the key group and scale it down
|
// Get the key group and scale it down
|
||||||
@@ -295,8 +300,9 @@ export class KeyOperations {
|
|||||||
keyContainer.add(keyGroup);
|
keyContainer.add(keyGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore the original key data
|
// Restore the original key data and pin count
|
||||||
this.parent.keyData = originalKeyData;
|
this.parent.keyData = originalKeyData;
|
||||||
|
this.parent.pinCount = originalPinCount;
|
||||||
|
|
||||||
return keyContainer;
|
return keyContainer;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ export class KeySelection {
|
|||||||
// Generate a random key with the specified number of pins
|
// Generate a random key with the specified number of pins
|
||||||
const cuts = [];
|
const cuts = [];
|
||||||
for (let i = 0; i < pinCount; i++) {
|
for (let i = 0; i < pinCount; i++) {
|
||||||
// Generate random cut depth between 20-80 (avoiding extremes)
|
// Generate random cut depth between 25-65 (middle range for realistic key variation)
|
||||||
cuts.push(Math.floor(Math.random() * 60) + 20);
|
cuts.push(Math.floor(Math.random() * 40) + 25);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
id: `random_key_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
id: `random_key_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
||||||
|
|||||||
@@ -505,8 +505,9 @@ export class LockpickingMinigamePhaser extends MinigameScene {
|
|||||||
// Create a UI for selecting between multiple keys
|
// Create a UI for selecting between multiple keys
|
||||||
// keys: array of key objects with id, cuts, and optional name properties
|
// keys: array of key objects with id, cuts, and optional name properties
|
||||||
// correctKeyId: ID of the correct key (if null, uses index 0 as fallback)
|
// correctKeyId: ID of the correct key (if null, uses index 0 as fallback)
|
||||||
|
// Shows 3 keys at a time with navigation buttons for more than 3 keys
|
||||||
|
|
||||||
// Find the correct key index
|
// Find the correct key index in the original array
|
||||||
let correctKeyIndex = 0;
|
let correctKeyIndex = 0;
|
||||||
if (correctKeyId) {
|
if (correctKeyId) {
|
||||||
correctKeyIndex = keys.findIndex(key => key.id === correctKeyId);
|
correctKeyIndex = keys.findIndex(key => key.id === correctKeyId);
|
||||||
@@ -530,6 +531,23 @@ export class LockpickingMinigamePhaser extends MinigameScene {
|
|||||||
// Reset pins to their original positions before showing key selection
|
// Reset pins to their original positions before showing key selection
|
||||||
this.lockConfig.resetPinsToOriginalPositions();
|
this.lockConfig.resetPinsToOriginalPositions();
|
||||||
|
|
||||||
|
// Layout constants
|
||||||
|
const keyWidth = 140;
|
||||||
|
const keyHeight = 80;
|
||||||
|
const spacing = 20;
|
||||||
|
const padding = 20;
|
||||||
|
const labelHeight = 30; // Space for key label below each key
|
||||||
|
const keysPerPage = 3; // Always show 3 keys at a time
|
||||||
|
const buttonWidth = 30;
|
||||||
|
const buttonHeight = 30;
|
||||||
|
const buttonSpacing = 10; // Space between button and keys
|
||||||
|
|
||||||
|
// Calculate container dimensions (always 3 keys wide + buttons on sides with minimal spacing)
|
||||||
|
// For 3 keys: [button] padding [key1] spacing [key2] spacing [key3] padding [button]
|
||||||
|
const keysWidth = (keysPerPage - 1) * (keyWidth + spacing) + keyWidth; // 3 keys with spacing between them
|
||||||
|
const containerWidth = keysWidth + (keys.length > keysPerPage ? buttonWidth * 2 + buttonSpacing * 2 + padding * 2 : padding * 2);
|
||||||
|
const containerHeight = keyHeight + labelHeight + spacing + padding * 2 + 50; // +50 for title
|
||||||
|
|
||||||
// Create container for key selection - positioned in the middle but below pins
|
// Create container for key selection - positioned in the middle but below pins
|
||||||
const keySelectionContainer = this.scene.add.container(0, 230);
|
const keySelectionContainer = this.scene.add.container(0, 230);
|
||||||
keySelectionContainer.setDepth(1000); // High z-index to appear above everything
|
keySelectionContainer.setDepth(1000); // High z-index to appear above everything
|
||||||
@@ -537,13 +555,14 @@ export class LockpickingMinigamePhaser extends MinigameScene {
|
|||||||
// Add background
|
// Add background
|
||||||
const background = this.scene.add.graphics();
|
const background = this.scene.add.graphics();
|
||||||
background.fillStyle(0x000000, 0.8);
|
background.fillStyle(0x000000, 0.8);
|
||||||
background.fillRect(0, 0, 700, 180);
|
background.fillRect(0, 0, containerWidth, containerHeight);
|
||||||
background.lineStyle(2, 0xffffff);
|
background.lineStyle(2, 0xffffff);
|
||||||
background.strokeRect(0, 0, 600, 170);
|
background.strokeRect(0, 0, containerWidth - 1, containerHeight - 1);
|
||||||
keySelectionContainer.add(background);
|
keySelectionContainer.add(background);
|
||||||
|
|
||||||
// Add title
|
// Add title
|
||||||
const title = this.scene.add.text(300, 15, 'Select the correct key', {
|
const titleX = containerWidth / 2;
|
||||||
|
const title = this.scene.add.text(titleX, 15, 'Select the correct key', {
|
||||||
fontSize: '24px',
|
fontSize: '24px',
|
||||||
fill: '#ffffff',
|
fill: '#ffffff',
|
||||||
fontFamily: 'VT323',
|
fontFamily: 'VT323',
|
||||||
@@ -551,41 +570,171 @@ export class LockpickingMinigamePhaser extends MinigameScene {
|
|||||||
title.setOrigin(0.5, 0);
|
title.setOrigin(0.5, 0);
|
||||||
keySelectionContainer.add(title);
|
keySelectionContainer.add(title);
|
||||||
|
|
||||||
// Create key options
|
// Track current page
|
||||||
const keyWidth = 140;
|
let currentPage = 0;
|
||||||
const keyHeight = 80;
|
const totalPages = Math.ceil(keys.length / keysPerPage);
|
||||||
const spacing = 20;
|
|
||||||
const startX = 50;
|
|
||||||
const startY = 50;
|
|
||||||
|
|
||||||
keys.forEach((keyData, index) => {
|
// Create navigation buttons if more than 3 keys
|
||||||
const keyX = startX + index * (keyWidth + spacing);
|
let prevButton = null;
|
||||||
const keyY = startY;
|
let nextButton = null;
|
||||||
|
let prevText = null;
|
||||||
|
let nextText = null;
|
||||||
|
let pageIndicator = null;
|
||||||
|
let itemsToRemoveNext = null; // Track items for cleanup
|
||||||
|
|
||||||
// Create key visual representation
|
// Create a function to render the current page of keys
|
||||||
const keyVisual = this.keyOps.createKeyVisual(keyData, keyWidth, keyHeight);
|
const renderKeyPage = () => {
|
||||||
keyVisual.setPosition(keyX, keyY);
|
// Remove any existing key visuals and labels from the previous page
|
||||||
keySelectionContainer.add(keyVisual);
|
const itemsToRemove = [];
|
||||||
|
keySelectionContainer.list.forEach(item => {
|
||||||
|
if (item !== background && item !== title && item !== prevButton && item !== nextButton && item !== pageIndicator && item !== prevText && item !== nextText) {
|
||||||
|
itemsToRemove.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
itemsToRemove.forEach(item => item.destroy());
|
||||||
|
|
||||||
// Make key clickable
|
// Calculate which keys to show on this page
|
||||||
keyVisual.setInteractive(new Phaser.Geom.Rectangle(0, 0, keyWidth, keyHeight), Phaser.Geom.Rectangle.Contains);
|
const startIndex = currentPage * keysPerPage;
|
||||||
keyVisual.on('pointerdown', () => {
|
const endIndex = Math.min(startIndex + keysPerPage, keys.length);
|
||||||
// Close the popup
|
const pageKeys = keys.slice(startIndex, endIndex);
|
||||||
keySelectionContainer.destroy();
|
|
||||||
// Trigger key selection and insertion
|
// Display keys for this page
|
||||||
this.keyOps.selectKey(index, correctKeyIndex, keyData);
|
// Position: [button] buttonSpacing [keys] buttonSpacing [button]
|
||||||
|
const keysStartX = (keys.length > keysPerPage ? buttonWidth + buttonSpacing : padding);
|
||||||
|
const startX = keysStartX + padding / 2;
|
||||||
|
const startY = 50;
|
||||||
|
|
||||||
|
pageKeys.forEach((keyData, pageIndex) => {
|
||||||
|
const actualIndex = startIndex + pageIndex;
|
||||||
|
const keyX = startX + pageIndex * (keyWidth + spacing);
|
||||||
|
const keyY = startY;
|
||||||
|
|
||||||
|
// Create key visual representation
|
||||||
|
const keyVisual = this.keyOps.createKeyVisual(keyData, keyWidth, keyHeight);
|
||||||
|
keyVisual.setPosition(keyX, keyY);
|
||||||
|
keySelectionContainer.add(keyVisual);
|
||||||
|
|
||||||
|
// Make key clickable
|
||||||
|
keyVisual.setInteractive(new Phaser.Geom.Rectangle(0, 0, keyWidth, keyHeight), Phaser.Geom.Rectangle.Contains);
|
||||||
|
keyVisual.on('pointerdown', () => {
|
||||||
|
// Close the popup
|
||||||
|
keySelectionContainer.destroy();
|
||||||
|
// Trigger key selection and insertion
|
||||||
|
this.keyOps.selectKey(actualIndex, correctKeyIndex, keyData);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add key label (use name if available, otherwise use number)
|
||||||
|
const keyName = keyData.name || `Key ${actualIndex + 1}`;
|
||||||
|
const keyLabel = this.scene.add.text(keyX + keyWidth/2, keyY + keyHeight + 5, keyName, {
|
||||||
|
fontSize: '16px',
|
||||||
|
fill: '#ffffff',
|
||||||
|
fontFamily: 'VT323'
|
||||||
|
});
|
||||||
|
keyLabel.setOrigin(0.5, 0);
|
||||||
|
keySelectionContainer.add(keyLabel);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add key label (use name if available, otherwise use number)
|
// Update page indicator
|
||||||
const keyName = keyData.name || `Key ${index + 1}`;
|
if (pageIndicator) {
|
||||||
const keyLabel = this.scene.add.text(keyX + keyWidth/2, keyY + keyHeight + 5, keyName, {
|
pageIndicator.setText(`${currentPage + 1}/${totalPages}`);
|
||||||
fontSize: '16px',
|
}
|
||||||
|
|
||||||
|
// Update button visibility
|
||||||
|
if (prevButton) {
|
||||||
|
if (currentPage > 0) {
|
||||||
|
prevButton.setVisible(true);
|
||||||
|
prevText.setVisible(true);
|
||||||
|
} else {
|
||||||
|
prevButton.setVisible(false);
|
||||||
|
prevText.setVisible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextButton) {
|
||||||
|
if (currentPage < totalPages - 1) {
|
||||||
|
nextButton.setVisible(true);
|
||||||
|
nextText.setVisible(true);
|
||||||
|
} else {
|
||||||
|
nextButton.setVisible(false);
|
||||||
|
nextText.setVisible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (keys.length > keysPerPage) {
|
||||||
|
// Position buttons on the sides of the keys, vertically centered
|
||||||
|
const keysAreaCenterY = 50 + (keyHeight + labelHeight) / 2;
|
||||||
|
|
||||||
|
// Previous button (left side)
|
||||||
|
prevButton = this.scene.add.graphics();
|
||||||
|
prevButton.fillStyle(0x444444);
|
||||||
|
prevButton.fillRect(0, 0, buttonWidth, buttonHeight);
|
||||||
|
prevButton.lineStyle(2, 0xffffff);
|
||||||
|
prevButton.strokeRect(0, 0, buttonWidth, buttonHeight);
|
||||||
|
prevButton.setInteractive(new Phaser.Geom.Rectangle(0, 0, buttonWidth, buttonHeight), Phaser.Geom.Rectangle.Contains);
|
||||||
|
prevButton.on('pointerdown', () => {
|
||||||
|
if (currentPage > 0) {
|
||||||
|
currentPage--;
|
||||||
|
renderKeyPage();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
prevButton.setPosition(padding / 2, keysAreaCenterY - buttonHeight / 2);
|
||||||
|
prevButton.setDepth(1001);
|
||||||
|
prevButton.setVisible(false); // Initially hidden
|
||||||
|
keySelectionContainer.add(prevButton);
|
||||||
|
|
||||||
|
// Previous button text
|
||||||
|
prevText = this.scene.add.text(padding / 2 + buttonWidth / 2, keysAreaCenterY, '‹', {
|
||||||
|
fontSize: '20px',
|
||||||
fill: '#ffffff',
|
fill: '#ffffff',
|
||||||
fontFamily: 'VT323'
|
fontFamily: 'VT323'
|
||||||
});
|
});
|
||||||
keyLabel.setOrigin(0.5, 0);
|
prevText.setOrigin(0.5, 0.5);
|
||||||
keySelectionContainer.add(keyLabel);
|
prevText.setDepth(1002);
|
||||||
});
|
prevText.setVisible(false); // Initially hidden
|
||||||
|
keySelectionContainer.add(prevText);
|
||||||
|
|
||||||
|
// Next button (right side)
|
||||||
|
nextButton = this.scene.add.graphics();
|
||||||
|
nextButton.fillStyle(0x444444);
|
||||||
|
nextButton.fillRect(0, 0, buttonWidth, buttonHeight);
|
||||||
|
nextButton.lineStyle(2, 0xffffff);
|
||||||
|
nextButton.strokeRect(0, 0, buttonWidth, buttonHeight);
|
||||||
|
nextButton.setInteractive(new Phaser.Geom.Rectangle(0, 0, buttonWidth, buttonHeight), Phaser.Geom.Rectangle.Contains);
|
||||||
|
nextButton.on('pointerdown', () => {
|
||||||
|
if (currentPage < totalPages - 1) {
|
||||||
|
currentPage++;
|
||||||
|
renderKeyPage();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
nextButton.setPosition(containerWidth - padding / 2 - buttonWidth, keysAreaCenterY - buttonHeight / 2);
|
||||||
|
nextButton.setDepth(1001);
|
||||||
|
nextButton.setVisible(false); // Initially hidden
|
||||||
|
keySelectionContainer.add(nextButton);
|
||||||
|
|
||||||
|
// Next button text
|
||||||
|
nextText = this.scene.add.text(containerWidth - padding / 2 - buttonWidth / 2, keysAreaCenterY, '›', {
|
||||||
|
fontSize: '20px',
|
||||||
|
fill: '#ffffff',
|
||||||
|
fontFamily: 'VT323'
|
||||||
|
});
|
||||||
|
nextText.setOrigin(0.5, 0.5);
|
||||||
|
nextText.setDepth(1002);
|
||||||
|
nextText.setVisible(false); // Initially hidden
|
||||||
|
keySelectionContainer.add(nextText);
|
||||||
|
|
||||||
|
// Page indicator - centered below all keys
|
||||||
|
pageIndicator = this.scene.add.text(containerWidth / 2, containerHeight - 20, `1/${totalPages}`, {
|
||||||
|
fontSize: '12px',
|
||||||
|
fill: '#888888',
|
||||||
|
fontFamily: 'VT323'
|
||||||
|
});
|
||||||
|
pageIndicator.setOrigin(0.5, 0.5);
|
||||||
|
keySelectionContainer.add(pageIndicator);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render the first page
|
||||||
|
renderKeyPage();
|
||||||
|
|
||||||
this.keySelectionContainer = keySelectionContainer;
|
this.keySelectionContainer = keySelectionContainer;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -254,6 +254,9 @@
|
|||||||
<div class="key-preset" data-key="key3">Key 3 (Partial)</div>
|
<div class="key-preset" data-key="key3">Key 3 (Partial)</div>
|
||||||
<div class="key-preset" data-key="key4">Key 4 (Random)</div>
|
<div class="key-preset" data-key="key4">Key 4 (Random)</div>
|
||||||
<div class="key-preset" data-key="key5">Key 5 (Deep)</div>
|
<div class="key-preset" data-key="key5">Key 5 (Deep)</div>
|
||||||
|
<div class="key-preset" data-key="key6">Key 6 (Shallow)</div>
|
||||||
|
<div class="key-preset" data-key="key7">Key 7 (Mixed)</div>
|
||||||
|
<div class="key-preset" data-key="key8">Key 8 (Reverse)</div>
|
||||||
<button class="key-preset" id="keySelectionToggle">🎲 Random Key Selection</button>
|
<button class="key-preset" id="keySelectionToggle">🎲 Random Key Selection</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -293,28 +296,43 @@
|
|||||||
this.keyPresets = {
|
this.keyPresets = {
|
||||||
key1: {
|
key1: {
|
||||||
name: "Key 1 (Correct)",
|
name: "Key 1 (Correct)",
|
||||||
cuts: [25, 50, 75, 30, 60], // This will be overridden by auto-generation
|
cuts: [35, 50, 65, 40, 55], // This will be overridden by auto-generation
|
||||||
description: "This key is auto-generated to match the actual pin heights"
|
description: "This key is auto-generated to match the actual pin heights"
|
||||||
},
|
},
|
||||||
key2: {
|
key2: {
|
||||||
name: "Key 2 (Wrong)",
|
name: "Key 2 (Wrong)",
|
||||||
cuts: [10, 20, 30, 40, 50],
|
cuts: [25, 30, 35, 40, 45],
|
||||||
description: "This key has cuts that don't match the pins"
|
description: "This key has cuts that don't match the pins"
|
||||||
},
|
},
|
||||||
key3: {
|
key3: {
|
||||||
name: "Key 3 (Partial)",
|
name: "Key 3 (Partial)",
|
||||||
cuts: [25, 50, 75, 40, 60],
|
cuts: [35, 50, 65, 45, 55],
|
||||||
description: "This key has some correct cuts but not all"
|
description: "This key has some correct cuts but not all"
|
||||||
},
|
},
|
||||||
key4: {
|
key4: {
|
||||||
name: "Key 4 (Random)",
|
name: "Key 4 (Random)",
|
||||||
cuts: [15, 45, 80, 35, 55],
|
cuts: [28, 48, 62, 38, 52],
|
||||||
description: "Random cut pattern - might work by chance"
|
description: "Random cut pattern - might work by chance"
|
||||||
},
|
},
|
||||||
key5: {
|
key5: {
|
||||||
name: "Key 5 (Deep)",
|
name: "Key 5 (Deep)",
|
||||||
cuts: [80, 90, 95, 85, 88],
|
cuts: [60, 63, 65, 62, 61],
|
||||||
description: "Very deep cuts - probably too deep"
|
description: "Very deep cuts - probably too deep"
|
||||||
|
},
|
||||||
|
key6: {
|
||||||
|
name: "Key 6 (Shallow)",
|
||||||
|
cuts: [25, 26, 27, 28, 29],
|
||||||
|
description: "Very shallow cuts - not deep enough"
|
||||||
|
},
|
||||||
|
key7: {
|
||||||
|
name: "Key 7 (Mixed)",
|
||||||
|
cuts: [40, 52, 60, 35, 58],
|
||||||
|
description: "Similar cuts but slightly off in each position"
|
||||||
|
},
|
||||||
|
key8: {
|
||||||
|
name: "Key 8 (Reverse)",
|
||||||
|
cuts: [55, 40, 65, 50, 35],
|
||||||
|
description: "Cuts in reverse order - interesting pattern"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -388,7 +406,7 @@
|
|||||||
|
|
||||||
// Generate new random keys for selection
|
// Generate new random keys for selection
|
||||||
this.currentGame.keySelectionMode = true; // Mark that we're in key selection mode
|
this.currentGame.keySelectionMode = true; // Mark that we're in key selection mode
|
||||||
this.currentGame.createKeysForChallenge('correct_key');
|
this.currentGame.keySelection.createKeysForChallenge('correct_key');
|
||||||
|
|
||||||
// Update feedback
|
// Update feedback
|
||||||
document.getElementById('feedback').textContent =
|
document.getElementById('feedback').textContent =
|
||||||
@@ -484,7 +502,16 @@
|
|||||||
// If in key mode, automatically show key selection
|
// If in key mode, automatically show key selection
|
||||||
if (this.currentMode === 'key') {
|
if (this.currentMode === 'key') {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.currentGame.startWithKeySelection();
|
// Prepare all preset keys for selection
|
||||||
|
const allKeys = Object.values(this.keyPresets).map((preset, index) => ({
|
||||||
|
id: `key_${index}`,
|
||||||
|
cuts: preset.cuts,
|
||||||
|
name: preset.name,
|
||||||
|
pinCount: preset.cuts.length
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Start with key selection showing all 8 keys
|
||||||
|
this.currentGame.startWithKeySelection(allKeys, 'key_0');
|
||||||
}, 500); // Small delay to ensure game is fully initialized
|
}, 500); // Small delay to ensure game is fully initialized
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user