diff --git a/src/configs/gamepad-utils.ts b/src/configs/gamepad-utils.ts index 707efd802..9e55e35c2 100644 --- a/src/configs/gamepad-utils.ts +++ b/src/configs/gamepad-utils.ts @@ -42,7 +42,7 @@ export function getCurrenlyAssignedIconFromInputIndex(config: GamepadConfig, ind } // Given a setting name, return the icon currently assigned to this setting name -export function getCurrentlyAssignedIconToSettingName(config: GamepadConfig, settingName: string) { +export function getCurrentlyAssignedIconToSettingName(config: GamepadConfig, settingName: string): string { const key = getCurrentlyAssignedToSettingName(config, settingName); return config.icons[key]; } diff --git a/src/configs/pad_unlicensedSNES.ts b/src/configs/pad_unlicensedSNES.ts index 7da7553ff..8321c364e 100644 --- a/src/configs/pad_unlicensedSNES.ts +++ b/src/configs/pad_unlicensedSNES.ts @@ -6,7 +6,7 @@ import {Button} from "../enums/buttons"; */ const pad_unlicensedSNES = { padID: '081f-e401', - padType: 'snes', + padType: 'xbox', gamepadMapping : { RC_S: 2, RC_E: 1, diff --git a/src/inputs-controller.ts b/src/inputs-controller.ts index 42581fccb..23e508507 100644 --- a/src/inputs-controller.ts +++ b/src/inputs-controller.ts @@ -188,6 +188,12 @@ export class InputsController { } } + /** + * Sets the currently chosen gamepad and initializes related settings. + * This method first deactivates any active key presses and then initializes the gamepad settings. + * + * @param gamepad - The identifier of the gamepad to set as chosen. + */ setChosenGamepad(gamepad: String): void { this.deactivatePressedKey(); this.initChosenGamepad(gamepad) @@ -196,15 +202,13 @@ export class InputsController { /** * Updates the interaction handling by processing input states. * This method gives priority to certain buttons by reversing the order in which they are checked. + * This method loops through all button values, checks for valid and timely interactions, and conditionally processes + * or ignores them based on the current state of gamepad support and other criteria. * - * @remarks - * The method iterates over all possible buttons, checking for specific conditions such as: - * - If the button is registered in the `interactions` dictionary. - * - If the button has been held down long enough. - * - If the button is currently pressed. + * It handles special conditions such as the absence of gamepad support or mismatches between the source of the input and + * the currently chosen gamepad. It also respects the paused state of updates to prevent unwanted input processing. * - * Special handling is applied if gamepad support is disabled but a gamepad source is still triggering inputs, - * preventing potential infinite loops by removing the last processed movement time for the button. + * If an interaction is valid and should be processed, it emits an 'input_down' event with details of the interaction. */ update(): void { for (const b of Utils.getEnumValues(Button).reverse()) { @@ -233,34 +237,39 @@ export class InputsController { } } + /** + * Retrieves the identifiers of all connected gamepads, excluding any that are currently marked as disconnected. + * @returns Array An array of strings representing the IDs of the connected gamepads. + */ getGamepadsName(): Array { return this.gamepads.filter(g => !this.disconnectedGamepads.includes(g.id)).map(g => g.id); } + /** + * Initializes the chosen gamepad by setting its identifier in the local storage and updating the UI to reflect the chosen gamepad. + * If a gamepad name is provided, it uses that as the chosen gamepad; otherwise, it defaults to the currently chosen gamepad. + * @param gamepadName Optional parameter to specify the name of the gamepad to initialize as chosen. + */ initChosenGamepad(gamepadName?: String): void { - // if we have a gamepad name in parameter, we set the chosen gamepad with this value let name = gamepadName; if (gamepadName) this.chosenGamepad = gamepadName; else - name = this.chosenGamepad; // otherwise we use the chosen gamepad's name + name = this.chosenGamepad; localStorage.setItem('chosenGamepad', name); - // we update the ui with the chosen gamepad const handler = this.scene.ui?.handlers[Mode.SETTINGS_GAMEPAD] as SettingsGamepadUiHandler; handler && handler.updateChosenGamepadDisplay() } - clearChosenGamepad() { - this.chosenGamepad = null; - if (localStorage.hasOwnProperty('chosenGamepad')) - localStorage.removeItem('chosenGamepad'); - } - + /** + * Handles the disconnection of a gamepad by adding its identifier to a list of disconnected gamepads. + * This is necessary because Phaser retains memory of previously connected gamepads, and without tracking + * disconnections, it would be impossible to determine the connection status of gamepads. This method ensures + * that disconnected gamepads are recognized and can be appropriately hidden in the gamepad selection menu. + * + * @param thisGamepad The gamepad that has been disconnected. + */ onDisconnect(thisGamepad: Phaser.Input.Gamepad.Gamepad): void { - // We need to add the disconnected gamepad into a local array - // Because Phaser keep in memory the previously connected gamepad - // If we don't do that, we have no way to determine if the gamepad is connected or not. - // We want to know that because we want to hide it in the selection menu of gamepad to use this.disconnectedGamepads.push(thisGamepad.id); /** commented for now this code because i don't know anymore if it's good to do that * for example i'm playing with a wireless gamepad that shutdown after 5 min @@ -280,27 +289,28 @@ export class InputsController { // } } + /** + * Updates the tracking of disconnected gamepads when a gamepad is reconnected. + * It removes the reconnected gamepad's identifier from the `disconnectedGamepads` array, + * effectively updating its status to connected. + * + * @param thisGamepad The gamepad that has been reconnected. + */ onReconnect(thisGamepad: Phaser.Input.Gamepad.Gamepad): void { - // We check if a gamepad reconnect by looking in the disconnectedGamepads array if is there - // If he is there, we remove it. this.disconnectedGamepads = this.disconnectedGamepads.filter(g => g !== thisGamepad.id); } /** - * Configures a gamepad for use based on its device ID. + * Initializes or updates configurations for connected gamepads. + * It retrieves the names of all connected gamepads, sets up their configurations according to stored or default settings, + * and ensures these configurations are saved. If the connected gamepad is the currently chosen one, + * it reinitializes the chosen gamepad settings. * - * @param thisGamepad - The gamepad to set up. - * - * @remarks - * This method initializes a gamepad by mapping its ID to a predefined configuration. - * It updates the player's gamepad mapping based on the identified configuration, ensuring - * that the gamepad controls are correctly mapped to in-game actions. + * @param thisGamepad The gamepad that is being set up. */ setupGamepad(thisGamepad: Phaser.Input.Gamepad.Gamepad): void { - // we fetch all the gamepads name const allGamepads = this.getGamepadsName(); for (const gamepad of allGamepads) { - // for each gamepad, we set its mapping in this.configs const gamepadID = gamepad.toLowerCase(); const config = this.getConfig(gamepadID); config.custom = this.configs[gamepad]?.custom || {...config.default}; @@ -330,21 +340,17 @@ export class InputsController { } /** - * Handles the 'down' event for gamepad buttons, emitting appropriate events and updating the interaction state. + * Handles button press events on a gamepad. This method sets the gamepad as chosen on the first input if no gamepad is currently chosen. + * It checks if gamepad support is enabled and if the event comes from the chosen gamepad. If so, it maps the button press to a specific + * action using a custom configuration, emits an event for the button press, and records the time of the action. * - * @param pad - The gamepad on which the button press occurred. - * @param button - The button that was pressed. - * @param value - The value associated with the button press, typically indicating pressure or degree of activation. - * - * @remarks - * This method is triggered when a gamepad button is pressed. If gamepad support is enabled, it: - * - Retrieves the current gamepad action mapping. - * - Checks if the pressed button is mapped to a game action. - * - If mapped, emits an 'input_down' event with the controller type and button action, and updates the interaction of this button. + * @param pad The gamepad on which the button was pressed. + * @param button The specific button that was pressed. + * @param value The intensity or value of the button press, if applicable. */ gamepadButtonDown(pad: Phaser.Input.Gamepad.Gamepad, button: Phaser.Input.Gamepad.Button, value: number): void { if (!pad) return; - if (!this.chosenGamepad) // at the very first input, if we have not yet a chosen gamepad, we set it + if (!this.chosenGamepad) this.setChosenGamepad(pad.id); if (!this.gamepadSupport || pad.id.toLowerCase() !== this.chosenGamepad.toLowerCase()) return; const key = getKeyFromInputIndex(this.configs[pad.id], button.index); @@ -360,17 +366,13 @@ export class InputsController { } /** - * Handles the 'up' event for gamepad buttons, emitting appropriate events and clearing the interaction state. + * Responds to a button release event on a gamepad by checking if the gamepad is supported and currently chosen. + * If conditions are met, it identifies the configured action for the button, emits an event signaling the button release, + * and clears the record of the button. * - * @param pad - The gamepad on which the button release occurred. - * @param button - The button that was released. - * @param value - The value associated with the button release, typically indicating pressure or degree of deactivation. - * - * @remarks - * This method is triggered when a gamepad button is released. If gamepad support is enabled, it: - * - Retrieves the current gamepad action mapping. - * - Checks if the released button is mapped to a game action. - * - If mapped, emits an 'input_up' event with the controller type and button action, and clears the interaction for this button. + * @param pad The gamepad from which the button was released. + * @param button The specific button that was released. + * @param value The intensity or value of the button release, if applicable. */ gamepadButtonUp(pad: Phaser.Input.Gamepad.Gamepad, button: Phaser.Input.Gamepad.Button, value: number): void { if (!pad) return; @@ -481,17 +483,12 @@ export class InputsController { } /** - * Maps a gamepad ID to a specific gamepad configuration based on the ID's characteristics. + * Retrieves the configuration object for a gamepad based on its identifier. The method identifies specific gamepad models + * based on substrings in the identifier and returns predefined configurations for recognized models. + * If no specific configuration matches, it defaults to a generic gamepad configuration. * - * @param id - The gamepad ID string, typically representing a unique identifier for a gamepad model or make. - * @returns A `GamepadConfig` object corresponding to the identified gamepad model. - * - * @remarks - * This function analyzes the provided gamepad ID and matches it to a predefined configuration based on known identifiers: - * - If the ID includes both '081f' and 'e401', it is identified as an unlicensed SNES gamepad. - * - If the ID contains 'xbox' and '360', it is identified as an Xbox 360 gamepad. - * - If the ID contains '054c', it is identified as a DualShock gamepad. - * If no specific identifiers are recognized, a generic gamepad configuration is returned. + * @param id The identifier string of the gamepad. + * @returns GamepadConfig The configuration object corresponding to the identified gamepad type. */ getConfig(id: string): GamepadConfig { id = id.toLowerCase(); @@ -504,7 +501,7 @@ export class InputsController { return pad_dualshock; } - // return pad_dualshock; + return pad_unlicensedSNES; return pad_generic; } @@ -648,20 +645,46 @@ export class InputsController { else if (this.buttonLock2 === button) this.buttonLock2 = null; } - getActiveConfig(): GamepadConfig { + /** + * Retrieves the active configuration for the currently chosen gamepad. + * It checks if a specific gamepad ID is stored under the chosen gamepad's configurations and returns it. + * + * @returns GamepadConfig The configuration object for the active gamepad, or null if not set. + */ + getActiveConfig(): GamepadConfig | null { if (this.configs[this.chosenGamepad]?.padID) return this.configs[this.chosenGamepad] return null; } - getPressedButtonLabel(button: Phaser.Input.Gamepad.Button) { + /** + * Determines icon for a button pressed on the currently chosen gamepad based on its configuration. + * + * @param button The button for which to retrieve the label and icon. + * @returns Array Tuple containing the pad type and the currently assigned icon for the button index. + */ + getPressedButtonLabel(button: Phaser.Input.Gamepad.Button): [string, string] { return [this.configs[this.chosenGamepad].padType, getCurrenlyAssignedIconFromInputIndex(this.configs[this.chosenGamepad], button.index)]; } - getCurrentlyAssignedIconToDisplay(target: SettingGamepad) { + /** + * Retrieves the currently assigned icon for a specific setting on the chosen gamepad. + * + * @param target The gamepad setting for which to retrieve the assigned icon. + * @returns string The icon assigned to the specified setting. + */ + getCurrentlyAssignedIconToDisplay(target: SettingGamepad): string { return getCurrentlyAssignedIconToSettingName(this.configs[this.chosenGamepad], target); } - swapBinding(settingName, pressedButton) { + /** + * Swaps the binding of two controls on the chosen gamepad configuration. + * It temporarily pauses updates, swaps the key bindings, saves the new configuration, + * and then resumes updates after a short delay. + * + * @param settingName The name of the setting for which to swap the binding. + * @param pressedButton The button index whose binding is to be swapped. + */ + swapBinding(settingName, pressedButton): void { this.pauseUpdate = true; const keyTarget = getCurrentlyAssignedToSettingName(this.configs[this.chosenGamepad], settingName) const keyNewBinding = getKeyFromInputIndex(this.configs[this.chosenGamepad], pressedButton); @@ -673,6 +696,13 @@ export class InputsController { setTimeout(() => this.pauseUpdate = false, 500); } + /** + * Injects a custom mapping configuration into the gamepad configuration for a specific gamepad. + * If the gamepad does not have an existing configuration, it initializes one first. + * + * @param gamepadName The identifier of the gamepad to configure. + * @param customMappings The custom mapping configuration to apply to the gamepad. + */ injectConfig(gamepadName: String, customMappings: MappingLayout): void { if (!this.configs[gamepadName]) this.configs[gamepadName] = {}; this.configs[gamepadName].custom = customMappings;