diff --git a/index.html b/index.html
index 9736ee1..af13d1b 100644
--- a/index.html
+++ b/index.html
@@ -884,58 +884,224 @@
}
.tension-control {
- display: flex;
+ display: grid;
+ grid-template-columns: auto 1fr;
+ gap: 20px;
align-items: center;
background: #333;
- padding: 10px 15px;
+ padding: 20px;
border-radius: 5px;
- font-size: 14px;
- gap: 15px;
+ margin-top: 20px;
}
-
- .tension-status {
- font-size: 13px;
- color: #ddd;
- }
-
- .tension-wrench {
+
+ .tension-wrench-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 10px;
position: relative;
- height: 25px;
+ width: 150px;
+ height: 60px;
+ }
+
+ .tension-track {
+ width: 100%;
+ height: 10px;
+ background: #444;
+ border-radius: 5px;
+ position: relative;
+ overflow: hidden;
+ }
+
+ .tension-progress {
+ position: absolute;
+ height: 100%;
+ width: 0%;
+ background: linear-gradient(to right, #666, #2196F3);
+ transition: width 0.3s;
+ }
+
+ .tension-status {
+ font-size: 16px;
+ text-align: left;
+ padding-left: 10px;
+ }
+
+ .tension-wrench {
width: 60px;
+ height: 40px;
+ background: #666;
+ border-radius: 4px;
cursor: pointer;
- }
-
- .wrench-handle {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: transform 0.3s, background-color 0.3s;
position: absolute;
- top: 10px;
- right: 0;
- width: 40px;
- height: 5px;
- background: #aaa;
- border-radius: 2px;
- transform-origin: right center;
- transition: transform 0.3s;
+ left: 0;
+ top: 20px;
+ z-index: 2;
+ box-shadow: 0 2px 5px rgba(0,0,0,0.3);
}
-
- .wrench-tip {
- position: absolute;
- right: 0;
- top: 5px;
- width: 10px;
- height: 15px;
- background: #888;
- border-radius: 2px;
+
+ .tension-wrench:hover {
+ background: #777;
}
-
- .tension-wrench.active .wrench-handle {
- transform: rotate(-20deg);
+
+ .tension-wrench.active {
background: #2196F3;
}
+ .wrench-handle {
+ width: 60%;
+ height: 10px;
+ background: #999;
+ position: absolute;
+ }
+
+ .wrench-tip {
+ width: 20px;
+ height: 30px;
+ background: #999;
+ position: absolute;
+ left: 5px;
+ }
+
.cylinder {
height: 20px;
margin-top: -5px;
}
+
+ .lock-visual {
+ display: flex;
+ justify-content: space-evenly;
+ align-items: center;
+ gap: 10px;
+ height: 160px;
+ background: #f0e6a6; /* Light yellow/beige background */
+ border-radius: 5px;
+ padding: 15px;
+ position: relative;
+ margin-bottom: 10px;
+ border: 2px solid #887722;
+ }
+
+ .pin {
+ width: 30px;
+ height: 110px;
+ position: relative;
+ background: transparent;
+ border-radius: 4px 4px 0 0;
+ overflow: visible;
+ cursor: pointer;
+ transition: transform 0.1s;
+ margin: 0 5px;
+ }
+
+ .pin:hover {
+ opacity: 0.9;
+ }
+
+ .shear-line {
+ position: absolute;
+ width: 100%;
+ height: 2px;
+ background: #aa8833;
+ bottom: 50px;
+ z-index: 5;
+ }
+
+ .key-pin {
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ height: 0px;
+ background: #dd3333; /* Red for key pins */
+ transition: height 0.05s;
+ border-radius: 0 0 0 0;
+ clip-path: polygon(0 0, 100% 0, 100% 70%, 50% 100%, 0 70%); /* Pointed bottom */
+ }
+
+ .driver-pin {
+ position: absolute;
+ width: 100%;
+ height: 50px;
+ background: #3388dd; /* Blue for driver pins */
+ transition: bottom 0.05s;
+ bottom: 50px;
+ border-radius: 0 0 0 0;
+ }
+
+ .spring {
+ position: absolute;
+ bottom: 100px;
+ width: 100%;
+ height: 25px;
+ background: linear-gradient(to bottom,
+ #cccccc 0%, #cccccc 20%,
+ #999999 20%, #999999 25%,
+ #cccccc 25%, #cccccc 40%,
+ #999999 40%, #999999 45%,
+ #cccccc 45%, #cccccc 60%,
+ #999999 60%, #999999 65%,
+ #cccccc 65%, #cccccc 80%,
+ #999999 80%, #999999 85%,
+ #cccccc 85%, #cccccc 100%
+ );
+ transition: height 0.05s;
+ }
+
+ .pin.binding {
+ box-shadow: 0 0 8px 2px #ffcc00;
+ }
+
+ .pin.set .driver-pin {
+ bottom: 52px; /* Just above shear line */
+ background: #22aa22; /* Green to indicate set */
+ }
+
+ .pin.set .key-pin {
+ height: 49px; /* Just below shear line */
+ background: #22aa22; /* Green to indicate set */
+ clip-path: polygon(0 0, 100% 0, 100% 70%, 50% 100%, 0 70%);
+ }
+
+ .cylinder {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ height: 30px;
+ background: #ddbb77;
+ border-radius: 5px;
+ margin-top: 5px;
+ position: relative;
+ z-index: 0;
+ border: 2px solid #887722;
+ }
+
+ .cylinder-inner {
+ width: 80%;
+ height: 20px;
+ background: #ccaa66;
+ border-radius: 3px;
+ transform-origin: center;
+ transition: transform 0.3s;
+ }
+
+ .cylinder.rotated .cylinder-inner {
+ transform: rotate(15deg);
+ }
+
+ .lockpick-feedback {
+ padding: 15px;
+ background: #333;
+ border-radius: 5px;
+ text-align: center;
+ min-height: 30px;
+ margin-top: 20px;
+ font-size: 16px;
+ }
@@ -4833,106 +4999,149 @@
this.container.style.padding = '20px';
this.container.style.gap = '15px';
- // Set up header content
+ // Set up header content with proper spacing
this.headerElement.innerHTML = `
-
Lockpicking
- Apply tension and hold click on pins to lift them to the shear line
+ Lockpicking
+ Apply tension and hold click on pins to lift them to the shear line
`;
+ this.headerElement.style.marginBottom = '30px'; // Add more space below header
// Add custom styles for the lockpicking minigame if they don't exist
if (!document.getElementById('lockpicking-styles')) {
const style = document.createElement('style');
style.id = 'lockpicking-styles';
style.textContent = `
+ /* Game container styles */
+ .minigame-container {
+ padding-top: 80px !important; /* Add padding at top to prevent header overlap */
+ position: relative;
+ }
+
+ .minigame-header {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ z-index: 10;
+ background: rgba(34, 34, 34, 0.95);
+ border-bottom: 1px solid #444;
+ padding: 10px 20px;
+ margin-bottom: 20px;
+ }
+
.lock-visual {
display: flex;
justify-content: space-evenly;
align-items: center;
- gap: 10px;
- height: 150px;
- background: #222;
+ gap: 20px;
+ height: 200px;
+ background: #f0e6a6; /* Light yellow/beige background */
border-radius: 5px;
- padding: 15px;
+ padding: 25px;
position: relative;
- margin-bottom: 10px;
+ margin-top: 20px; /* Add top margin for better spacing from header */
+ margin-bottom: 20px;
+ border: 2px solid #887722;
+ z-index: 1; /* Ensure pins are below header */
}
+ /* Rest of existing CSS */
.pin {
- width: 30px;
- height: 100px;
+ width: 40px;
+ height: 150px;
position: relative;
- background: #e0d8b0;
+ background: transparent;
border-radius: 4px 4px 0 0;
overflow: visible;
cursor: pointer;
transition: transform 0.1s;
- margin: 0 5px;
+ margin: 0 15px;
}
.pin:hover {
- background: #f0e8c0;
+ opacity: 0.9;
}
.shear-line {
position: absolute;
width: 100%;
height: 2px;
- background: #aaa;
- bottom: 40px;
+ background: #aa8833;
+ bottom: 70px;
z-index: 5;
}
+ .pin-assembly {
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ height: 140px;
+ transition: transform 0.05s;
+ }
+
.key-pin {
position: absolute;
bottom: 0;
width: 100%;
- height: 0px;
- background: #66a3ff;
- border-radius: 3px 3px 0 0;
- transition: height 0.05s;
+ height: 50px; /* Fixed height for all pins */
+ background: #dd3333; /* Red for key pins */
+ border-radius: 0 0 0 0;
+ clip-path: polygon(0 0, 100% 0, 100% 70%, 50% 100%, 0 70%); /* Pointed bottom */
+ transition: transform 0.3s;
}
.driver-pin {
position: absolute;
width: 100%;
- height: 40px;
- background: #66a3ff;
- transition: bottom 0.05s;
- bottom: 40px;
- border-radius: 0 0 3px 3px;
+ height: 70px;
+ background: #3388dd; /* Blue for driver pins */
+ bottom: 50px; /* Position right above key pin */
+ border-radius: 0 0 0 0;
+ transition: transform 0.3s, background-color 0.3s;
}
.spring {
position: absolute;
- bottom: 80px;
+ bottom: 120px;
width: 100%;
- height: 20px;
+ height: 40px;
background: linear-gradient(to bottom,
- transparent 0%, transparent 20%,
- #999 20%, #999 30%,
- transparent 30%, transparent 40%,
- #999 40%, #999 50%,
- transparent 50%, transparent 60%,
- #999 60%, #999 70%,
- transparent 70%, transparent 80%,
- #999 80%, #999 90%,
- transparent 90%, transparent 100%
+ #cccccc 0%, #cccccc 20%,
+ #999999 20%, #999999 25%,
+ #cccccc 25%, #cccccc 40%,
+ #999999 40%, #999999 45%,
+ #cccccc 45%, #cccccc 60%,
+ #999999 60%, #999999 65%,
+ #cccccc 65%, #cccccc 80%,
+ #999999 80%, #999999 85%,
+ #cccccc 85%, #cccccc 100%
);
- transition: height 0.05s;
+ transition: transform 0.3s;
}
.pin.binding {
box-shadow: 0 0 8px 2px #ffcc00;
}
- .pin.set .driver-pin {
- bottom: 42px; /* Just above shear line */
- background: #22aa22; /* Green to indicate set */
+ /* Remove the pin-assembly transform for set pins */
+ .pin.set .pin-assembly {
+ transform: none; /* Reset transform so we can control individual pieces */
}
- .pin.set .key-pin {
- height: 39px; /* Just below shear line */
+ /* Keep driver pin (blue) above the shear line when set */
+ .pin.set .driver-pin {
background: #22aa22; /* Green to indicate set */
+ transform: translateY(-22px); /* Move up above shear line */
+ }
+
+ /* Reset key pin (red) to the bottom when set */
+ .pin.set .key-pin {
+ transform: translateY(0); /* Keep at bottom */
+ }
+
+ /* Move spring up with driver pin when set */
+ .pin.set .spring {
+ transform: translateY(-22px); /* Move up with driver pin */
}
.cylinder {
@@ -4941,17 +5150,18 @@
align-items: center;
width: 100%;
height: 30px;
- background: #222;
+ background: #ddbb77;
border-radius: 5px;
margin-top: 5px;
position: relative;
z-index: 0;
+ border: 2px solid #887722;
}
.cylinder-inner {
width: 80%;
height: 20px;
- background: #333;
+ background: #ccaa66;
border-radius: 3px;
transform-origin: center;
transition: transform 0.3s;
@@ -4962,31 +5172,57 @@
}
.lockpick-feedback {
- padding: 10px;
+ padding: 15px;
background: #333;
border-radius: 5px;
text-align: center;
- min-height: 20px;
- margin-top: 10px;
+ min-height: 30px;
+ margin-top: 20px;
+ font-size: 16px;
}
.tension-control {
- display: flex;
- justify-content: space-between;
+ display: grid;
+ grid-template-columns: auto 1fr;
+ gap: 20px;
align-items: center;
background: #333;
- padding: 15px;
+ padding: 20px;
border-radius: 5px;
- margin-top: 10px;
+ margin-top: 20px;
}
- .tension-control label {
- flex: 1;
+ .tension-wrench-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 10px;
+ position: relative;
+ width: 150px;
+ height: 60px;
+ }
+
+ .tension-track {
+ width: 100%;
+ height: 10px;
+ background: #444;
+ border-radius: 5px;
+ position: relative;
+ overflow: hidden;
+ }
+
+ .tension-progress {
+ position: absolute;
+ height: 100%;
+ width: 0%;
+ background: linear-gradient(to right, #666, #2196F3);
+ transition: width 0.3s;
}
.tension-status {
- flex: 1;
- text-align: right;
+ font-size: 16px;
+ text-align: left;
+ padding-left: 10px;
}
.tension-wrench {
@@ -4998,9 +5234,12 @@
display: flex;
align-items: center;
justify-content: center;
- margin-right: 15px;
- transition: transform 0.2s;
- position: relative;
+ transition: transform 0.3s, background-color 0.3s;
+ position: absolute;
+ left: 0;
+ top: 20px;
+ z-index: 2;
+ box-shadow: 0 2px 5px rgba(0,0,0,0.3);
}
.tension-wrench:hover {
@@ -5009,7 +5248,6 @@
.tension-wrench.active {
background: #2196F3;
- transform: rotate(-5deg);
}
.wrench-handle {
@@ -5020,8 +5258,8 @@
}
.wrench-tip {
- width: 15px;
- height: 25px;
+ width: 20px;
+ height: 30px;
background: #999;
position: absolute;
left: 5px;
@@ -5030,6 +5268,10 @@
document.head.appendChild(style);
}
+ // Add a class to the container for positioning
+ this.container.classList.add('minigame-container');
+ this.headerElement.classList.add('minigame-header');
+
// Replace the game container with custom lockpicking interface
this.setupLockpickingInterface();
@@ -5046,52 +5288,94 @@
this.gameContainer.parentNode.removeChild(this.gameContainer);
}
+ // Create the content wrapper with padding to avoid header overlap
+ const contentWrapper = document.createElement('div');
+ contentWrapper.style.paddingTop = '10px';
+ this.container.appendChild(contentWrapper);
+ this.contentWrapper = contentWrapper;
+
+ // Create instructions
+ const instructions = document.createElement('div');
+ instructions.className = 'instructions';
+ instructions.textContent = 'Apply tension first, then click and hold on pins to lift them to the shear line';
+ contentWrapper.appendChild(instructions);
+
// Create the lock visual container
const lockVisual = document.createElement('div');
lockVisual.className = 'lock-visual';
- this.container.appendChild(lockVisual);
+ contentWrapper.appendChild(lockVisual);
this.lockVisual = lockVisual;
- // Create the cylinder that will rotate when tension is applied
- const cylinder = document.createElement('div');
- cylinder.className = 'cylinder';
- cylinder.innerHTML = '';
- this.container.appendChild(cylinder);
- this.cylinder = cylinder;
+ // Remove cylinder creation - it's no longer needed
- // Add tension toggle control
+ // Add tension toggle control with horizontal movement
const tensionControl = document.createElement('div');
tensionControl.className = 'tension-control';
- tensionControl.innerHTML = `
-
- Click wrench to apply tension
+
+ const wrenchContainer = document.createElement('div');
+ wrenchContainer.className = 'tension-wrench-container';
+
+ const wrenchLabel = document.createElement('div');
+ wrenchLabel.textContent = 'Tension Wrench';
+ wrenchLabel.style.fontSize = '14px';
+ wrenchContainer.appendChild(wrenchLabel);
+
+ // Add tension track and progress
+ const tensionTrack = document.createElement('div');
+ tensionTrack.className = 'tension-track';
+
+ const tensionProgress = document.createElement('div');
+ tensionProgress.className = 'tension-progress';
+ tensionTrack.appendChild(tensionProgress);
+
+ wrenchContainer.appendChild(tensionTrack);
+
+ const tensionWrench = document.createElement('div');
+ tensionWrench.className = 'tension-wrench';
+ tensionWrench.innerHTML = `
+
+
`;
- this.container.appendChild(tensionControl);
+ wrenchContainer.appendChild(tensionWrench);
+
+ tensionControl.appendChild(wrenchContainer);
+
+ const tensionStatus = document.createElement('div');
+ tensionStatus.className = 'tension-status';
+ tensionStatus.textContent = 'Click wrench to apply tension';
+ tensionControl.appendChild(tensionStatus);
+
+ this.contentWrapper.appendChild(tensionControl);
// Feedback area
const feedback = document.createElement('div');
feedback.className = 'lockpick-feedback';
- this.container.appendChild(feedback);
+ this.contentWrapper.appendChild(feedback);
this.feedback = feedback;
- // Set up tension wrench interaction
- const tensionWrench = tensionControl.querySelector('.tension-wrench');
- const tensionStatus = tensionControl.querySelector('.tension-status');
-
+ // Set up tension wrench interaction with horizontal movement
tensionWrench.addEventListener('click', () => {
this.lockState.tensionApplied = !this.lockState.tensionApplied;
tensionWrench.classList.toggle('active', this.lockState.tensionApplied);
- cylinder.classList.toggle('rotated', this.lockState.tensionApplied);
+
+ // Move wrench horizontally instead of rotating
+ if (this.lockState.tensionApplied) {
+ // Move to initial right position (25%)
+ this.updateTensionPosition(25);
+ } else {
+ // Return to left position (0%)
+ this.updateTensionPosition(0);
+
+ // Reset progress fill
+ tensionProgress.style.width = '0%';
+ }
// Update status text
tensionStatus.textContent = this.lockState.tensionApplied ?
- 'Tension applied' : 'Click wrench to apply tension';
+ 'Tension applied - now lift pins' : 'Click wrench to apply tension';
// Update which pins are binding
- this.updatePinBindings();
+ this.updateBindingPins();
// If tension is toggled off, reset any unset pins
if (!this.lockState.tensionApplied) {
@@ -5110,6 +5394,27 @@
// Store references
this.tensionWrench = tensionWrench;
this.tensionStatus = tensionStatus;
+ this.tensionProgress = tensionProgress;
+ }
+
+ // New method to update the tension wrench position
+ updateTensionPosition(percentage) {
+ if (percentage < 0) percentage = 0;
+ if (percentage > 100) percentage = 100;
+
+ // Calculate position based on container width
+ const containerWidth = this.tensionWrench.parentElement.offsetWidth;
+ const wrenchWidth = this.tensionWrench.offsetWidth;
+ const maxOffset = containerWidth - wrenchWidth;
+ const position = (maxOffset * percentage) / 100;
+
+ // Update wrench position
+ this.tensionWrench.style.transform = `translateX(${position}px)`;
+
+ // Update progress bar fill
+ if (this.tensionProgress) {
+ this.tensionProgress.style.width = `${percentage}%`;
+ }
}
createPins() {
@@ -5128,30 +5433,49 @@
shearLine.className = 'shear-line';
pinElement.appendChild(shearLine);
- // Create key pin (bottom pin)
+ // Create pin assembly container
+ const pinAssembly = document.createElement('div');
+ pinAssembly.className = 'pin-assembly';
+ pinElement.appendChild(pinAssembly);
+
+ // Create key pin (bottom pin) with varying height
const keyPin = document.createElement('div');
keyPin.className = 'key-pin';
- pinElement.appendChild(keyPin);
+ pinAssembly.appendChild(keyPin);
- // Create driver pin (top pin)
+ // Generate random key pin height (30px to 60px)
+ const keyPinHeight = Math.floor(Math.random() * 31) + 30;
+ keyPin.style.height = `${keyPinHeight}px`;
+
+ // Create driver pin (top pin) with consistent height
const driverPin = document.createElement('div');
driverPin.className = 'driver-pin';
- pinElement.appendChild(driverPin);
+ pinAssembly.appendChild(driverPin);
+ // Position driver pin right above the key pin
+ driverPin.style.bottom = `${keyPinHeight}px`;
// Create spring
const spring = document.createElement('div');
spring.className = 'spring';
- pinElement.appendChild(spring);
+ pinAssembly.appendChild(spring);
+ // Position spring above driver pin
+ spring.style.bottom = `${keyPinHeight + 70}px`; // 70px is driver pin height
+
+ // Calculate the distance from the bottom of the pin to the shear line (70px from bottom)
+ const distanceToShearLine = 70 - keyPinHeight;
// Store pin data
const pin = {
index: i,
binding: bindingOrder.indexOf(i),
- setPoint: Math.random() * 0.4 + 0.3, // Point between 30-70% where pin sets
- currentHeight: 0,
+ keyPinHeight: keyPinHeight,
+ distanceToShearLine: distanceToShearLine,
+ currentHeight: 0, // How high the pin is currently lifted (0-1 scale)
isSet: false,
+ resistance: Math.random() * 0.02 + 0.01,
elements: {
container: pinElement,
+ assembly: pinAssembly,
keyPin: keyPin,
driverPin: driverPin,
spring: spring
@@ -5160,99 +5484,89 @@
this.pins.push(pin);
- // Add custom pin click handler
- this.addEventListenerWithCleanup(pinElement, 'mousedown', (e) => {
- this.handlePinMouseDown(pin, e);
+ // Fix: Use an arrow function to preserve 'this' context
+ // and define the handler inline instead of referencing this.handlePinMouseDown
+ const self = this; // Store reference to 'this'
+ this.addEventListenerWithCleanup(pinElement, 'mousedown', function(e) {
+ // Skip if game is not active or pin is already set
+ if (!self.gameState.isActive || pin.isSet) return;
+
+ // Only proceed if tension is applied
+ if (!self.lockState.tensionApplied) {
+ self.updateFeedback("Apply tension first by toggling the wrench");
+ return;
+ }
+
+ // Play a sound effect when interacting with pins
+ if (typeof self.playSound === 'function') {
+ self.playSound('pin_click');
+ }
+
+ // Start lifting the pin
+ self.lockState.currentPin = pin;
+ self.gameState.mouseDown = true;
+ self.liftPin();
+
+ // Add mouse up listener to document
+ const mouseUpHandler = function() {
+ self.gameState.mouseDown = false;
+ self.checkPinSet(self.lockState.currentPin);
+ self.lockState.currentPin = null;
+ document.removeEventListener('mouseup', mouseUpHandler);
+ };
+
+ document.addEventListener('mouseup', mouseUpHandler);
+
+ // Prevent text selection
+ e.preventDefault();
});
}
}
- // Custom pin mouse down handler
- handlePinMouseDown(pin, e) {
- if (!this.gameState.isActive || pin.isSet) return;
-
- // Only proceed if tension is applied
- if (!this.lockState.tensionApplied) {
- this.updateFeedback("Apply tension first by toggling the wrench");
- return;
- }
-
- // Start lifting the pin
- this.lockState.currentPin = pin;
- this.gameState.mouseDown = true; // Add this line to track mouse state
- this.liftPin();
-
- // Add mouse up listener to document
- const mouseUpHandler = () => {
- this.gameState.mouseDown = false;
- this.checkPinSet(this.lockState.currentPin);
- this.lockState.currentPin = null;
- document.removeEventListener('mouseup', mouseUpHandler);
- };
-
- document.addEventListener('mouseup', mouseUpHandler);
-
- // Prevent text selection
- e.preventDefault();
- }
-
- // Override framework's mouse event handlers
- handleMouseUp(e) {
- // The document-level handler above will take care of this
- // This is still needed for framework compatibility
- this.gameState.mouseDown = false;
- }
-
- // Pin-lifting logic
- liftPin() {
- if (!this.lockState.currentPin || !this.gameState.isActive ||
- !this.lockState.tensionApplied || !this.gameState.mouseDown) {
- return;
- }
-
- const pin = this.lockState.currentPin;
-
- // Only binding pins can be lifted effectively
- if (!this.shouldPinBind(pin)) {
- // Non-binding pins can be lifted, but with resistance and limited height
- pin.currentHeight += 0.01;
- if (pin.currentHeight > 0.3) {
- pin.currentHeight = 0.3; // Can't lift non-binding pins very high
- }
- } else {
- // Binding pins lift smoothly
- pin.currentHeight += 0.03;
- if (pin.currentHeight > 1) {
- pin.currentHeight = 1; // Max height
- }
- }
-
- // Update visual
- this.updatePinVisual(pin);
-
- // Continue lifting while mouse is down
- if (this.gameState.mouseDown) {
- requestAnimationFrame(() => this.liftPin());
- }
- }
-
// Check if a pin should be set or dropped
checkPinSet(pin) {
if (!this.lockState.tensionApplied || !this.shouldPinBind(pin)) {
- // If no tension or not binding, the pin drops
- this.dropPin(pin);
+ // Define dropPin function inline since it's not being found
+ this.animatePinDrop(pin);
return;
}
- // Check if pin is at the correct height (with some tolerance)
- const heightDiff = Math.abs(pin.currentHeight - pin.setPoint);
+ // Calculate current pin height in pixels
+ const currentLiftInPixels = pin.currentHeight * pin.distanceToShearLine;
- if (heightDiff < 0.1) {
+ // Check if the top of the key pin (or bottom of driver pin) is exactly at the shear line
+ // Allow a small tolerance of 2 pixels
+ const tolerance = 2;
+ const isAtShearLine = Math.abs(currentLiftInPixels - pin.distanceToShearLine) <= tolerance;
+
+ if (isAtShearLine) {
// Pin set successfully!
pin.isSet = true;
this.lockState.pinsSet++;
+
+ // Play a satisfying click sound when pin sets
+ if (typeof this.playSound === 'function') {
+ this.playSound('pin_set');
+ }
+
+ // First reset the assembly position
+ pin.elements.assembly.style.transform = 'none';
+
+ // Calculate exact position for the pin junction to be at the shear line
+ const exactLift = pin.distanceToShearLine;
+ pin.elements.assembly.style.transform = `translateY(-${exactLift}px)`;
+
+ // Mark the pin as set
+ pin.elements.container.classList.add('set');
+
+ // Change color of the driver pin to green
+ pin.elements.driverPin.style.backgroundColor = '#22aa22';
+
this.updateFeedback(`Pin set at the shear line! (${this.lockState.pinsSet}/${this.pinCount})`);
- this.updatePinVisual(pin);
+
+ // Move the tension wrench further right based on progress
+ const progressPercentage = 25 + (this.lockState.pinsSet / this.pinCount * 75);
+ this.updateTensionPosition(progressPercentage);
// Update progress
this.updateProgress(this.lockState.pinsSet, this.pinCount);
@@ -5264,26 +5578,29 @@
}
// Update which pin is binding next
- this.updatePinBindings();
+ this.updateBindingPins();
} else {
// Pin not at the correct height, drops back down
- this.dropPin(pin);
+ this.animatePinDrop(pin);
- if (pin.currentHeight > pin.setPoint + 0.1) {
- this.updateFeedback("Pin was pushed too far and dropped");
+ if (currentLiftInPixels > pin.distanceToShearLine) {
+ this.updateFeedback("Pin was pushed too far past the shear line");
} else {
- this.updateFeedback("Pin wasn't lifted high enough and dropped");
+ this.updateFeedback("Pin wasn't lifted high enough to reach the shear line");
}
}
}
- // Animate a pin dropping down
- dropPin(pin) {
+ // Define the animatePinDrop method to replace the missing dropPin method
+ animatePinDrop(pin) {
// Don't drop pins that are already set
if (pin.isSet) return;
+ // Calculate drop speed based on how high the pin is
+ const dropSpeed = 0.05 + (pin.currentHeight * 0.1);
+
const dropInterval = setInterval(() => {
- pin.currentHeight -= 0.05;
+ pin.currentHeight -= dropSpeed;
if (pin.currentHeight <= 0) {
pin.currentHeight = 0;
@@ -5294,47 +5611,70 @@
}, 10);
}
- // Update a single pin's visual appearance
+ // Update pin visual based on current height
updatePinVisual(pin) {
- // Update key pin and driver pin heights
- pin.elements.keyPin.style.height = `${pin.currentHeight * 40}px`;
- pin.elements.driverPin.style.bottom = `${pin.currentHeight * 40 + 1}px`;
- pin.elements.spring.style.height = `${20 - pin.currentHeight * 5}px`;
+ // Skip visualization update if the pin is set
+ if (pin.isSet) return;
- // Show set state
- pin.elements.container.classList.toggle('set', pin.isSet);
+ // Calculate the lift in pixels based on the current progress (0-1) times the distance to the shear line
+ const translateY = pin.currentHeight * pin.distanceToShearLine * -1; // Negative because we're moving up
+
+ // Move the entire pin assembly up
+ pin.elements.assembly.style.transform = `translateY(${translateY}px)`;
}
- // Update which pins are binding based on binding order
- updatePinBindings() {
- if (!this.lockState.tensionApplied) {
- // No binding if no tension
- this.pins.forEach(pin => {
- pin.elements.container.classList.remove('binding');
- });
+ // Pin-lifting logic with realistic physics
+ liftPin() {
+ if (!this.lockState.currentPin || !this.gameState.isActive ||
+ !this.lockState.tensionApplied || !this.gameState.mouseDown) {
return;
}
- // Find the next unset pin in binding order
- let bindingPinFound = false;
+ const pin = this.lockState.currentPin;
- for (let order = 0; order < this.pinCount; order++) {
- const nextPin = this.pins.find(p => p.binding === order && !p.isSet);
- if (nextPin) {
- // Mark this pin as binding
- this.pins.forEach(pin => {
- pin.elements.container.classList.toggle('binding', pin.index === nextPin.index);
- });
- bindingPinFound = true;
- break;
+ // Add realistic resistance based on binding state
+ let liftAmount = 0;
+
+ // Only binding pins can be lifted effectively
+ if (!this.shouldPinBind(pin)) {
+ // Non-binding pins can be lifted, but with resistance and limited height
+ liftAmount = 0.01;
+ if (pin.currentHeight > 0.3) {
+ liftAmount = 0.005; // Increased resistance at higher positions
}
+ } else {
+ // Binding pins lift more smoothly but still have some resistance
+ liftAmount = 0.03 - (pin.resistance * pin.currentHeight);
+
+ // Add slight random variation to simulate realistic feel
+ liftAmount += (Math.random() * 0.01 - 0.005);
}
- // If no binding pin was found (all pins set), remove binding class from all
- if (!bindingPinFound) {
- this.pins.forEach(pin => {
- pin.elements.container.classList.remove('binding');
- });
+ // Update pin height
+ pin.currentHeight += liftAmount;
+
+ // Cap at maximum height
+ if (pin.currentHeight > 1.2) { // Allow overshooting the shear line a bit
+ pin.currentHeight = 1.2;
+ }
+
+ // Update visual
+ this.updatePinVisual(pin);
+
+ // Add subtle feedback when pin is near the shear line
+ const currentLiftInPixels = pin.currentHeight * pin.distanceToShearLine;
+ const distanceToShearLine = Math.abs(currentLiftInPixels - pin.distanceToShearLine);
+
+ if (distanceToShearLine < 5) {
+ // Pin is close to the shear line
+ pin.elements.container.style.boxShadow = "0 0 5px #ffffff";
+ } else {
+ pin.elements.container.style.boxShadow = "";
+ }
+
+ // Continue lifting while mouse is down
+ if (this.gameState.mouseDown) {
+ requestAnimationFrame(() => this.liftPin());
}
}
@@ -5438,6 +5778,39 @@
}
return array;
}
+
+ // Add new method for updating binding pins
+ updateBindingPins() {
+ if (!this.lockState.tensionApplied) {
+ // No binding if no tension
+ this.pins.forEach(pin => {
+ pin.elements.container.classList.remove('binding');
+ });
+ return;
+ }
+
+ // Find the next unset pin in binding order
+ let bindingPinFound = false;
+
+ for (let order = 0; order < this.pinCount; order++) {
+ const nextPin = this.pins.find(p => p.binding === order && !p.isSet);
+ if (nextPin) {
+ // Mark this pin as binding
+ this.pins.forEach(pin => {
+ pin.elements.container.classList.toggle('binding', pin.index === nextPin.index);
+ });
+ bindingPinFound = true;
+ break;
+ }
+ }
+
+ // If no binding pin was found (all pins set), remove binding class from all
+ if (!bindingPinFound) {
+ this.pins.forEach(pin => {
+ pin.elements.container.classList.remove('binding');
+ });
+ }
+ }
}
// Register the lockpicking minigame with the framework
@@ -6666,15 +7039,6 @@
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 {