From 1a85a49ea633086c57d669204a332ec2daba813d Mon Sep 17 00:00:00 2001 From: Greenlamp Date: Thu, 2 May 2024 00:04:03 +0200 Subject: [PATCH] rework of the input handling, including different gamepad and keyboard --- src/battle-scene.ts | 474 +++++++++++++++++++++++++++----------------- 1 file changed, 293 insertions(+), 181 deletions(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index cbf363f689a..26976604a36 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -61,6 +61,10 @@ import CandyBar from './ui/candy-bar'; import { Variant, variantData } from './data/variant'; import { Localizable } from './plugins/i18n'; import { STARTING_WAVE_OVERRIDE, OPP_SPECIES_OVERRIDE, SEED_OVERRIDE, STARTING_BIOME_OVERRIDE } from './overrides'; +import pad_generic from "./configs/pad_generic"; +import pad_unlicensedSNES from "./configs/pad_unlicensedSNES"; +import pad_xbox360 from "./configs/pad_xbox360"; +import pad_dualshock from "./configs/pad_dualshock"; export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1"; @@ -69,7 +73,6 @@ const DEBUG_RNG = false; export const startingWave = STARTING_WAVE_OVERRIDE || 1; const expSpriteKeys: string[] = []; -const repeatInputDelayMillis = 250; export let starterColors: StarterColors; interface StarterColors { @@ -191,32 +194,12 @@ export default class BattleScene extends SceneBase { private playTimeTimer: Phaser.Time.TimerEvent; private buttonKeys: Phaser.Input.Keyboard.Key[][]; + private isButtonPressing: boolean; private lastProcessedButtonPressTimes: Map = new Map(); // movementButtonLock ensures only a single movement key is firing repeated inputs // (i.e. by holding down a button) at a time private movementButtonLock: Button; - - // using a dualshock controller as a map - private gamepadKeyConfig = { - [Button.UP]: 12, // up - [Button.DOWN]: 13, // down - [Button.LEFT]: 14, // left - [Button.RIGHT]: 15, // right - [Button.SUBMIT]: 17, // touchpad - [Button.ACTION]: 0, // X - [Button.CANCEL]: 1, // O - [Button.MENU]: 9, // options - [Button.STATS]: 8, // share - [Button.CYCLE_SHINY]: 5, // RB - [Button.CYCLE_FORM]: 4, // LB - [Button.CYCLE_GENDER]: 6, // LT - [Button.CYCLE_ABILITY]: 7, // RT - [Button.CYCLE_NATURE]: 2, // square - [Button.CYCLE_VARIANT]: 3, // triangle - [Button.SPEED_UP]: 10, // L3 - [Button.SLOW_DOWN]: 11 // R3 - }; - public gamepadButtonStates: boolean[] = new Array(17).fill(false); + private mappedPad; public rngCounter: integer = 0; public rngSeedOverride: string = ''; @@ -274,6 +257,8 @@ export default class BattleScene extends SceneBase { addUiThemeOverrides(this); this.setupControls(); + this.checkInputGamepad(); + this.checkInputKeyboard(); this.load.setBaseURL(); @@ -287,7 +272,6 @@ export default class BattleScene extends SceneBase { } update() { - this.checkInput(); this.ui?.update(); } @@ -1343,172 +1327,300 @@ export default class BattleScene extends SceneBase { return biomes[Utils.randSeedInt(biomes.length)]; } - checkInput(): boolean { - let inputSuccess = false; - let vibrationLength = 0; - if (this.buttonJustPressed(Button.UP) || this.repeatInputDurationJustPassed(Button.UP)) { - inputSuccess = this.ui.processInput(Button.UP); - vibrationLength = 5; - this.setLastProcessedMovementTime(Button.UP) - } else if (this.buttonJustPressed(Button.DOWN) || this.repeatInputDurationJustPassed(Button.DOWN)) { - inputSuccess = this.ui.processInput(Button.DOWN); - vibrationLength = 5; - this.setLastProcessedMovementTime(Button.DOWN) - } else if (this.buttonJustPressed(Button.LEFT) || this.repeatInputDurationJustPassed(Button.LEFT)) { - inputSuccess = this.ui.processInput(Button.LEFT); - vibrationLength = 5; - this.setLastProcessedMovementTime(Button.LEFT) - } else if (this.buttonJustPressed(Button.RIGHT) || this.repeatInputDurationJustPassed(Button.RIGHT)) { - inputSuccess = this.ui.processInput(Button.RIGHT); - vibrationLength = 5; - this.setLastProcessedMovementTime(Button.RIGHT) - } else if (this.buttonJustPressed(Button.SUBMIT) || this.repeatInputDurationJustPassed(Button.SUBMIT)) { - inputSuccess = this.ui.processInput(Button.SUBMIT) || this.ui.processInput(Button.ACTION); - this.setLastProcessedMovementTime(Button.SUBMIT); - } else if (this.buttonJustPressed(Button.ACTION) || this.repeatInputDurationJustPassed(Button.ACTION)) { - inputSuccess = this.ui.processInput(Button.ACTION); - this.setLastProcessedMovementTime(Button.ACTION); - } else if (this.buttonJustPressed(Button.CANCEL)|| this.repeatInputDurationJustPassed(Button.CANCEL)) { - inputSuccess = this.ui.processInput(Button.CANCEL); - this.setLastProcessedMovementTime(Button.CANCEL); - } else if (this.buttonJustPressed(Button.MENU)) { - if (this.disableMenu) - return; - switch (this.ui?.getMode()) { - case Mode.MESSAGE: - if (!(this.ui.getHandler() as MessageUiHandler).pendingPrompt) - return; - case Mode.TITLE: - case Mode.COMMAND: - case Mode.FIGHT: - case Mode.BALL: - case Mode.TARGET_SELECT: - case Mode.SAVE_SLOT: - case Mode.PARTY: - case Mode.SUMMARY: - case Mode.STARTER_SELECT: - case Mode.CONFIRM: - case Mode.OPTION_SELECT: - this.ui.setOverlayMode(Mode.MENU); - inputSuccess = true; - break; - case Mode.MENU: - case Mode.SETTINGS: - case Mode.ACHIEVEMENTS: - this.ui.revertMode(); - this.playSound('select'); - inputSuccess = true; - break; - default: - return; - } - } else if (this.ui?.getHandler() instanceof StarterSelectUiHandler) { - if (this.buttonJustPressed(Button.CYCLE_SHINY)) { - inputSuccess = this.ui.processInput(Button.CYCLE_SHINY); - this.setLastProcessedMovementTime(Button.CYCLE_SHINY); - } else if (this.buttonJustPressed(Button.CYCLE_FORM)) { - inputSuccess = this.ui.processInput(Button.CYCLE_FORM); - this.setLastProcessedMovementTime(Button.CYCLE_FORM); - } else if (this.buttonJustPressed(Button.CYCLE_GENDER)) { - inputSuccess = this.ui.processInput(Button.CYCLE_GENDER); - this.setLastProcessedMovementTime(Button.CYCLE_GENDER); - } else if (this.buttonJustPressed(Button.CYCLE_ABILITY)) { - inputSuccess = this.ui.processInput(Button.CYCLE_ABILITY); - this.setLastProcessedMovementTime(Button.CYCLE_ABILITY); - } else if (this.buttonJustPressed(Button.CYCLE_NATURE)) { - inputSuccess = this.ui.processInput(Button.CYCLE_NATURE); - this.setLastProcessedMovementTime(Button.CYCLE_NATURE); - } else if (this.buttonJustPressed(Button.CYCLE_VARIANT)) { - inputSuccess = this.ui.processInput(Button.CYCLE_VARIANT); - this.setLastProcessedMovementTime(Button.CYCLE_VARIANT); - } else - return; - } else if (this.buttonJustPressed(Button.SPEED_UP)) { - if (this.gameSpeed < 5) { - this.gameData.saveSetting(Setting.Game_Speed, settingOptions[Setting.Game_Speed].indexOf(`${this.gameSpeed}x`) + 1); - if (this.ui?.getMode() === Mode.SETTINGS) - (this.ui.getHandler() as SettingsUiHandler).show([]); - } - } else if (this.buttonJustPressed(Button.SLOW_DOWN)) { - if (this.gameSpeed > 1) { - this.gameData.saveSetting(Setting.Game_Speed, Math.max(settingOptions[Setting.Game_Speed].indexOf(`${this.gameSpeed}x`) - 1, 0)); - if (this.ui?.getMode() === Mode.SETTINGS) - (this.ui.getHandler() as SettingsUiHandler).show([]); - } + mapGamepad(id) { + id = id.toLowerCase(); + let padConfig = pad_generic; + + if (id.includes('081f') && id.includes('e401')) { + padConfig = pad_unlicensedSNES; + } + else if (id.includes('xbox') && id.includes('360')) { + padConfig = pad_xbox360; + } + else if (id.includes('054c')) { + padConfig = pad_dualshock; + } + + return padConfig; + } + + button_up() { + const inputSuccess = this.ui.processInput(Button.UP); + const vibrationLength = 5; + this.setLastProcessedMovementTime(Button.UP); + return [inputSuccess, vibrationLength]; + } + + button_down() { + const inputSuccess = this.ui.processInput(Button.DOWN); + const vibrationLength = 5; + this.setLastProcessedMovementTime(Button.DOWN); + return [inputSuccess, vibrationLength]; + } + + button_left() { + const inputSuccess = this.ui.processInput(Button.LEFT); + const vibrationLength = 5; + this.setLastProcessedMovementTime(Button.LEFT); + return [inputSuccess, vibrationLength]; + } + + button_right() { + const inputSuccess = this.ui.processInput(Button.RIGHT); + const vibrationLength = 5; + this.setLastProcessedMovementTime(Button.RIGHT); + return [inputSuccess, vibrationLength]; + } + + button_touch() { + const inputSuccess = this.ui.processInput(Button.SUBMIT) || this.ui.processInput(Button.ACTION); + this.setLastProcessedMovementTime(Button.SUBMIT); + return inputSuccess; + } + + button_action() { + const inputSuccess = this.ui.processInput(Button.ACTION); + this.setLastProcessedMovementTime(Button.ACTION); + return inputSuccess; + } + + button_cancel() { + const inputSuccess = this.ui.processInput(Button.CANCEL); + this.setLastProcessedMovementTime(Button.CANCEL); + return inputSuccess; + } + + button_stats(pressed = true) { + if (pressed) { + for (let p of this.getField().filter(p => p?.isActive(true))) + p.toggleStats(true); + this.setLastProcessedMovementTime(Button.STATS); } else { - let pressed = false; - if (this.ui && (this.buttonJustReleased(Button.STATS) || (pressed = this.buttonJustPressed(Button.STATS)))) { - for (let p of this.getField().filter(p => p?.isActive(true))) - p.toggleStats(pressed); - if (pressed) - this.setLastProcessedMovementTime(Button.STATS); - } else + for (let p of this.getField().filter(p => p?.isActive(true))) + p.toggleStats(false); + } + } + + button_menu() { + let inputSuccess; + if (this.disableMenu) + return; + switch (this.ui?.getMode()) { + case Mode.MESSAGE: + if (!(this.ui.getHandler() as MessageUiHandler).pendingPrompt) + return; + case Mode.TITLE: + case Mode.COMMAND: + case Mode.FIGHT: + case Mode.BALL: + case Mode.TARGET_SELECT: + case Mode.SAVE_SLOT: + case Mode.PARTY: + case Mode.SUMMARY: + case Mode.STARTER_SELECT: + case Mode.CONFIRM: + case Mode.OPTION_SELECT: + this.ui.setOverlayMode(Mode.MENU); + inputSuccess = true; + break; + case Mode.MENU: + case Mode.SETTINGS: + case Mode.ACHIEVEMENTS: + this.ui.revertMode(); + this.playSound('select'); + inputSuccess = true; + break; + default: return; } - if (inputSuccess && this.enableVibration && typeof navigator.vibrate !== 'undefined') - navigator.vibrate(vibrationLength || 10); + return inputSuccess; } - /** - * gamepadButtonJustDown returns true if @param button has just been pressed down - * or not. It will only return true once, until the key is released and pressed down - * again. - */ - gamepadButtonJustDown(button: Phaser.Input.Gamepad.Button): boolean { - if (!button || !this.gamepadSupport) - return false; - - let ret = false; - if (button.pressed) { - if (!this.gamepadButtonStates[button.index]) - ret = true; - this.gamepadButtonStates[button.index] = true; - } else - this.gamepadButtonStates[button.index] = false; - - return ret; - } - - buttonJustPressed(button: Button): boolean { - const gamepad = this.input.gamepad?.gamepads[0]; - return this.buttonKeys[button].some(k => Phaser.Input.Keyboard.JustDown(k)) || this.gamepadButtonJustDown(gamepad?.buttons[this.gamepadKeyConfig[button]]); - } - - /** - * gamepadButtonJustUp returns true if @param button has just been released - * or not. It will only return true once, until the key is released and pressed down - * again. - */ - gamepadButtonJustUp(button: Phaser.Input.Gamepad.Button): boolean { - if (!button || !this.gamepadSupport) - return false; - - return !this.gamepadButtonStates[button.index]; - } - - buttonJustReleased(button: Button): boolean { - const gamepad = this.input.gamepad?.gamepads[0]; - return this.buttonKeys[button].some(k => Phaser.Input.Keyboard.JustUp(k)) || this.gamepadButtonJustUp(gamepad?.buttons[this.gamepadKeyConfig[button]]); - } - - /** - * repeatInputDurationJustPassed returns true if @param button has been held down long - * enough to fire a repeated input. A button must claim the movementButtonLock before - * firing a repeated input - this is to prevent multiple buttons from firing repeatedly. - */ - repeatInputDurationJustPassed(button: Button): boolean { - if (this.movementButtonLock !== null && this.movementButtonLock !== button) { - return false; + button_cycle_option(button) { + let inputSuccess; + if (this.ui?.getHandler() instanceof StarterSelectUiHandler) { + inputSuccess = this.ui.processInput(button); + this.setLastProcessedMovementTime(button); } - if (this.buttonKeys[button].every(k => k.isUp) && this.gamepadButtonStates.every(b => b == false)) { - this.movementButtonLock = null; - return false; - } - if (this.time.now - this.lastProcessedButtonPressTimes.get(button) >= repeatInputDelayMillis) { - return true; + return inputSuccess; + } + + button_speed_up() { + if (this.gameSpeed < 5) { + this.gameData.saveSetting(Setting.Game_Speed, settingOptions[Setting.Game_Speed].indexOf(`${this.gameSpeed}x`) + 1); + if (this.ui?.getMode() === Mode.SETTINGS) + (this.ui.getHandler() as SettingsUiHandler).show([]); } } + button_speed_down() { + if (this.gameSpeed > 1) { + this.gameData.saveSetting(Setting.Game_Speed, Math.max(settingOptions[Setting.Game_Speed].indexOf(`${this.gameSpeed}x`) - 1, 0)); + if (this.ui?.getMode() === Mode.SETTINGS) + (this.ui.getHandler() as SettingsUiHandler).show([]); + } + } + + key_down(button) { + switch(button) { + case Button.UP: + this.button_up(); + break + case Button.DOWN: + this.button_down(); + break + case Button.LEFT: + this.button_left(); + break + case Button.RIGHT: + this.button_right(); + break + case Button.SUBMIT: + this.button_touch(); + break + case Button.ACTION: + this.button_action(); + break + case Button.CANCEL: + this.button_cancel(); + break + case Button.MENU: + this.button_menu(); + break; + case Button.STATS: + this.button_stats(true); + break + case Button.CYCLE_SHINY: + this.button_cycle_option(Button.CYCLE_SHINY); + break; + case Button.CYCLE_FORM: + this.button_cycle_option(Button.CYCLE_FORM); + break; + case Button.CYCLE_GENDER: + this.button_cycle_option(Button.CYCLE_GENDER); + break; + case Button.CYCLE_ABILITY: + this.button_cycle_option(Button.CYCLE_ABILITY); + break; + case Button.CYCLE_NATURE: + this.button_cycle_option(Button.CYCLE_NATURE); + break; + case Button.CYCLE_VARIANT: + this.button_cycle_option(Button.CYCLE_VARIANT); + break; + case Button.SPEED_UP: + this.button_speed_up(); + break + case Button.SLOW_DOWN: + this.button_speed_down(); + break + + } + } + + key_up(button) { + switch(button) { + case Button.STATS: + this.button_stats(false); + break + } + } + + checkInputKeyboard() { + this.buttonKeys.forEach((row, index) => { + for (const key of row) { + key.on('down', () => { + if (!this.isButtonPressing) { + this.isButtonPressing = true; + this.key_down(index); + } + }); + key.on('up', () => { + if (this.isButtonPressing) { + this.isButtonPressing = false; + this.key_up(index); + } + }); + } + }) + } + checkInputGamepad() { + this.input.gamepad.once('connected', function (pad) { + const gamepadID = pad.id.toLowerCase(); + this.scene.mappedPad = this.scene.mapGamepad(gamepadID); + }); + + this.input.gamepad.on('down', function (gamepad, button) { + if (!this.scene.mappedPad) return; + let inputSuccess; + let vibrationLength; + switch(button.index) { + case this.scene.mappedPad.gamepadMapping.LC_N: + [inputSuccess, vibrationLength] = this.scene.button_up(); + break; + case this.scene.mappedPad.gamepadMapping.LC_S: + [inputSuccess, vibrationLength] = this.scene.button_down(); + break; + case this.scene.mappedPad.gamepadMapping.LC_W: + [inputSuccess, vibrationLength] = this.scene.button_left(); + break + case this.scene.mappedPad.gamepadMapping.LC_E: + [inputSuccess, vibrationLength] = this.scene.button_right(); + break + case this.scene.mappedPad.gamepadMapping.TOUCH: + inputSuccess = this.scene.button_touch(); + break + case this.scene.mappedPad.gamepadMapping.RC_S: + inputSuccess = this.scene.button_action(); + break; + case this.scene.mappedPad.gamepadMapping.RC_E: + inputSuccess = this.scene.button_cancel(); + break + case this.scene.mappedPad.gamepadMapping.SELECT: + this.scene.button_stats(true); + break + case this.scene.mappedPad.gamepadMapping.START: + inputSuccess = this.scene.button_menu(); + break + case this.scene.mappedPad.gamepadMapping.RB: + inputSuccess = this.scene.button_cycle_option(Button.CYCLE_SHINY); + break + case this.scene.mappedPad.gamepadMapping.LB: + inputSuccess = this.scene.button_cycle_option(Button.CYCLE_FORM); + break + case this.scene.mappedPad.gamepadMapping.LT: + inputSuccess = this.scene.button_cycle_option(Button.CYCLE_GENDER); + break + case this.scene.mappedPad.gamepadMapping.RT: + inputSuccess = this.scene.button_cycle_option(Button.CYCLE_ABILITY); + break; + case this.scene.mappedPad.gamepadMapping.RC_W: + inputSuccess = this.scene.button_cycle_option(Button.CYCLE_NATURE); + break + case this.scene.mappedPad.gamepadMapping.RC_N: + inputSuccess = this.scene.button_cycle_option(Button.CYCLE_VARIANT); + break + case this.scene.mappedPad.gamepadMapping.LS: + this.scene.button_speed_up(); + break + case this.scene.mappedPad.gamepadMapping.RS: + this.scene.button_speed_down(); + break + } + if (inputSuccess && this.scene.enableVibration && typeof navigator.vibrate !== 'undefined') + navigator.vibrate(vibrationLength); + }); + + this.input.gamepad.on('up', function (gamepad, button) { + switch(button.index) { + case this.scene.mappedPad.gamepadMapping.SELECT: + this.scene.button_stats(false); + break + } + }); + } + + setLastProcessedMovementTime(button: Button) { this.lastProcessedButtonPressTimes.set(button, this.time.now); this.movementButtonLock = button;