acesesibiliti
This commit is contained in:
525
src/systems/InputRemappingSystem.js
Normal file
525
src/systems/InputRemappingSystem.js
Normal file
@@ -0,0 +1,525 @@
|
||||
/**
|
||||
* INPUT REMAPPING SYSTEM
|
||||
* Allows players to customize keyboard and controller bindings
|
||||
* Supports multiple control profiles and one-handed layouts
|
||||
*/
|
||||
class InputRemappingSystem {
|
||||
constructor(scene) {
|
||||
this.scene = scene;
|
||||
this.enabled = true;
|
||||
|
||||
// Default key bindings
|
||||
this.defaultBindings = {
|
||||
// Movement
|
||||
'move_up': ['W', 'UP'],
|
||||
'move_down': ['S', 'DOWN'],
|
||||
'move_left': ['A', 'LEFT'],
|
||||
'move_right': ['D', 'RIGHT'],
|
||||
|
||||
// Actions
|
||||
'interact': ['E', 'SPACE'],
|
||||
'attack': ['MOUSE_LEFT', 'J'],
|
||||
'use_tool': ['MOUSE_LEFT', 'J'],
|
||||
'cancel': ['ESC', 'X'],
|
||||
'confirm': ['ENTER', 'E'],
|
||||
|
||||
// Inventory & UI
|
||||
'inventory': ['I', 'TAB'],
|
||||
'crafting': ['C'],
|
||||
'map': ['M'],
|
||||
'quest_log': ['Q'],
|
||||
'pause': ['ESC', 'P'],
|
||||
|
||||
// Tools
|
||||
'tool_1': ['1'],
|
||||
'tool_2': ['2'],
|
||||
'tool_3': ['3'],
|
||||
'tool_4': ['4'],
|
||||
'tool_5': ['5'],
|
||||
|
||||
// Quick actions
|
||||
'quick_heal': ['H'],
|
||||
'quick_eat': ['F'],
|
||||
'sprint': ['SHIFT'],
|
||||
'crouch': ['CTRL'],
|
||||
|
||||
// Camera
|
||||
'zoom_in': ['PLUS', 'MOUSE_WHEEL_UP'],
|
||||
'zoom_out': ['MINUS', 'MOUSE_WHEEL_DOWN'],
|
||||
'camera_reset': ['R']
|
||||
};
|
||||
|
||||
// Controller bindings (Xbox/PlayStation layout)
|
||||
this.controllerBindings = {
|
||||
'move': 'LEFT_STICK',
|
||||
'camera': 'RIGHT_STICK',
|
||||
'interact': 'A', // Xbox A / PS Cross
|
||||
'attack': 'X', // Xbox X / PS Square
|
||||
'cancel': 'B', // Xbox B / PS Circle
|
||||
'inventory': 'Y', // Xbox Y / PS Triangle
|
||||
'sprint': 'LB',
|
||||
'use_tool': 'RT',
|
||||
'quick_menu': 'LT',
|
||||
'pause': 'START',
|
||||
'map': 'SELECT'
|
||||
};
|
||||
|
||||
// Control profiles
|
||||
this.profiles = {
|
||||
'default': JSON.parse(JSON.stringify(this.defaultBindings)),
|
||||
'wasd': JSON.parse(JSON.stringify(this.defaultBindings)),
|
||||
'arrows': this.createArrowsProfile(),
|
||||
'left-handed': this.createLeftHandedProfile(),
|
||||
'right-handed': this.createRightHandedProfile(),
|
||||
'custom-1': JSON.parse(JSON.stringify(this.defaultBindings)),
|
||||
'custom-2': JSON.parse(JSON.stringify(this.defaultBindings)),
|
||||
'custom-3': JSON.parse(JSON.stringify(this.defaultBindings))
|
||||
};
|
||||
|
||||
// Current active profile
|
||||
this.activeProfile = 'default';
|
||||
this.currentBindings = this.profiles[this.activeProfile];
|
||||
|
||||
// Rebinding state
|
||||
this.isRebinding = false;
|
||||
this.rebindingAction = null;
|
||||
this.rebindingCallback = null;
|
||||
|
||||
// Input buffer for detecting key presses
|
||||
this.inputBuffer = [];
|
||||
this.maxBufferSize = 10;
|
||||
|
||||
this.loadSettings();
|
||||
this.init();
|
||||
|
||||
console.log('✅ Input Remapping System initialized');
|
||||
}
|
||||
|
||||
init() {
|
||||
// Set up input listeners
|
||||
this.setupInputListeners();
|
||||
}
|
||||
|
||||
setupInputListeners() {
|
||||
// Keyboard input
|
||||
this.scene.input.keyboard.on('keydown', (event) => {
|
||||
if (this.isRebinding) {
|
||||
this.handleRebindInput(event.key.toUpperCase());
|
||||
}
|
||||
});
|
||||
|
||||
// Mouse input
|
||||
this.scene.input.on('pointerdown', (pointer) => {
|
||||
if (this.isRebinding) {
|
||||
const button = pointer.leftButtonDown() ? 'MOUSE_LEFT' :
|
||||
pointer.rightButtonDown() ? 'MOUSE_RIGHT' :
|
||||
pointer.middleButtonDown() ? 'MOUSE_MIDDLE' : null;
|
||||
if (button) {
|
||||
this.handleRebindInput(button);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Mouse wheel
|
||||
this.scene.input.on('wheel', (pointer, gameObjects, deltaX, deltaY, deltaZ) => {
|
||||
if (this.isRebinding) {
|
||||
const input = deltaY < 0 ? 'MOUSE_WHEEL_UP' : 'MOUSE_WHEEL_DOWN';
|
||||
this.handleRebindInput(input);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create arrows-based profile (arrow keys for movement)
|
||||
*/
|
||||
createArrowsProfile() {
|
||||
const profile = JSON.parse(JSON.stringify(this.defaultBindings));
|
||||
profile.move_up = ['UP', 'W'];
|
||||
profile.move_down = ['DOWN', 'S'];
|
||||
profile.move_left = ['LEFT', 'A'];
|
||||
profile.move_right = ['RIGHT', 'D'];
|
||||
return profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create left-handed profile (numpad for movement)
|
||||
*/
|
||||
createLeftHandedProfile() {
|
||||
return {
|
||||
// Movement on numpad
|
||||
'move_up': ['NUMPAD_8', 'I'],
|
||||
'move_down': ['NUMPAD_5', 'K'],
|
||||
'move_left': ['NUMPAD_4', 'J'],
|
||||
'move_right': ['NUMPAD_6', 'L'],
|
||||
|
||||
// Actions on left side
|
||||
'interact': ['Q', 'SPACE'],
|
||||
'attack': ['MOUSE_LEFT', 'W'],
|
||||
'use_tool': ['MOUSE_LEFT', 'W'],
|
||||
'cancel': ['ESC', 'E'],
|
||||
'confirm': ['ENTER', 'Q'],
|
||||
|
||||
// Inventory & UI
|
||||
'inventory': ['TAB', 'U'],
|
||||
'crafting': ['R'],
|
||||
'map': ['T'],
|
||||
'quest_log': ['Y'],
|
||||
'pause': ['ESC', 'P'],
|
||||
|
||||
// Tools (right side for easy access)
|
||||
'tool_1': ['7'],
|
||||
'tool_2': ['8'],
|
||||
'tool_3': ['9'],
|
||||
'tool_4': ['0'],
|
||||
'tool_5': ['MINUS'],
|
||||
|
||||
// Quick actions
|
||||
'quick_heal': ['A'],
|
||||
'quick_eat': ['S'],
|
||||
'sprint': ['SHIFT'],
|
||||
'crouch': ['CTRL'],
|
||||
|
||||
// Camera
|
||||
'zoom_in': ['PLUS', 'MOUSE_WHEEL_UP'],
|
||||
'zoom_out': ['EQUALS', 'MOUSE_WHEEL_DOWN'],
|
||||
'camera_reset': ['BACKSPACE']
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create right-handed profile (standard WASD)
|
||||
*/
|
||||
createRightHandedProfile() {
|
||||
return JSON.parse(JSON.stringify(this.defaultBindings));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an action is pressed
|
||||
*/
|
||||
isActionPressed(action) {
|
||||
const bindings = this.currentBindings[action];
|
||||
if (!bindings) return false;
|
||||
|
||||
for (const key of bindings) {
|
||||
if (this.isKeyPressed(key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a specific key is pressed
|
||||
*/
|
||||
isKeyPressed(key) {
|
||||
if (key.startsWith('MOUSE_')) {
|
||||
const pointer = this.scene.input.activePointer;
|
||||
if (key === 'MOUSE_LEFT') return pointer.leftButtonDown();
|
||||
if (key === 'MOUSE_RIGHT') return pointer.rightButtonDown();
|
||||
if (key === 'MOUSE_MIDDLE') return pointer.middleButtonDown();
|
||||
return false;
|
||||
}
|
||||
|
||||
const keyObj = this.scene.input.keyboard.addKey(key, false);
|
||||
return keyObj && keyObj.isDown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an action was just pressed (single frame)
|
||||
*/
|
||||
isActionJustPressed(action) {
|
||||
const bindings = this.currentBindings[action];
|
||||
if (!bindings) return false;
|
||||
|
||||
for (const key of bindings) {
|
||||
if (this.isKeyJustPressed(key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a key was just pressed
|
||||
*/
|
||||
isKeyJustPressed(key) {
|
||||
if (key.startsWith('MOUSE_')) {
|
||||
const pointer = this.scene.input.activePointer;
|
||||
if (key === 'MOUSE_LEFT') return pointer.leftButtonDown() && pointer.getDuration() < 100;
|
||||
if (key === 'MOUSE_RIGHT') return pointer.rightButtonDown() && pointer.getDuration() < 100;
|
||||
if (key === 'MOUSE_MIDDLE') return pointer.middleButtonDown() && pointer.getDuration() < 100;
|
||||
return false;
|
||||
}
|
||||
|
||||
const keyObj = this.scene.input.keyboard.addKey(key, false);
|
||||
return keyObj && Phaser.Input.Keyboard.JustDown(keyObj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start rebinding an action
|
||||
*/
|
||||
startRebinding(action, callback) {
|
||||
if (!this.currentBindings[action]) {
|
||||
console.error(`Action "${action}" does not exist`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.isRebinding = true;
|
||||
this.rebindingAction = action;
|
||||
this.rebindingCallback = callback;
|
||||
|
||||
console.log(`🎮 Rebinding action: ${action}. Press any key...`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle rebind input
|
||||
*/
|
||||
handleRebindInput(input) {
|
||||
if (!this.isRebinding) return;
|
||||
|
||||
// Ignore ESC (cancel rebinding)
|
||||
if (input === 'ESC' || input === 'ESCAPE') {
|
||||
this.cancelRebinding();
|
||||
return;
|
||||
}
|
||||
|
||||
// Set new binding (replace first binding)
|
||||
this.currentBindings[this.rebindingAction][0] = input;
|
||||
|
||||
console.log(`✅ Action "${this.rebindingAction}" rebound to: ${input}`);
|
||||
|
||||
// Call callback if provided
|
||||
if (this.rebindingCallback) {
|
||||
this.rebindingCallback(this.rebindingAction, input);
|
||||
}
|
||||
|
||||
this.isRebinding = false;
|
||||
this.rebindingAction = null;
|
||||
this.rebindingCallback = null;
|
||||
|
||||
this.saveSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel rebinding
|
||||
*/
|
||||
cancelRebinding() {
|
||||
console.log('❌ Rebinding cancelled');
|
||||
this.isRebinding = false;
|
||||
this.rebindingAction = null;
|
||||
this.rebindingCallback = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset action to default binding
|
||||
*/
|
||||
resetAction(action) {
|
||||
if (!this.defaultBindings[action]) {
|
||||
console.error(`Action "${action}" does not exist`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentBindings[action] = JSON.parse(JSON.stringify(this.defaultBindings[action]));
|
||||
this.saveSettings();
|
||||
console.log(`🔄 Action "${action}" reset to default`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all bindings to default
|
||||
*/
|
||||
resetAllBindings() {
|
||||
this.currentBindings = JSON.parse(JSON.stringify(this.defaultBindings));
|
||||
this.profiles[this.activeProfile] = this.currentBindings;
|
||||
this.saveSettings();
|
||||
console.log('🔄 All bindings reset to default');
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch to a different profile
|
||||
*/
|
||||
switchProfile(profileName) {
|
||||
if (!this.profiles[profileName]) {
|
||||
console.error(`Profile "${profileName}" does not exist`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.activeProfile = profileName;
|
||||
this.currentBindings = this.profiles[profileName];
|
||||
this.saveSettings();
|
||||
console.log(`🎮 Switched to profile: ${profileName}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save current bindings to a custom profile
|
||||
*/
|
||||
saveToProfile(profileName) {
|
||||
if (!profileName.startsWith('custom-')) {
|
||||
console.error('Can only save to custom profiles (custom-1, custom-2, custom-3)');
|
||||
return;
|
||||
}
|
||||
|
||||
this.profiles[profileName] = JSON.parse(JSON.stringify(this.currentBindings));
|
||||
this.saveSettings();
|
||||
console.log(`💾 Bindings saved to profile: ${profileName}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get binding display name
|
||||
*/
|
||||
getBindingDisplay(action) {
|
||||
const bindings = this.currentBindings[action];
|
||||
if (!bindings || bindings.length === 0) return 'Not bound';
|
||||
|
||||
return bindings.map(key => this.formatKeyName(key)).join(' / ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Format key name for display
|
||||
*/
|
||||
formatKeyName(key) {
|
||||
const keyMap = {
|
||||
'MOUSE_LEFT': 'Left Click',
|
||||
'MOUSE_RIGHT': 'Right Click',
|
||||
'MOUSE_MIDDLE': 'Middle Click',
|
||||
'MOUSE_WHEEL_UP': 'Scroll Up',
|
||||
'MOUSE_WHEEL_DOWN': 'Scroll Down',
|
||||
'SPACE': 'Space',
|
||||
'ENTER': 'Enter',
|
||||
'ESC': 'Escape',
|
||||
'SHIFT': 'Shift',
|
||||
'CTRL': 'Ctrl',
|
||||
'ALT': 'Alt',
|
||||
'TAB': 'Tab',
|
||||
'BACKSPACE': 'Backspace',
|
||||
'UP': '↑',
|
||||
'DOWN': '↓',
|
||||
'LEFT': '←',
|
||||
'RIGHT': '→',
|
||||
'PLUS': '+',
|
||||
'MINUS': '-',
|
||||
'EQUALS': '='
|
||||
};
|
||||
|
||||
return keyMap[key] || key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all available profiles
|
||||
*/
|
||||
getProfiles() {
|
||||
return Object.keys(this.profiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current profile name
|
||||
*/
|
||||
getCurrentProfile() {
|
||||
return this.activeProfile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export bindings as JSON
|
||||
*/
|
||||
exportBindings() {
|
||||
const data = {
|
||||
activeProfile: this.activeProfile,
|
||||
profiles: this.profiles
|
||||
};
|
||||
return JSON.stringify(data, null, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import bindings from JSON
|
||||
*/
|
||||
importBindings(jsonString) {
|
||||
try {
|
||||
const data = JSON.parse(jsonString);
|
||||
this.activeProfile = data.activeProfile || 'default';
|
||||
this.profiles = data.profiles || this.profiles;
|
||||
this.currentBindings = this.profiles[this.activeProfile];
|
||||
this.saveSettings();
|
||||
console.log('✅ Bindings imported successfully');
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to import bindings:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save settings to localStorage
|
||||
*/
|
||||
saveSettings() {
|
||||
const data = {
|
||||
activeProfile: this.activeProfile,
|
||||
profiles: this.profiles
|
||||
};
|
||||
localStorage.setItem('novafarma_input_bindings', JSON.stringify(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load settings from localStorage
|
||||
*/
|
||||
loadSettings() {
|
||||
const saved = localStorage.getItem('novafarma_input_bindings');
|
||||
if (saved) {
|
||||
try {
|
||||
const data = JSON.parse(saved);
|
||||
this.activeProfile = data.activeProfile || 'default';
|
||||
this.profiles = { ...this.profiles, ...data.profiles };
|
||||
this.currentBindings = this.profiles[this.activeProfile];
|
||||
console.log('✅ Input bindings loaded from localStorage');
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to load input bindings:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get controller button name
|
||||
*/
|
||||
getControllerButtonName(button) {
|
||||
const buttonMap = {
|
||||
'A': 'A (Cross)',
|
||||
'B': 'B (Circle)',
|
||||
'X': 'X (Square)',
|
||||
'Y': 'Y (Triangle)',
|
||||
'LB': 'LB (L1)',
|
||||
'RB': 'RB (R1)',
|
||||
'LT': 'LT (L2)',
|
||||
'RT': 'RT (R2)',
|
||||
'START': 'Start',
|
||||
'SELECT': 'Select (Share)',
|
||||
'LEFT_STICK': 'Left Stick',
|
||||
'RIGHT_STICK': 'Right Stick'
|
||||
};
|
||||
return buttonMap[button] || button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if controller is connected
|
||||
*/
|
||||
isControllerConnected() {
|
||||
return this.scene.input.gamepad && this.scene.input.gamepad.total > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get connected controller info
|
||||
*/
|
||||
getControllerInfo() {
|
||||
if (!this.isControllerConnected()) return null;
|
||||
|
||||
const pad = this.scene.input.gamepad.getPad(0);
|
||||
return {
|
||||
id: pad.id,
|
||||
index: pad.index,
|
||||
buttons: pad.buttons.length,
|
||||
axes: pad.axes.length
|
||||
};
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.saveSettings();
|
||||
console.log('🎮 Input Remapping System destroyed');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user