diff --git a/src/configs/cfg_keyboard_azerty.ts b/src/configs/cfg_keyboard_azerty.ts index 77dc8b02a..89871772d 100644 --- a/src/configs/cfg_keyboard_azerty.ts +++ b/src/configs/cfg_keyboard_azerty.ts @@ -5,6 +5,7 @@ const cfg_keyboard_azerty = { padID: 'keyboard', padType: 'azerty', gamepadMapping: { + NEED TO ADD KEYCODE FROM EVENT HERE KEY_A: 0, KEY_B: 0, KEY_C: 0, diff --git a/src/configs/gamepad-utils.ts b/src/configs/gamepad-utils.ts index 9e55e35c2..eac1e9598 100644 --- a/src/configs/gamepad-utils.ts +++ b/src/configs/gamepad-utils.ts @@ -10,6 +10,12 @@ export function getKeyFromInputIndex(config: GamepadConfig, index: number): Stri } return null; } +export function getKeyFromKeyboardKey(config: GamepadConfig, key): String | null { + for (const _key of Object.keys(config.gamepadMapping)) { + if (config.gamepadMapping[_key] === key) return _key; + } + return null; +} // Given a setting name, return the key assigned to it from the config file export function getKeyForSettingName(config: GamepadConfig, settingName: string): String | null { @@ -41,6 +47,11 @@ export function getCurrenlyAssignedIconFromInputIndex(config: GamepadConfig, ind return config.icons[key]; } +export function getCurrenlyAssignedIconFromKeyboardKey(config: GamepadConfig, key): String { + const _key = getKeyFromKeyboardKey(config, key); + return config.icons[_key]; +} + // Given a setting name, return the icon currently assigned to this setting name export function getCurrentlyAssignedIconToSettingName(config: GamepadConfig, settingName: string): string { const key = getCurrentlyAssignedToSettingName(config, settingName); diff --git a/src/inputs-controller.ts b/src/inputs-controller.ts index 3aa61443d..0413953f7 100644 --- a/src/inputs-controller.ts +++ b/src/inputs-controller.ts @@ -10,7 +10,7 @@ import SettingsGamepadUiHandler from "./ui/settings/settings-gamepad-ui-handler" import {SettingGamepad} from "./system/settings-gamepad"; import { getCurrenlyAssignedIconFromInputIndex, getCurrentlyAssignedIconToSettingName, - getKeyFromInputIndex, getCurrentlyAssignedToSettingName + getKeyFromInputIndex, getCurrentlyAssignedToSettingName, getCurrenlyAssignedIconFromKeyboardKey } from "./configs/gamepad-utils"; import SettingsKeyboardUiHandler from "#app/ui/settings/settings-keyboard-ui-handler"; import cfg_keyboard_azerty from "#app/configs/cfg_keyboard_azerty"; @@ -724,6 +724,10 @@ export class InputsController { return [this.configs[this.chosenGamepad].padType, getCurrenlyAssignedIconFromInputIndex(this.configs[this.chosenGamepad], button.index)]; } + getPressedKeyLabel(key): string { + return getCurrenlyAssignedIconFromKeyboardKey(this.configs[this.chosenKeyboard], key); + } + /** * Retrieves the currently assigned icon for a specific setting on the chosen gamepad. * @@ -734,6 +738,10 @@ export class InputsController { return getCurrentlyAssignedIconToSettingName(this.configs[this.chosenGamepad], target); } + getKeyboardCurrentlyAssignedIconToDisplay(target: SettingGamepad): string { + return getCurrentlyAssignedIconToSettingName(this.configs[this.chosenKeyboard], target); + } + /** * Swaps the binding of two controls on the chosen gamepad configuration. * It temporarily pauses updates, swaps the key bindings, saves the new configuration, @@ -754,6 +762,10 @@ export class InputsController { setTimeout(() => this.pauseUpdate = false, 500); } + swapKeyboardBinding(settingName, pressedButton): void { + + } + /** * 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. diff --git a/src/ui/settings/keyboard-binding-ui-handler.ts b/src/ui/settings/keyboard-binding-ui-handler.ts index 469de1605..061a1e983 100644 --- a/src/ui/settings/keyboard-binding-ui-handler.ts +++ b/src/ui/settings/keyboard-binding-ui-handler.ts @@ -2,88 +2,207 @@ import UiHandler from "../ui-handler"; import BattleScene from "../../battle-scene"; import {Mode} from "../ui"; import {Button} from "../../enums/buttons"; +import Phaser from "phaser"; +import {addWindow} from "#app/ui/ui-theme"; +import {addTextObject, TextStyle} from "#app/ui/text"; +import {SettingKeyboard} from "#app/system/settings-keyboard"; export default class KeyboardBindingUiHandler extends UiHandler { + // Containers for different segments of the UI. + protected optionSelectContainer: Phaser.GameObjects.Container; + protected actionsContainer: Phaser.GameObjects.Container; + + // Background elements for titles and action areas. + protected titleBg: Phaser.GameObjects.NineSlice; + protected actionBg: Phaser.GameObjects.NineSlice; + protected optionSelectBg: Phaser.GameObjects.NineSlice; + + // Text elements for displaying instructions and actions. + private unlockText: Phaser.GameObjects.Text; + private swapText: Phaser.GameObjects.Text; + private actionLabel: Phaser.GameObjects.Text; + private cancelLabel: Phaser.GameObjects.Text; + + private listening: boolean = false; + private buttonPressed: number | null = null; + + // Icons for displaying current and new button assignments. + private newButtonIcon: Phaser.GameObjects.Sprite; + private targetButtonIcon: Phaser.GameObjects.Sprite; + + // Function to call on cancel or completion of binding. + private cancelFn: (boolean?) => boolean; + + // The specific setting being modified. + private target: SettingKeyboard; constructor(scene: BattleScene, mode?: Mode) { super(scene, mode); + // Listen to gamepad button down events to initiate binding. + scene.input.keyboard.on('keydown', this.onKeyDown, this); } setup() { const ui = this.getUi(); + this.optionSelectContainer = this.scene.add.container(0, 0); + this.actionsContainer = this.scene.add.container(0, 0); + // Initially, containers are not visible. + this.optionSelectContainer.setVisible(false); + this.actionsContainer.setVisible(false); + + // Add containers to the UI. + ui.add(this.optionSelectContainer); + ui.add(this.actionsContainer); + + // Setup backgrounds and text objects for UI. + this.titleBg = addWindow(this.scene, (this.scene.game.canvas.width / 6) - this.getWindowWidth(), -(this.scene.game.canvas.height / 6) + 28 + 21, this.getWindowWidth(), 24); + this.titleBg.setOrigin(0.5); + this.optionSelectContainer.add(this.titleBg); + + this.actionBg = addWindow(this.scene, (this.scene.game.canvas.width / 6) - this.getWindowWidth(), -(this.scene.game.canvas.height / 6) + this.getWindowHeight() + 28 + 21 + 21, this.getWindowWidth(), 24); + this.actionBg.setOrigin(0.5); + this.actionsContainer.add(this.actionBg); + + // Text prompts and instructions for the user. + this.unlockText = addTextObject(this.scene, 0, 0, 'Press a button...', TextStyle.WINDOW); + this.unlockText.setOrigin(0, 0); + this.unlockText.setPositionRelative(this.titleBg, 36, 4); + this.optionSelectContainer.add(this.unlockText); + + this.optionSelectBg = addWindow(this.scene, (this.scene.game.canvas.width / 6) - this.getWindowWidth(), -(this.scene.game.canvas.height / 6) + this.getWindowHeight() + 28, this.getWindowWidth(), this.getWindowHeight()); + this.optionSelectBg.setOrigin(0.5); + this.optionSelectContainer.add(this.optionSelectBg); + + // New button icon setup. + this.newButtonIcon = this.scene.add.sprite(0, 0, 'keyboard'); + this.newButtonIcon.setScale(0.15); + this.newButtonIcon.setPositionRelative(this.optionSelectBg, 78, 16); + this.newButtonIcon.setOrigin(0.5); + this.newButtonIcon.setVisible(false); + + this.swapText = addTextObject(this.scene, 0, 0, 'will swap with', TextStyle.WINDOW); + this.swapText.setOrigin(0.5); + this.swapText.setPositionRelative(this.optionSelectBg, this.optionSelectBg.width / 2 - 2, this.optionSelectBg.height / 2 - 2); + this.swapText.setVisible(false); + + this.targetButtonIcon = this.scene.add.sprite(0, 0, 'keyboard'); + this.targetButtonIcon.setScale(0.15); + this.targetButtonIcon.setPositionRelative(this.optionSelectBg, 78, 48); + this.targetButtonIcon.setOrigin(0.5); + this.targetButtonIcon.setVisible(false); + + this.cancelLabel = addTextObject(this.scene, 0, 0, 'Cancel', TextStyle.SETTINGS_LABEL); + this.cancelLabel.setOrigin(0, 0.5); + this.cancelLabel.setPositionRelative(this.actionBg, 10, this.actionBg.height / 2); + + this.actionLabel = addTextObject(this.scene, 0, 0, 'Confirm Swap', TextStyle.SETTINGS_LABEL); + this.actionLabel.setOrigin(0, 0.5); + this.actionLabel.setPositionRelative(this.actionBg, this.actionBg.width - 75, this.actionBg.height / 2); + + // Add swap and cancel labels to the containers. + this.optionSelectContainer.add(this.newButtonIcon); + this.optionSelectContainer.add(this.swapText); + this.optionSelectContainer.add(this.targetButtonIcon); + this.actionsContainer.add(this.actionLabel); + this.actionsContainer.add(this.cancelLabel); } - updateBindings(): void { - + onKeyDown(event): void { + const key = event.keyCode; + // // Check conditions before processing the button press. + if (!this.listening || this.buttonPressed !== null) return; + this.buttonPressed = key; + const buttonIcon = this.scene.inputController.getPressedKeyLabel(key); + const assignedButtonIcon = this.scene.inputController.getKeyboardCurrentlyAssignedIconToDisplay(this.target); + this.newButtonIcon.setFrame(buttonIcon); + this.targetButtonIcon.setFrame(assignedButtonIcon); + this.newButtonIcon.setVisible(true); + this.targetButtonIcon.setVisible(true); + this.swapText.setVisible(true); + this.setCursor(0); + this.actionsContainer.setVisible(true); } show(args: any[]): boolean { super.show(args); + this.buttonPressed = null; + this.cancelFn = args[0].cancelHandler; + this.target = args[0].target; - // Move the settings container to the end of the UI stack to ensure it is displayed on top. - // this.getUi().moveTo(this.settingsContainer, this.getUi().length - 1); + // Bring the option and action containers to the front of the UI. + this.getUi().bringToTop(this.optionSelectContainer); + this.getUi().bringToTop(this.actionsContainer); - // Hide any tooltips that might be visible before showing the settings container. - this.getUi().hideTooltip(); - - // Return true to indicate the UI was successfully shown. + this.optionSelectContainer.setVisible(true); + setTimeout(() => this.listening = true, 150); return true; } + getWindowWidth(): number { + return 160; + } + + getWindowHeight(): number { + return 64; + } + processInput(button: Button): boolean { + if (this.buttonPressed === null) return; const ui = this.getUi(); - return false; + let success = false; + switch (button) { + case Button.LEFT: + case Button.RIGHT: + // Toggle between action and cancel options. + const cursor = this.cursor ? 0 : 1; + success = this.setCursor(cursor); + break + case Button.ACTION: + // Process actions based on current cursor position. + if (this.cursor === 0) { + this.cancelFn(); + } else { + success = true; + this.scene.inputController.swapKeyboardBinding(this.target, this.buttonPressed); + this.cancelFn(success); + } + break; + } + + // Plays a select sound effect if an action was successfully processed. + if (success) + ui.playSelect(); + else + ui.playError(); + + return success; } setCursor(cursor: integer): boolean { - const ret = super.setCursor(cursor); - return ret; - } - - setOptionCursor(settingIndex: integer, cursor: integer, save?: boolean): boolean { + this.cursor = cursor; + if (cursor === 1) { + this.actionLabel.setColor(this.getTextColor(TextStyle.SETTINGS_SELECTED)); + this.actionLabel.setShadowColor(this.getTextColor(TextStyle.SETTINGS_SELECTED, true)); + this.cancelLabel.setColor(this.getTextColor(TextStyle.WINDOW)); + this.cancelLabel.setShadowColor(this.getTextColor(TextStyle.WINDOW, true)); + return true; + } + this.actionLabel.setColor(this.getTextColor(TextStyle.WINDOW)); + this.actionLabel.setShadowColor(this.getTextColor(TextStyle.WINDOW, true)); + this.cancelLabel.setColor(this.getTextColor(TextStyle.SETTINGS_SELECTED)); + this.cancelLabel.setShadowColor(this.getTextColor(TextStyle.SETTINGS_SELECTED, true)); return true; } - setScrollCursor(scrollCursor: integer): boolean { - return true; - } - - // updateSettingsScroll(): void { - // // Return immediately if the options container is not initialized. - // if (!this.optionsContainer) return; - // - // // Set the vertical position of the options container based on the current scroll cursor, multiplying by the item height. - // this.optionsContainer.setY(-16 * this.scrollCursor); - // - // // Iterate over all setting labels to update their visibility. - // for (let s = 0; s < this.settingLabels.length; s++) { - // // Determine if the current setting should be visible based on the scroll position. - // const visible = s >= this.scrollCursor && s < this.scrollCursor + 9; - // - // // Set the visibility of the setting label and its corresponding options. - // this.settingLabels[s].setVisible(visible); - // for (let option of this.optionValueLabels[s]) - // option.setVisible(visible); - // } - // } - - clear(): void { + clear() { super.clear(); - - // Hide the settings container to remove it from the view. - // this.settingsContainer.setVisible(false); - - // Remove the cursor from the UI. - this.eraseCursor(); - } - - eraseCursor(): void { - // Check if a cursor object exists. - // if (this.cursorObj) - // this.cursorObj.destroy(); // Destroy the cursor object to clean up resources. - // - // // Set the cursor object reference to null to fully dereference it. - // this.cursorObj = null; + this.target = null; + this.cancelFn = null; + this.optionSelectContainer.setVisible(false); + this.actionsContainer.setVisible(false); + this.newButtonIcon.setVisible(false); + this.targetButtonIcon.setVisible(false); + this.swapText.setVisible(false); } } \ No newline at end of file