✅ 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
179 lines
4.6 KiB
JavaScript
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;
|