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
|
||||
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 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();
|
||||
|
||||
// Get the key group and scale it down
|
||||
@@ -295,8 +300,9 @@ export class KeyOperations {
|
||||
keyContainer.add(keyGroup);
|
||||
}
|
||||
|
||||
// Restore the original key data
|
||||
// Restore the original key data and pin count
|
||||
this.parent.keyData = originalKeyData;
|
||||
this.parent.pinCount = originalPinCount;
|
||||
|
||||
return keyContainer;
|
||||
}
|
||||
|
||||
@@ -44,8 +44,8 @@ export class KeySelection {
|
||||
// Generate a random key with the specified number of pins
|
||||
const cuts = [];
|
||||
for (let i = 0; i < pinCount; i++) {
|
||||
// Generate random cut depth between 20-80 (avoiding extremes)
|
||||
cuts.push(Math.floor(Math.random() * 60) + 20);
|
||||
// Generate random cut depth between 25-65 (middle range for realistic key variation)
|
||||
cuts.push(Math.floor(Math.random() * 40) + 25);
|
||||
}
|
||||
return {
|
||||
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
|
||||
// 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)
|
||||
// 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;
|
||||
if (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
|
||||
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
|
||||
const keySelectionContainer = this.scene.add.container(0, 230);
|
||||
keySelectionContainer.setDepth(1000); // High z-index to appear above everything
|
||||
@@ -537,13 +555,14 @@ export class LockpickingMinigamePhaser extends MinigameScene {
|
||||
// Add background
|
||||
const background = this.scene.add.graphics();
|
||||
background.fillStyle(0x000000, 0.8);
|
||||
background.fillRect(0, 0, 700, 180);
|
||||
background.fillRect(0, 0, containerWidth, containerHeight);
|
||||
background.lineStyle(2, 0xffffff);
|
||||
background.strokeRect(0, 0, 600, 170);
|
||||
background.strokeRect(0, 0, containerWidth - 1, containerHeight - 1);
|
||||
keySelectionContainer.add(background);
|
||||
|
||||
// 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',
|
||||
fill: '#ffffff',
|
||||
fontFamily: 'VT323',
|
||||
@@ -551,41 +570,171 @@ export class LockpickingMinigamePhaser extends MinigameScene {
|
||||
title.setOrigin(0.5, 0);
|
||||
keySelectionContainer.add(title);
|
||||
|
||||
// Create key options
|
||||
const keyWidth = 140;
|
||||
const keyHeight = 80;
|
||||
const spacing = 20;
|
||||
const startX = 50;
|
||||
const startY = 50;
|
||||
// Track current page
|
||||
let currentPage = 0;
|
||||
const totalPages = Math.ceil(keys.length / keysPerPage);
|
||||
|
||||
keys.forEach((keyData, index) => {
|
||||
const keyX = startX + index * (keyWidth + spacing);
|
||||
const keyY = startY;
|
||||
// Create navigation buttons if more than 3 keys
|
||||
let prevButton = null;
|
||||
let nextButton = null;
|
||||
let prevText = null;
|
||||
let nextText = null;
|
||||
let pageIndicator = null;
|
||||
let itemsToRemoveNext = null; // Track items for cleanup
|
||||
|
||||
// Create a function to render the current page of keys
|
||||
const renderKeyPage = () => {
|
||||
// Remove any existing key visuals and labels from the previous page
|
||||
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());
|
||||
|
||||
// Create key visual representation
|
||||
const keyVisual = this.keyOps.createKeyVisual(keyData, keyWidth, keyHeight);
|
||||
keyVisual.setPosition(keyX, keyY);
|
||||
keySelectionContainer.add(keyVisual);
|
||||
// Calculate which keys to show on this page
|
||||
const startIndex = currentPage * keysPerPage;
|
||||
const endIndex = Math.min(startIndex + keysPerPage, keys.length);
|
||||
const pageKeys = keys.slice(startIndex, endIndex);
|
||||
|
||||
// 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(index, correctKeyIndex, keyData);
|
||||
// Display keys for this page
|
||||
// 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)
|
||||
const keyName = keyData.name || `Key ${index + 1}`;
|
||||
const keyLabel = this.scene.add.text(keyX + keyWidth/2, keyY + keyHeight + 5, keyName, {
|
||||
fontSize: '16px',
|
||||
// Update page indicator
|
||||
if (pageIndicator) {
|
||||
pageIndicator.setText(`${currentPage + 1}/${totalPages}`);
|
||||
}
|
||||
|
||||
// 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',
|
||||
fontFamily: 'VT323'
|
||||
});
|
||||
keyLabel.setOrigin(0.5, 0);
|
||||
keySelectionContainer.add(keyLabel);
|
||||
});
|
||||
prevText.setOrigin(0.5, 0.5);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -254,6 +254,9 @@
|
||||
<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="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>
|
||||
</div>
|
||||
|
||||
@@ -293,28 +296,43 @@
|
||||
this.keyPresets = {
|
||||
key1: {
|
||||
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"
|
||||
},
|
||||
key2: {
|
||||
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"
|
||||
},
|
||||
key3: {
|
||||
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"
|
||||
},
|
||||
key4: {
|
||||
name: "Key 4 (Random)",
|
||||
cuts: [15, 45, 80, 35, 55],
|
||||
cuts: [28, 48, 62, 38, 52],
|
||||
description: "Random cut pattern - might work by chance"
|
||||
},
|
||||
key5: {
|
||||
name: "Key 5 (Deep)",
|
||||
cuts: [80, 90, 95, 85, 88],
|
||||
cuts: [60, 63, 65, 62, 61],
|
||||
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
|
||||
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
|
||||
document.getElementById('feedback').textContent =
|
||||
@@ -484,7 +502,16 @@
|
||||
// If in key mode, automatically show key selection
|
||||
if (this.currentMode === 'key') {
|
||||
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
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user