diff --git a/js/minigames/lockpicking/lockpicking-game-phaser.js b/js/minigames/lockpicking/lockpicking-game-phaser.js
index d764191..30b95a9 100644
--- a/js/minigames/lockpicking/lockpicking-game-phaser.js
+++ b/js/minigames/lockpicking/lockpicking-game-phaser.js
@@ -130,7 +130,7 @@ export class LockpickingMinigamePhaser extends MinigameScene {
// Create a container for the Phaser game
this.gameContainer.innerHTML = `
- Ready to pick
+
`;
this.feedback = this.gameContainer.querySelector('.lockpick-feedback');
@@ -256,13 +256,14 @@ export class LockpickingMinigamePhaser extends MinigameScene {
this.tensionWrench.add(this.wrenchGraphics);
- // Make it interactive - larger hit area to include horizontal arm
- // Covers vertical arm, horizontal arm, and handle
- this.tensionWrench.setInteractive(new Phaser.Geom.Rectangle(-12.5, -138.75, 60, 176.25), Phaser.Geom.Rectangle.Contains);
+ // Make it interactive - extended hit area to match pin click zones (down to keyway bottom)
+ // Covers vertical arm, horizontal arm, handle, and extends down to bottom of keyway
+ this.tensionWrench.setInteractive(new Phaser.Geom.Rectangle(-12.5, -138.75, 60, 268.75), Phaser.Geom.Rectangle.Contains);
// Add text
const wrenchText = this.scene.add.text(-10, 58, 'Tension Wrench', {
- fontSize: '14px',
+ fontSize: '18px',
+ fontFamily: 'VT323',
fill: '#00ff00',
fontWeight: 'bold'
});
@@ -280,7 +281,7 @@ export class LockpickingMinigamePhaser extends MinigameScene {
// Play tension sound
if (this.sounds.tension) {
this.sounds.tension.play();
- navigator.vibrate([200]);
+ navigator.vibrate([50]);
}
if (this.lockState.tensionApplied) {
@@ -473,7 +474,8 @@ export class LockpickingMinigamePhaser extends MinigameScene {
// Add hook pick label
const hookPickLabel = this.scene.add.text(-10, 85, 'Hook Pick', {
- fontSize: '14px',
+ fontSize: '18px',
+ fontFamily: 'VT323',
fill: '#00ff00',
fontWeight: 'bold'
});
@@ -885,7 +887,8 @@ export class LockpickingMinigamePhaser extends MinigameScene {
if (i === 0) {
// Spring label
const springLabel = this.scene.add.text(pinX, pinY - 140, 'Spring', {
- fontSize: '14px',
+ fontSize: '18px',
+ fontFamily: 'VT323',
fill: '#00ff00',
fontWeight: 'bold'
});
@@ -893,8 +896,10 @@ export class LockpickingMinigamePhaser extends MinigameScene {
springLabel.setDepth(100); // Bring to front
// Driver pin label - positioned below the shear line
- const driverPinLabel = this.scene.add.text(pinX, pinY - 35, 'Driver Pin', {
- fontSize: '14px',
+ const driverPinX = 100 + margin + 1 * pinSpacing; // Pin index 1 (2nd pin)
+ const driverPinLabel = this.scene.add.text(driverPinX, pinY - 35, 'Driver Pin', {
+ fontSize: '18px',
+ fontFamily: 'VT323',
fill: '#00ff00',
fontWeight: 'bold'
});
@@ -902,8 +907,10 @@ export class LockpickingMinigamePhaser extends MinigameScene {
driverPinLabel.setDepth(100); // Bring to front
// Key pin label - positioned at the middle of the key pin
- const keyPinLabel = this.scene.add.text(pinX, pinY - 50 + driverPinLength + (keyPinLength / 2), 'Key Pin', {
- fontSize: '14px',
+ const keyPinX = 100 + margin + 2 * pinSpacing; // Pin index 2 (3rd pin)
+ const keyPinLabel = this.scene.add.text(keyPinX, pinY - 50 + driverPinLength + (keyPinLength / 2), 'Key Pin', {
+ fontSize: '18px',
+ fontFamily: 'VT323',
fill: '#00ff00',
fontWeight: 'bold'
});
@@ -953,7 +960,8 @@ export class LockpickingMinigamePhaser extends MinigameScene {
// Add pin number
const pinText = this.scene.add.text(0, 40, (i + 1).toString(), {
- fontSize: '14px',
+ fontSize: '18px',
+ fontFamily: 'VT323',
fill: '#ffffff',
fontWeight: 'bold'
});
@@ -983,7 +991,7 @@ export class LockpickingMinigamePhaser extends MinigameScene {
// Play click sound
if (this.sounds.click) {
this.sounds.click.play();
- navigator.vibrate(200);
+ navigator.vibrate(50);
}
// Hide labels on first pin click
@@ -1045,7 +1053,8 @@ export class LockpickingMinigamePhaser extends MinigameScene {
// Add shear line label
const shearLineText = this.scene.add.text(503, 145, 'SHEAR LINE', {
- fontSize: '14px',
+ fontSize: '12px',
+ fontFamily: 'VT323',
fill: '#00ff00',
fontWeight: 'bold'
});
@@ -1075,6 +1084,187 @@ export class LockpickingMinigamePhaser extends MinigameScene {
this.returnHookToStart();
}
});
+
+ // Add keyboard bindings
+ this.scene.input.keyboard.on('keydown', (event) => {
+ const key = event.key;
+
+ // Pin number keys (1-8)
+ if (key >= '1' && key <= '8') {
+ const pinIndex = parseInt(key) - 1; // Convert 1-8 to 0-7
+
+ // Check if pin exists
+ if (pinIndex < this.pinCount) {
+ const pin = this.pins[pinIndex];
+ if (pin) {
+ // Simulate pin click
+ this.lockState.currentPin = pin;
+ this.gameState.mouseDown = true;
+
+ // Play click sound
+ if (this.sounds.click) {
+ this.sounds.click.play();
+ navigator.vibrate(50);
+ }
+
+ // Hide labels on first pin click
+ if (!this.pinClicked) {
+ this.pinClicked = true;
+ if (this.wrenchText) {
+ this.wrenchText.setVisible(false);
+ }
+ if (this.shearLineText) {
+ this.shearLineText.setVisible(false);
+ }
+ if (this.hookPickLabel) {
+ this.hookPickLabel.setVisible(false);
+ }
+ if (this.springLabel) {
+ this.springLabel.setVisible(false);
+ }
+ if (this.driverPinLabel) {
+ this.driverPinLabel.setVisible(false);
+ }
+ if (this.keyPinLabel) {
+ this.keyPinLabel.setVisible(false);
+ }
+
+ // Hide all pin numbers
+ this.pins.forEach(pin => {
+ if (pin.pinText) {
+ pin.pinText.setVisible(false);
+ }
+ });
+ }
+
+ if (!this.lockState.tensionApplied) {
+ this.updateFeedback("Apply tension first before picking pins");
+ }
+ }
+ }
+ }
+
+ // SPACE key for tension wrench toggle
+ if (key === ' ') {
+ event.preventDefault(); // Prevent page scroll
+
+ // Simulate tension wrench click
+ this.lockState.tensionApplied = !this.lockState.tensionApplied;
+
+ // Play tension sound
+ if (this.sounds.tension) {
+ this.sounds.tension.play();
+ navigator.vibrate([200]);
+ }
+
+ if (this.lockState.tensionApplied) {
+ this.wrenchGraphics.clear();
+ this.wrenchGraphics.fillStyle(0x00ff00);
+
+ // Long vertical arm (left side of L) - same dimensions as inactive
+ this.wrenchGraphics.fillRect(0, -120, 10, 170);
+
+ // Short horizontal arm (bottom of L) extending into keyway - same dimensions as inactive
+ this.wrenchGraphics.fillRect(0, 40, 37.5, 10);
+
+ this.updateFeedback("Tension applied. Only the binding pin can be set - others will fall back down.");
+ } else {
+ this.wrenchGraphics.clear();
+ this.wrenchGraphics.fillStyle(0x888888);
+
+ // Long vertical arm (left side of L) - same dimensions as active
+ this.wrenchGraphics.fillRect(0, -120, 10, 170);
+
+ // Short horizontal arm (bottom of L) extending into keyway - same dimensions as active
+ this.wrenchGraphics.fillRect(0, 40, 37.5, 10);
+
+ this.updateFeedback("Tension released. All pins will fall back down.");
+
+ // Play reset sound
+ if (this.sounds.reset) {
+ this.sounds.reset.play();
+ }
+
+ // Reset ALL pins when tension is released (including set and overpicked ones)
+ this.pins.forEach(pin => {
+ pin.isSet = false;
+ pin.isOverpicked = false;
+ pin.currentHeight = 0;
+ pin.keyPinHeight = 0; // Reset key pin height
+ pin.driverPinHeight = 0; // Reset driver pin height
+ pin.overpickingTimer = null; // Reset overpicking timer
+
+ // Reset visual
+ pin.keyPin.clear();
+ pin.keyPin.fillStyle(0xdd3333);
+
+ // Draw rectangular part of key pin
+ pin.keyPin.fillRect(-12, -50 + pin.driverPinLength, 24, pin.keyPinLength - 8);
+
+ // Draw triangular bottom in pixel art style
+ pin.keyPin.fillRect(-12, -50 + pin.driverPinLength + pin.keyPinLength - 8, 24, 2);
+ pin.keyPin.fillRect(-10, -50 + pin.driverPinLength + pin.keyPinLength - 6, 20, 2);
+ pin.keyPin.fillRect(-8, -50 + pin.driverPinLength + pin.keyPinLength - 4, 16, 2);
+ pin.keyPin.fillRect(-6, -50 + pin.driverPinLength + pin.keyPinLength - 2, 12, 2);
+
+ pin.driverPin.clear();
+ pin.driverPin.fillStyle(0x3388dd);
+ pin.driverPin.fillRect(-12, -50, 24, pin.driverPinLength);
+
+ // Reset spring to original position
+ pin.spring.clear();
+ pin.spring.fillStyle(0x666666);
+ const springTop = -130; // Fixed spring top
+ const springBottom = -50; // Driver pin top when not lifted
+ const springHeight = springBottom - springTop;
+
+ // Calculate total spring space and distribute segments evenly
+ const totalSpringSpace = springHeight;
+ const segmentSpacing = totalSpringSpace / 11; // 11 gaps between 12 segments
+
+ for (let s = 0; s < 12; s++) {
+ const segmentHeight = 4;
+ const segmentY = springTop + (s * segmentSpacing);
+ pin.spring.fillRect(-12, segmentY, 24, segmentHeight);
+ }
+
+ // Hide all highlights
+ if (pin.shearHighlight) pin.shearHighlight.setVisible(false);
+ if (pin.setHighlight) pin.setHighlight.setVisible(false);
+ if (pin.bindingHighlight) pin.bindingHighlight.setVisible(false);
+ if (pin.overpickedHighlight) pin.overpickedHighlight.setVisible(false);
+ if (pin.failureHighlight) pin.failureHighlight.setVisible(false);
+ });
+
+ // Reset lock state
+ this.lockState.pinsSet = 0;
+ }
+
+ this.updateBindingPins();
+ }
+ });
+
+ // Add keyboard release handler for pin keys
+ this.scene.input.keyboard.on('keyup', (event) => {
+ const key = event.key;
+
+ // Pin number keys (1-8)
+ if (key >= '1' && key <= '8') {
+ const pinIndex = parseInt(key) - 1; // Convert 1-8 to 0-7
+
+ // Check if pin exists and is currently being held
+ if (pinIndex < this.pinCount && this.lockState.currentPin && this.lockState.currentPin.index === pinIndex) {
+ this.checkPinSet(this.lockState.currentPin);
+ this.lockState.currentPin = null;
+ this.gameState.mouseDown = false;
+
+ // Return hook to resting position
+ if (this.hookPickGraphics && this.hookConfig) {
+ this.returnHookToStart();
+ }
+ }
+ }
+ });
}
update() {
@@ -1345,7 +1535,20 @@ export class LockpickingMinigamePhaser extends MinigameScene {
pin.shearHighlight.fillRect(-22.5, -110, 45, 140);
pin.container.addAt(pin.shearHighlight, 0); // Add at beginning to appear behind pins
}
+
+ // Check if highlight is transitioning from hidden to visible
+ const wasHidden = !pin.shearHighlight.visible;
pin.shearHighlight.setVisible(true);
+
+ // Play feedback when highlight first appears
+ if (wasHidden) {
+ if (this.sounds.click) {
+ this.sounds.click.play();
+ }
+ if (typeof navigator !== 'undefined' && navigator.vibrate) {
+ navigator.vibrate(100);
+ }
+ }
} else {
if (pin.shearHighlight) {
pin.shearHighlight.setVisible(false);
@@ -1592,7 +1795,7 @@ export class LockpickingMinigamePhaser extends MinigameScene {
// Play set sound
if (this.sounds.set) {
this.sounds.set.play();
- navigator.vibrate([200,100,200]);
+ navigator.vibrate(500);
}
this.updateFeedback(`Pin ${pin.index + 1} set! (${this.lockState.pinsSet}/${this.pinCount})`);
@@ -1821,7 +2024,7 @@ export class LockpickingMinigamePhaser extends MinigameScene {
// Play success sound
if (this.sounds.success) {
this.sounds.success.play();
- navigator.vibrate([200,100,200,100,200]);
+ navigator.vibrate(500);
}
this.updateFeedback("Lock picked successfully!");
@@ -2045,7 +2248,6 @@ export class LockpickingMinigamePhaser extends MinigameScene {
// Show success message immediately but delay the game completion
const successHTML = `
Lock picked successfully!
- All pins set at the shear line
`;
// this.showSuccess(successHTML, false, 2000);
diff --git a/locksmith-forge.html b/locksmith-forge.html
index 04938dd..cd7096c 100644
--- a/locksmith-forge.html
+++ b/locksmith-forge.html
@@ -236,16 +236,20 @@
max-width: 90%;
z-index: 10;
}
+
+
+
@@ -355,39 +359,69 @@
const config = {};
for (let level = 1; level <= this.maxLevel; level++) {
- // Base progression
- let pinCount = Math.min(3 + Math.floor((level - 1) / 5), 8); // 3-8 pins
- let difficulty = this.getDifficulty(level);
- let sensitivity = Math.max(1, Math.min(8, 1 + Math.floor((level - 1) / 5.57))); // 1-8, reaches 8 at level 40
- let liftSpeed = Math.max(0.5, Math.min(3.0, 0.6 + (level - 1) * 0.03)); // 0.5-3.0 (starts much slower, progresses very gradually)
+ // Add base progression across 10-level blocks
+ const blockNumber = Math.floor((level - 1) / 10) + 1;
+ // Determine position within the current 10-level block (1-10)
+ const positionInBlock = ((level - 1) % 10) + 1;
- // Add some randomness and complexity
- if (level > 10) {
- // Randomly disable hints for higher levels
- const disableHints = Math.random() > 0.7;
- if (disableHints) {
- sensitivity = Math.max(1, sensitivity - 2);
- }
+
+ // Each set of 10 levels starts with 3 pins + 1 for each block of 10 levels
+ let pinCount = Math.min(8, blockNumber + 2); // updated below
+ console.log('pinCount', pinCount);
+ console.log('blockNumber', blockNumber);
+
+ // Alternate between increasing speed and sensitivity within each 10-level block
+ let sensitivity = 1;
+ let liftSpeed = 0.6;
+
+ if (positionInBlock <= 5) {
+ // First 5 levels: increase sensitivity
+ sensitivity = 1 + Math.floor((positionInBlock - 1) / 2);
+ } else {
+ // Last 5 levels: increase speed
+ const speedLevel = positionInBlock - 5;
+ liftSpeed = 0.6 + (speedLevel * 0.1);
+ }
+
+ // Add base progression across 10-level blocks
+ sensitivity += blockNumber;
+ liftSpeed += (blockNumber * 0.2);
+
+ // every 3rd, 5th level, increase pin count
+ if (positionInBlock >= 5) {
+ pinCount = Math.min(8, pinCount + 2); // max 8 pins
+ } else if (positionInBlock >= 3) {
+ pinCount = Math.min(8, pinCount + 1); // max 8 pins
}
- if (level > 20) {
- // Increase difficulty more aggressively
- liftSpeed = Math.min(3.0, liftSpeed + 0.1);
- }
+ // Ensure values stay within bounds
+ sensitivity = Math.max(1, Math.min(8, sensitivity));
+ liftSpeed = Math.max(0.5, Math.min(3.0, liftSpeed));
- if (level > 30) {
- // Very challenging levels
- pinCount = Math.min(8, pinCount + 1);
- sensitivity = Math.max(1, sensitivity - 1);
+ // Hint settings based on position in 10-level block
+ let highlightBindingOrder = 'enabled';
+ let pinAlignmentHighlighting = 'enabled';
+
+ // Last 3 levels of each 10-level block remove hints progressively
+ if (positionInBlock === 8) {
+ // 8th level: remove binding order highlighting
+ highlightBindingOrder = 'disabled';
+ } else if (positionInBlock === 9) {
+ // 9th level: remove pin alignment highlighting
+ pinAlignmentHighlighting = 'disabled';
+ } else if (positionInBlock === 10) {
+ // 10th level: remove both
+ highlightBindingOrder = 'disabled';
+ pinAlignmentHighlighting = 'disabled';
}
config[level] = {
pinCount,
- difficulty,
+ difficulty: this.getDifficulty(level),
sensitivity,
- liftSpeed: Math.round(liftSpeed * 10) / 10,
- highlightBindingOrder: (level % 10 === 0) ? 'disabled' : (level <= 15 ? 'enabled' : (Math.random() > 0.5 ? 'enabled' : 'disabled')),
- pinAlignmentHighlighting: (level % 10 === 0) ? 'disabled' : (level <= 10 ? 'enabled' : (Math.random() > 0.6 ? 'enabled' : 'disabled'))
+ liftSpeed: parseFloat(liftSpeed.toFixed(2)),
+ highlightBindingOrder,
+ pinAlignmentHighlighting
};
}
@@ -517,23 +551,23 @@
// Check for milestone levels
const milestoneMessages = {
1: {
- status: `🎯 Great start! Level 1 complete! You're on your way to becoming a lockpicking master! 🎯`,
+ status: `🎯 Great start! 🎯`,
achievement: `🌟 First Steps - Level 1 Complete! 🌟`
},
10: {
- status: `🔥 Excellent progress! Level 10 conquered! You're getting the hang of this! 🔥`,
+ status: `🔥 Excellent progress! 🔥`,
achievement: `⚡ Rising Star - Level 10 Complete! ⚡`
},
20: {
- status: `💪 Impressive! Level 20 mastered! Your skills are really developing! 💪`,
+ status: `💪 Impressive! 💪`,
achievement: `🏅 Skillful Picker - Level 20 Complete! 🏅`
},
30: {
- status: `🚀 Outstanding! Level 30 achieved! You're becoming a true expert! 🚀`,
+ status: `🚀 Outstanding! 🚀`,
achievement: `🎖️ Expert Level - Level 30 Complete! 🎖️`
},
40: {
- status: `⚔️ Phenomenal! Level 40 conquered! You're almost at the ultimate challenge! ⚔️`,
+ status: `⚔️ Phenomenal! ⚔️`,
achievement: `🏆 Elite Picker - Level 40 Complete! 🏆`
}
};
@@ -606,20 +640,84 @@
const config = this.levelConfig[this.currentLevel];
if (config) {
+ // Update values
document.getElementById('pinCount').textContent = config.pinCount;
-
- // Show hints status based on whether visual highlighting is enabled
- const hintsEnabled = config.highlightBindingOrder === 'enabled' || config.pinAlignmentHighlighting === 'enabled';
- document.getElementById('hints').textContent = hintsEnabled ? 'Enabled' : 'Disabled';
-
+ document.getElementById('bindingHints').textContent = config.highlightBindingOrder === 'enabled' ? 'Visible' : 'Hidden';
+ document.getElementById('alignmentHints').textContent = config.pinAlignmentHighlighting === 'enabled' ? 'Visible' : 'Hidden';
document.getElementById('sensitivity').textContent = config.sensitivity;
document.getElementById('liftSpeed').textContent = config.liftSpeed;
+
+ // Apply highlighting based on level position
+ this.highlightBasedOnLevel(config);
}
// Update progress bar to reflect current level
this.updateProgress();
}
+ highlightBasedOnLevel(config) {
+ // Determine position within the current 10-level block (1-10)
+ const positionInBlock = ((this.currentLevel - 1) % 10) + 1;
+ const blockNumber = Math.floor((this.currentLevel - 1) / 10);
+
+ // Get all stat elements
+ const statElements = document.querySelectorAll('.stat');
+
+ statElements.forEach(stat => {
+ const span = stat.querySelector('span');
+ if (!span) return;
+
+ const statType = span.id;
+ let shouldHighlight = false;
+
+ // Determine what should be highlighted based on level position
+ switch (statType) {
+ case 'pinCount':
+ // Highlight on levels 3 and 5 (when pin count increases)
+ shouldHighlight = (positionInBlock === 3 || positionInBlock === 5);
+ break;
+
+ case 'sensitivity':
+ // Highlight on levels 1-5 (sensitivity focus phase)
+ shouldHighlight = (positionInBlock <= 5);
+ break;
+
+ case 'liftSpeed':
+ // Highlight on levels 6-10 (speed focus phase)
+ shouldHighlight = (positionInBlock >= 6);
+ break;
+
+ case 'bindingHints':
+ // Highlight when binding order hints are enabled
+ shouldHighlight = (config.highlightBindingOrder === 'disabled');
+ break;
+
+ case 'alignmentHints':
+ // Highlight when pin alignment hints are enabled
+ shouldHighlight = (config.pinAlignmentHighlighting === 'disabled');
+ break;
+
+ default:
+ shouldHighlight = false;
+ }
+
+ if (shouldHighlight) {
+ // Highlight the value
+ stat.style.backgroundColor = '#2a4a2a';
+ stat.style.color = '#00ff00';
+ stat.style.fontWeight = 'bold';
+ stat.style.transition = 'all 0.3s ease';
+ stat.style.opacity = '1';
+ } else {
+ // Show normally (slightly faded)
+ stat.style.backgroundColor = '';
+ stat.style.color = '';
+ stat.style.fontWeight = '';
+ stat.style.opacity = '0.7';
+ }
+ });
+ }
+
updateStatus(message) {
const feedbackElement = document.querySelector('.lockpick-feedback');
if (feedbackElement) {