/** * GAMEPAD CONTROLLER SYSTEM * Xbox/PlayStation controller support with haptic feedback */ class GamepadController { constructor(scene) { this.scene = scene; this.gamepad = null; this.deadzone = 0.15; // Button mappings (Xbox layout) this.buttons = { A: 0, // Interact B: 1, // Menu X: 2, // Gronk Vape Shield Y: 3, // Whistle to Susi LB: 4, RB: 5, LT: 6, RT: 7, SELECT: 8, START: 9, L_STICK: 10, R_STICK: 11, DPAD_UP: 12, DPAD_DOWN: 13, DPAD_LEFT: 14, DPAD_RIGHT: 15 }; this.lastVibration = 0; this.init(); } init() { // Check for gamepad connection window.addEventListener('gamepadconnected', (e) => { console.log('🎮 Gamepad connected:', e.gamepad.id); this.gamepad = e.gamepad; this.scene.events.emit('gamepad-connected', e.gamepad); }); window.addEventListener('gamepaddisconnected', (e) => { console.log('🎮 Gamepad disconnected'); this.gamepad = null; this.scene.events.emit('gamepad-disconnected'); }); } update() { // Refresh gamepad state const gamepads = navigator.getGamepads(); this.gamepad = gamepads[0] || gamepads[1] || gamepads[2] || gamepads[3]; if (!this.gamepad) return null; return { leftStick: this.getLeftStick(), rightStick: this.getRightStick(), buttons: this.getButtons() }; } getLeftStick() { if (!this.gamepad) return { x: 0, y: 0 }; let x = this.gamepad.axes[0]; let y = this.gamepad.axes[1]; // Apply deadzone if (Math.abs(x) < this.deadzone) x = 0; if (Math.abs(y) < this.deadzone) y = 0; return { x, y }; } getRightStick() { if (!this.gamepad) return { x: 0, y: 0 }; let x = this.gamepad.axes[2]; let y = this.gamepad.axes[3]; // Apply deadzone if (Math.abs(x) < this.deadzone) x = 0; if (Math.abs(y) < this.deadzone) y = 0; return { x, y }; } getButtons() { if (!this.gamepad) return {}; const pressed = {}; Object.keys(this.buttons).forEach(key => { const index = this.buttons[key]; pressed[key] = this.gamepad.buttons[index]?.pressed || false; }); return pressed; } isButtonPressed(buttonName) { if (!this.gamepad) return false; const index = this.buttons[buttonName]; return this.gamepad.buttons[index]?.pressed || false; } isButtonJustPressed(buttonName) { // Track button state changes for single press detection if (!this.gamepad) return false; const index = this.buttons[buttonName]; const pressed = this.gamepad.buttons[index]?.pressed || false; if (!this.lastButtonState) this.lastButtonState = {}; const wasPressed = this.lastButtonState[buttonName] || false; this.lastButtonState[buttonName] = pressed; return pressed && !wasPressed; } /** * HAPTIC FEEDBACK (Rumble) * intensity: 0.0 - 1.0 * duration: milliseconds */ vibrate(intensity = 0.5, duration = 200) { if (!this.gamepad || !this.gamepad.vibrationActuator) return; // Prevent spam const now = Date.now(); if (now - this.lastVibration < 100) return; this.lastVibration = now; try { this.gamepad.vibrationActuator.playEffect('dual-rumble', { startDelay: 0, duration: duration, weakMagnitude: intensity * 0.5, strongMagnitude: intensity }); } catch (e) { console.warn('Vibration not supported:', e); } } /** * COLLISION RUMBLE * Light vibration for hitting obstacles */ collisionRumble() { this.vibrate(0.3, 150); } /** * ZOMBIE ENCOUNTER RUMBLE * Heavy vibration for enemy appearance */ zombieRumble() { this.vibrate(0.8, 300); } /** * VAPE SHIELD RUMBLE * Medium pulse for Gronk's ability */ vapeShieldRumble() { this.vibrate(0.5, 200); } destroy() { window.removeEventListener('gamepadconnected', this.init); window.removeEventListener('gamepaddisconnected', this.init); } } export default GamepadController;