Files
novafarma/src/systems/GamepadController.js
David Kotnik afab1ecc09 🏗️💎 MASTER SYSTEM ARCHITECTURE - 100% COMPLETE!
 ALL 6 SYSTEMS IMPLEMENTED (1,830 lines):

1️⃣ GAMEPAD CONTROLLER (200 lines) 
- Xbox/PS controller support
- Left stick → Longboard movement
- Buttons: A (interact), X (vape), Y (whistle), B (menu)
- Haptic feedback: collision, zombie, vape rumble
- Auto-detect connection

2️⃣ VIP MANAGER (250 lines) 
- First 20 buyers → Gronk exclusive
- Purchase order tracking
- Founder badge system
- Streamer access keys
- Steam/Itch API stubs ready

3️⃣ GRONK STATS (180 lines) 
- Level 1-10 progression
- XP from vape usage (+10 each)
- Stats scale per level:
  - Cloud size: +15%
  - Duration: +0.5s
  - Shield: +20 HP
  - Speed: +5%
  - Cooldown: -0.5s

4️⃣ SUSI COMPANION (350 lines) 
- Follow Kai (50px distance)
- Whistle response (Y button)
- Memory tracking AI
- Bark animations + sounds
- State machine: follow/track/sit/sleep

5️⃣ SAVE/LOAD + AGING (400 lines) 
- Complete save structure
- Auto-save every 5 min
- Export/import saves
- Aging engine 9 stages (14-60 years)
- Memory-based progression
- Sprite auto-switch

6️⃣ NOIR CITY ATMOSPHERE (450 lines) 
- Stray cats (3-5) - run from longboard
- Stray dogs (2-3) - bark from shadows
- Ambient sounds (city, wind, distant)
- Dust particles, blowing trash
- Flickering streetlights

📊 TECHNICAL:
- All systems use singleton pattern
- LocalStorage persistence
- Event-driven architecture
- Phaser 3 compatible
- 16:9 centered layout

🎮 INTEGRATION READY:
- Full GameScene integration guide
- All imports prepared
- Event listeners documented
- Usage examples provided

PROJECT IS NOW 'BETONIRAN' (CONCRETE-SOLID)! 🏗️

Files:
- src/systems/GamepadController.js
- src/systems/VIPManager.js
- src/systems/GronkStats.js
- src/systems/SusiCompanion.js
- src/systems/SaveLoadSystem.js
- src/systems/NoirCitySystem.js
- MASTER_SYSTEM_ARCHITECTURE_COMPLETE.md
2026-01-10 19:13:15 +01:00

179 lines
4.6 KiB
JavaScript

/**
* 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;