From 0b9f1cb2de3c5c027a19ae63ab22e167dd401d7c Mon Sep 17 00:00:00 2001 From: "Z. Cliffe Schreuders" Date: Mon, 27 Oct 2025 15:21:22 +0000 Subject: [PATCH] Integrate LockGraphics module into lockpicking minigame: Initialize LockGraphics in the LockpickingMinigamePhaser class for enhanced graphical representation. --- js/minigames/lockpicking/lock-graphics.js | 336 ++++++++++++++++++ .../lockpicking/lockpicking-game-phaser.js | 325 +---------------- 2 files changed, 343 insertions(+), 318 deletions(-) create mode 100644 js/minigames/lockpicking/lock-graphics.js diff --git a/js/minigames/lockpicking/lock-graphics.js b/js/minigames/lockpicking/lock-graphics.js new file mode 100644 index 0000000..134dbf5 --- /dev/null +++ b/js/minigames/lockpicking/lock-graphics.js @@ -0,0 +1,336 @@ + +/** + * LockGraphics + * + * Extracted from lockpicking-game-phaser.js + * Instantiate with: new LockGraphics(this) + * + * All 'this' references replaced with 'this.parent' to access parent instance state: + * - this.parent.pins (array of pin objects) + * - this.parent.scene (Phaser scene) + * - this.parent.lockId (lock identifier) + * - this.parent.lockState (lock state object) + * etc. + */ +export class LockGraphics { + + constructor(parent) { + this.parent = parent; + } + + createLockBackground() { + const graphics = this.parent.scene.add.graphics(); + graphics.lineStyle(2, 0x666666); + graphics.strokeRect(100, 50, 400, 300); + graphics.fillStyle(0x555555); + graphics.fillRect(100, 50, 400, 300); + + // Create key cylinder - rectangle from shear line to near bottom + this.parent.cylinderGraphics = this.parent.scene.add.graphics(); + this.parent.cylinderGraphics.fillStyle(0xcd7f32); // Bronze color + this.parent.cylinderGraphics.fillRect(100, 155, 400, 180); // From shear line (y=155) to near bottom (y=335) + this.parent.cylinderGraphics.lineStyle(1, 0x8b4513); // Darker bronze border + this.parent.cylinderGraphics.strokeRect(100, 155, 400, 180); + + // Create keyway - space where key would enter (moved higher to align with shear line) + this.parent.keywayGraphics = this.parent.scene.add.graphics(); + this.parent.keywayGraphics.fillStyle(0x2a2a2a); // Dark gray for keyway + this.parent.keywayGraphics.fillRect(100, 170, 400, 120); // Moved higher (y=170) and increased height (120) + this.parent.keywayGraphics.lineStyle(1, 0x1a1a1a); // Darker border + this.parent.keywayGraphics.strokeRect(100, 170, 400, 120); + } + + createTensionWrench() { + const wrenchX = 80; // Position to the left of the lock + const wrenchY = 160; // Position down by half the arm width (5 units) from shear line + + // Create tension wrench container + this.parent.tensionWrench = this.parent.scene.add.container(wrenchX, wrenchY); + + // Create L-shaped tension wrench graphics (25% larger) + this.parent.wrenchGraphics = this.parent.scene.add.graphics(); + this.parent.wrenchGraphics.fillStyle(0x888888); + + // Long vertical arm (left side of L) - extended above the lock + this.parent.wrenchGraphics.fillRect(0, -120, 10, 170); + + // Short horizontal arm (bottom of L) extending into keyway - 25% larger + this.parent.wrenchGraphics.fillRect(0, 40, 37.5, 10); + + this.parent.tensionWrench.add(this.parent.wrenchGraphics); + + // 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.parent.tensionWrench.setInteractive(new Phaser.Geom.Rectangle(-12.5, -138.75, 60, 268.75), Phaser.Geom.Rectangle.Contains); + + // Add text + const wrenchText = this.parent.scene.add.text(-10, 58, 'Tension Wrench', { + fontSize: '18px', + fontFamily: 'VT323', + fill: '#00ff00', + fontWeight: 'bold' + }); + wrenchText.setOrigin(0.5); + wrenchText.setDepth(100); // Bring to front + this.parent.tensionWrench.add(wrenchText); + + // Store reference to wrench text for hiding + this.parent.wrenchText = wrenchText; + + // Add click handler + this.parent.tensionWrench.on('pointerdown', () => { + this.parent.lockState.tensionApplied = !this.parent.lockState.tensionApplied; + + // Play tension sound + if (this.parent.sounds.tension) { + this.parent.sounds.tension.play(); + if (typeof navigator !== 'undefined' && navigator.vibrate) { + navigator.vibrate([50]); + } + } + + if (this.parent.lockState.tensionApplied) { + this.parent.wrenchGraphics.clear(); + this.parent.wrenchGraphics.fillStyle(0x00ff00); + + // Long vertical arm (left side of L) - same dimensions as inactive + this.parent.wrenchGraphics.fillRect(0, -120, 10, 170); + + // Short horizontal arm (bottom of L) extending into keyway - same dimensions as inactive + this.parent.wrenchGraphics.fillRect(0, 40, 37.5, 10); + + this.parent.updateFeedback("Tension applied. Only the binding pin can be set - others will fall back down."); + } else { + this.parent.wrenchGraphics.clear(); + this.parent.wrenchGraphics.fillStyle(0x888888); + + // Long vertical arm (left side of L) - same dimensions as active + this.parent.wrenchGraphics.fillRect(0, -120, 10, 170); + + // Short horizontal arm (bottom of L) extending into keyway - same dimensions as active + this.parent.wrenchGraphics.fillRect(0, 40, 37.5, 10); + + this.parent.updateFeedback("Tension released. All pins will fall back down."); + + // Play reset sound + if (this.parent.sounds.reset) { + this.parent.sounds.reset.play(); + } + + // Reset ALL pins when tension is released (including set and overpicked ones) + this.parent.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.parent.lockState.pinsSet = 0; + } + + this.parent.updateBindingPins(); + }); + } + + createHookPick() { + // Create hook pick that comes in from the left side + // Handle is off-screen, long horizontal arm curves up to bottom of key pin 1 + + // Calculate pin spacing and margin (same as createPins) + const pinSpacing = 400 / (this.parent.pinCount + 1); + const margin = pinSpacing * 0.75; // 25% smaller margins + + // Hook target coordinates (can be easily changed to point at any pin or coordinate) + const targetX = 100 + margin + (this.parent.pinCount - 1) * pinSpacing; // Last pin X position + const targetY = -50 + this.parent.pins[this.parent.pinCount - 1].driverPinLength + this.parent.pins[this.parent.pinCount - 1].keyPinLength; // Last pin bottom Y + + // Hook should start 2/3rds down the keyway (keyway is from y=200 to y=290, so 2/3rds down is y=260) + const keywayStartY = 200; + const keywayEndY = 290; + const keywayHeight = keywayEndY - keywayStartY; + const hookEntryY = keywayStartY + (keywayHeight * 2/3); // 2/3rds down the keyway + + // Hook pick dimensions and positioning + const handleWidth = 20; + const handleHeight = 240; // 4x longer (was 60) + const armWidth = 8; + const armLength = 140; // Horizontal arm length + + // Start position (handle off-screen to the left) + const startX = -120; // Handle starts further off-screen (was -30) + const startY = hookEntryY; // Handle center Y position (2/3rds down keyway) + + // Calculate hook dimensions based on target + const hookStartX = startX + handleWidth + armLength; + const hookStartY = startY; + + // Hook segments configuration + const segmentSize = 8; + const diagonalSegments = 2; // Number of diagonal segments + const verticalSegments = 3; // Number of vertical segments (increased by 1) + const segmentStep = 8; // Distance between segment centers + + // Calculate total hook height needed + const totalHookHeight = (diagonalSegments + verticalSegments) * segmentStep; + + // Calculate required horizontal length to reach target + const requiredHorizontalLength = targetX - hookStartX - totalHookHeight + 48; // Add 48px to reach target (24px + 24px further right) + + // Adjust horizontal length to align with target + const curveStartX = hookStartX + requiredHorizontalLength; + + // Calculate the tip position (end of the hook) + const tipX = curveStartX + (diagonalSegments * segmentStep); + const tipY = hookStartY - (diagonalSegments * segmentStep) - (verticalSegments * segmentStep); + + // Create a container for the hook pick with rotation center at the tip + this.parent.hookGroup = this.parent.scene.add.container(0, 0); + this.parent.hookGroup.x = tipX; + this.parent.hookGroup.y = tipY; + + // Create graphics for hook pick (relative to group center) + const hookPickGraphics = this.parent.scene.add.graphics(); + hookPickGraphics.fillStyle(0x888888); // Gray color for the pick + hookPickGraphics.lineStyle(2, 0x888888); // Darker border + + // Calculate positions relative to group center (tip position) + const relativeStartX = startX - tipX; + const relativeStartY = startY - tipY; + const relativeHookStartX = hookStartX - tipX; + const relativeCurveStartX = curveStartX - tipX; + + // Draw the handle (off-screen) + hookPickGraphics.fillRect(relativeStartX, relativeStartY - handleHeight/2, handleWidth, handleHeight); + hookPickGraphics.strokeRect(relativeStartX, relativeStartY - handleHeight/2, handleWidth, handleHeight); + + // Draw the horizontal arm (extends from handle to near the lock) + const armStartX = relativeStartX + handleWidth; + const armEndX = armStartX + armLength; + hookPickGraphics.fillRect(armStartX, relativeStartY - armWidth/2, armLength, armWidth); + hookPickGraphics.strokeRect(armStartX, relativeStartY - armWidth/2, armLength, armWidth); + + // Draw horizontal part to curve start + hookPickGraphics.fillRect(relativeHookStartX, relativeStartY - armWidth/2, relativeCurveStartX - relativeHookStartX, armWidth); + hookPickGraphics.strokeRect(relativeHookStartX, relativeStartY - armWidth/2, relativeCurveStartX - relativeHookStartX, armWidth); + + // Draw the hook segments: diagonal then vertical + // First 2 segments: up and right (2x scale) + for (let i = 0; i < diagonalSegments; i++) { + const x = relativeCurveStartX + (i * segmentStep); // Move right 8px each segment + const y = relativeStartY - (i * segmentStep); // Move up 8px each segment + hookPickGraphics.fillRect(x - armWidth/2, y - segmentSize/2, armWidth, segmentSize); + hookPickGraphics.strokeRect(x - armWidth/2, y - segmentSize/2, armWidth, segmentSize); + } + + // Next 3 segments: straight up (increased by 1 segment) + for (let i = 0; i < verticalSegments; i++) { + const x = relativeCurveStartX + (diagonalSegments * segmentStep); // Stay at the rightmost position from diagonal segments + const y = relativeStartY - (diagonalSegments * segmentStep) - (i * segmentStep); // Continue moving up from where we left off + hookPickGraphics.fillRect(x - armWidth/2, y - segmentSize/2, armWidth, segmentSize); + hookPickGraphics.strokeRect(x - armWidth/2, y - segmentSize/2, armWidth, segmentSize); + } + + // Add graphics to container + this.parent.hookGroup.add(hookPickGraphics); + + // Add hook pick label + const hookPickLabel = this.parent.scene.add.text(-10, 85, 'Hook Pick', { + fontSize: '18px', + fontFamily: 'VT323', + fill: '#00ff00', + fontWeight: 'bold' + }); + hookPickLabel.setOrigin(0.5); + hookPickLabel.setDepth(100); // Bring to front + this.parent.tensionWrench.add(hookPickLabel); + + // Store reference to hook pick label for hiding + this.parent.hookPickLabel = hookPickLabel; + + // Debug logging + console.log('Hook positioning debug:', { + targetX, + targetY, + hookStartX, + hookStartY, + tipX, + tipY, + totalHookHeight, + requiredHorizontalLength, + curveStartX, + pinCount: this.parent.pinCount, + pinSpacing, + margin + }); + + // Store reference to hook pick for animations + this.parent.hookPickGraphics = hookPickGraphics; + + // Store hook configuration for dynamic updates + this.parent.hookConfig = { + targetPin: this.parent.pinCount - 1, // Default to last pin (should be 4 for 5 pins) + lastTargetedPin: this.parent.pinCount - 1, // Track the last pin that was targeted + baseTargetX: targetX, + baseTargetY: targetY, + hookStartX: hookStartX, + hookStartY: hookStartY, + diagonalSegments: diagonalSegments, + verticalSegments: verticalSegments, + segmentStep: segmentStep, + segmentSize: segmentSize, + armWidth: armWidth, + curveStartX: curveStartX, + tipX: tipX, + tipY: tipY, + rotationCenterX: tipX, + rotationCenterY: tipY + }; + + console.log('Hook config initialized - targetPin:', this.parent.hookConfig.targetPin, 'pinCount:', this.parent.pinCount); + } + +} diff --git a/js/minigames/lockpicking/lockpicking-game-phaser.js b/js/minigames/lockpicking/lockpicking-game-phaser.js index d4a864e..d720d02 100644 --- a/js/minigames/lockpicking/lockpicking-game-phaser.js +++ b/js/minigames/lockpicking/lockpicking-game-phaser.js @@ -1,5 +1,6 @@ import { MinigameScene } from '../framework/base-minigame.js'; import { LockConfiguration } from './lock-configuration.js'; +import { LockGraphics } from './lock-graphics.js'; // Phaser Lockpicking Minigame Scene implementation export class LockpickingMinigamePhaser extends MinigameScene { @@ -99,6 +100,9 @@ export class LockpickingMinigamePhaser extends MinigameScene { // Initialize lock configuration module this.lockConfig = new LockConfiguration(this); + + // Initialize lock graphics module + this.lockGraphics = new LockGraphics(this); } // Method to get the lock's pin configuration for key generation @@ -275,10 +279,10 @@ export class LockpickingMinigamePhaser extends MinigameScene { self.sounds.wrong = this.sound.add('lockpick_wrong'); // Create game elements - self.createLockBackground(); - self.createTensionWrench(); + self.lockGraphics.createLockBackground(); + self.lockGraphics.createTensionWrench(); self.createPins(); - self.createHookPick(); + self.lockGraphics.createHookPick(); self.createShearLine(); // Create key if in key mode and not skipping starting key @@ -360,321 +364,6 @@ export class LockpickingMinigamePhaser extends MinigameScene { - createLockBackground() { - const graphics = this.scene.add.graphics(); - graphics.lineStyle(2, 0x666666); - graphics.strokeRect(100, 50, 400, 300); - graphics.fillStyle(0x555555); - graphics.fillRect(100, 50, 400, 300); - - // Create key cylinder - rectangle from shear line to near bottom - this.cylinderGraphics = this.scene.add.graphics(); - this.cylinderGraphics.fillStyle(0xcd7f32); // Bronze color - this.cylinderGraphics.fillRect(100, 155, 400, 180); // From shear line (y=155) to near bottom (y=335) - this.cylinderGraphics.lineStyle(1, 0x8b4513); // Darker bronze border - this.cylinderGraphics.strokeRect(100, 155, 400, 180); - - // Create keyway - space where key would enter (moved higher to align with shear line) - this.keywayGraphics = this.scene.add.graphics(); - this.keywayGraphics.fillStyle(0x2a2a2a); // Dark gray for keyway - this.keywayGraphics.fillRect(100, 170, 400, 120); // Moved higher (y=170) and increased height (120) - this.keywayGraphics.lineStyle(1, 0x1a1a1a); // Darker border - this.keywayGraphics.strokeRect(100, 170, 400, 120); - } - - createTensionWrench() { - const wrenchX = 80; // Position to the left of the lock - const wrenchY = 160; // Position down by half the arm width (5 units) from shear line - - // Create tension wrench container - this.tensionWrench = this.scene.add.container(wrenchX, wrenchY); - - // Create L-shaped tension wrench graphics (25% larger) - this.wrenchGraphics = this.scene.add.graphics(); - this.wrenchGraphics.fillStyle(0x888888); - - // Long vertical arm (left side of L) - extended above the lock - this.wrenchGraphics.fillRect(0, -120, 10, 170); - - // Short horizontal arm (bottom of L) extending into keyway - 25% larger - this.wrenchGraphics.fillRect(0, 40, 37.5, 10); - - this.tensionWrench.add(this.wrenchGraphics); - - // 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: '18px', - fontFamily: 'VT323', - fill: '#00ff00', - fontWeight: 'bold' - }); - wrenchText.setOrigin(0.5); - wrenchText.setDepth(100); // Bring to front - this.tensionWrench.add(wrenchText); - - // Store reference to wrench text for hiding - this.wrenchText = wrenchText; - - // Add click handler - this.tensionWrench.on('pointerdown', () => { - this.lockState.tensionApplied = !this.lockState.tensionApplied; - - // Play tension sound - if (this.sounds.tension) { - this.sounds.tension.play(); - if (typeof navigator !== 'undefined' && navigator.vibrate) { - navigator.vibrate([50]); - } - } - - 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(); - }); - } - - createHookPick() { - // Create hook pick that comes in from the left side - // Handle is off-screen, long horizontal arm curves up to bottom of key pin 1 - - // Calculate pin spacing and margin (same as createPins) - const pinSpacing = 400 / (this.pinCount + 1); - const margin = pinSpacing * 0.75; // 25% smaller margins - - // Hook target coordinates (can be easily changed to point at any pin or coordinate) - const targetX = 100 + margin + (this.pinCount - 1) * pinSpacing; // Last pin X position - const targetY = -50 + this.pins[this.pinCount - 1].driverPinLength + this.pins[this.pinCount - 1].keyPinLength; // Last pin bottom Y - - // Hook should start 2/3rds down the keyway (keyway is from y=200 to y=290, so 2/3rds down is y=260) - const keywayStartY = 200; - const keywayEndY = 290; - const keywayHeight = keywayEndY - keywayStartY; - const hookEntryY = keywayStartY + (keywayHeight * 2/3); // 2/3rds down the keyway - - // Hook pick dimensions and positioning - const handleWidth = 20; - const handleHeight = 240; // 4x longer (was 60) - const armWidth = 8; - const armLength = 140; // Horizontal arm length - - // Start position (handle off-screen to the left) - const startX = -120; // Handle starts further off-screen (was -30) - const startY = hookEntryY; // Handle center Y position (2/3rds down keyway) - - // Calculate hook dimensions based on target - const hookStartX = startX + handleWidth + armLength; - const hookStartY = startY; - - // Hook segments configuration - const segmentSize = 8; - const diagonalSegments = 2; // Number of diagonal segments - const verticalSegments = 3; // Number of vertical segments (increased by 1) - const segmentStep = 8; // Distance between segment centers - - // Calculate total hook height needed - const totalHookHeight = (diagonalSegments + verticalSegments) * segmentStep; - - // Calculate required horizontal length to reach target - const requiredHorizontalLength = targetX - hookStartX - totalHookHeight + 48; // Add 48px to reach target (24px + 24px further right) - - // Adjust horizontal length to align with target - const curveStartX = hookStartX + requiredHorizontalLength; - - // Calculate the tip position (end of the hook) - const tipX = curveStartX + (diagonalSegments * segmentStep); - const tipY = hookStartY - (diagonalSegments * segmentStep) - (verticalSegments * segmentStep); - - // Create a container for the hook pick with rotation center at the tip - this.hookGroup = this.scene.add.container(0, 0); - this.hookGroup.x = tipX; - this.hookGroup.y = tipY; - - // Create graphics for hook pick (relative to group center) - const hookPickGraphics = this.scene.add.graphics(); - hookPickGraphics.fillStyle(0x888888); // Gray color for the pick - hookPickGraphics.lineStyle(2, 0x888888); // Darker border - - // Calculate positions relative to group center (tip position) - const relativeStartX = startX - tipX; - const relativeStartY = startY - tipY; - const relativeHookStartX = hookStartX - tipX; - const relativeCurveStartX = curveStartX - tipX; - - // Draw the handle (off-screen) - hookPickGraphics.fillRect(relativeStartX, relativeStartY - handleHeight/2, handleWidth, handleHeight); - hookPickGraphics.strokeRect(relativeStartX, relativeStartY - handleHeight/2, handleWidth, handleHeight); - - // Draw the horizontal arm (extends from handle to near the lock) - const armStartX = relativeStartX + handleWidth; - const armEndX = armStartX + armLength; - hookPickGraphics.fillRect(armStartX, relativeStartY - armWidth/2, armLength, armWidth); - hookPickGraphics.strokeRect(armStartX, relativeStartY - armWidth/2, armLength, armWidth); - - // Draw horizontal part to curve start - hookPickGraphics.fillRect(relativeHookStartX, relativeStartY - armWidth/2, relativeCurveStartX - relativeHookStartX, armWidth); - hookPickGraphics.strokeRect(relativeHookStartX, relativeStartY - armWidth/2, relativeCurveStartX - relativeHookStartX, armWidth); - - // Draw the hook segments: diagonal then vertical - // First 2 segments: up and right (2x scale) - for (let i = 0; i < diagonalSegments; i++) { - const x = relativeCurveStartX + (i * segmentStep); // Move right 8px each segment - const y = relativeStartY - (i * segmentStep); // Move up 8px each segment - hookPickGraphics.fillRect(x - armWidth/2, y - segmentSize/2, armWidth, segmentSize); - hookPickGraphics.strokeRect(x - armWidth/2, y - segmentSize/2, armWidth, segmentSize); - } - - // Next 3 segments: straight up (increased by 1 segment) - for (let i = 0; i < verticalSegments; i++) { - const x = relativeCurveStartX + (diagonalSegments * segmentStep); // Stay at the rightmost position from diagonal segments - const y = relativeStartY - (diagonalSegments * segmentStep) - (i * segmentStep); // Continue moving up from where we left off - hookPickGraphics.fillRect(x - armWidth/2, y - segmentSize/2, armWidth, segmentSize); - hookPickGraphics.strokeRect(x - armWidth/2, y - segmentSize/2, armWidth, segmentSize); - } - - // Add graphics to container - this.hookGroup.add(hookPickGraphics); - - // Add hook pick label - const hookPickLabel = this.scene.add.text(-10, 85, 'Hook Pick', { - fontSize: '18px', - fontFamily: 'VT323', - fill: '#00ff00', - fontWeight: 'bold' - }); - hookPickLabel.setOrigin(0.5); - hookPickLabel.setDepth(100); // Bring to front - this.tensionWrench.add(hookPickLabel); - - // Store reference to hook pick label for hiding - this.hookPickLabel = hookPickLabel; - - // Debug logging - console.log('Hook positioning debug:', { - targetX, - targetY, - hookStartX, - hookStartY, - tipX, - tipY, - totalHookHeight, - requiredHorizontalLength, - curveStartX, - pinCount: this.pinCount, - pinSpacing, - margin - }); - - // Store reference to hook pick for animations - this.hookPickGraphics = hookPickGraphics; - - // Store hook configuration for dynamic updates - this.hookConfig = { - targetPin: this.pinCount - 1, // Default to last pin (should be 4 for 5 pins) - lastTargetedPin: this.pinCount - 1, // Track the last pin that was targeted - baseTargetX: targetX, - baseTargetY: targetY, - hookStartX: hookStartX, - hookStartY: hookStartY, - diagonalSegments: diagonalSegments, - verticalSegments: verticalSegments, - segmentStep: segmentStep, - segmentSize: segmentSize, - armWidth: armWidth, - curveStartX: curveStartX, - tipX: tipX, - tipY: tipY, - rotationCenterX: tipX, - rotationCenterY: tipY - }; - - console.log('Hook config initialized - targetPin:', this.hookConfig.targetPin, 'pinCount:', this.pinCount); - } - generateKeyDataFromPins() { // Generate key cuts based on actual pin heights // Calculate cut depths so that when key is inserted, pins align at shear line