rework of the input handling, including different gamepad and keyboard

This commit is contained in:
Greenlamp 2024-05-02 00:04:03 +02:00
parent c8583d6c69
commit 1a85a49ea6

View File

@ -61,6 +61,10 @@ import CandyBar from './ui/candy-bar';
import { Variant, variantData } from './data/variant'; import { Variant, variantData } from './data/variant';
import { Localizable } from './plugins/i18n'; import { Localizable } from './plugins/i18n';
import { STARTING_WAVE_OVERRIDE, OPP_SPECIES_OVERRIDE, SEED_OVERRIDE, STARTING_BIOME_OVERRIDE } from './overrides'; 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"; 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; export const startingWave = STARTING_WAVE_OVERRIDE || 1;
const expSpriteKeys: string[] = []; const expSpriteKeys: string[] = [];
const repeatInputDelayMillis = 250;
export let starterColors: StarterColors; export let starterColors: StarterColors;
interface StarterColors { interface StarterColors {
@ -191,32 +194,12 @@ export default class BattleScene extends SceneBase {
private playTimeTimer: Phaser.Time.TimerEvent; private playTimeTimer: Phaser.Time.TimerEvent;
private buttonKeys: Phaser.Input.Keyboard.Key[][]; private buttonKeys: Phaser.Input.Keyboard.Key[][];
private isButtonPressing: boolean;
private lastProcessedButtonPressTimes: Map<Button, number> = new Map(); private lastProcessedButtonPressTimes: Map<Button, number> = new Map();
// movementButtonLock ensures only a single movement key is firing repeated inputs // movementButtonLock ensures only a single movement key is firing repeated inputs
// (i.e. by holding down a button) at a time // (i.e. by holding down a button) at a time
private movementButtonLock: Button; private movementButtonLock: Button;
private mappedPad;
// 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);
public rngCounter: integer = 0; public rngCounter: integer = 0;
public rngSeedOverride: string = ''; public rngSeedOverride: string = '';
@ -274,6 +257,8 @@ export default class BattleScene extends SceneBase {
addUiThemeOverrides(this); addUiThemeOverrides(this);
this.setupControls(); this.setupControls();
this.checkInputGamepad();
this.checkInputKeyboard();
this.load.setBaseURL(); this.load.setBaseURL();
@ -287,7 +272,6 @@ export default class BattleScene extends SceneBase {
} }
update() { update() {
this.checkInput();
this.ui?.update(); this.ui?.update();
} }
@ -1343,172 +1327,300 @@ export default class BattleScene extends SceneBase {
return biomes[Utils.randSeedInt(biomes.length)]; return biomes[Utils.randSeedInt(biomes.length)];
} }
checkInput(): boolean { mapGamepad(id) {
let inputSuccess = false; id = id.toLowerCase();
let vibrationLength = 0; let padConfig = pad_generic;
if (this.buttonJustPressed(Button.UP) || this.repeatInputDurationJustPassed(Button.UP)) {
inputSuccess = this.ui.processInput(Button.UP); if (id.includes('081f') && id.includes('e401')) {
vibrationLength = 5; padConfig = pad_unlicensedSNES;
this.setLastProcessedMovementTime(Button.UP) }
} else if (this.buttonJustPressed(Button.DOWN) || this.repeatInputDurationJustPassed(Button.DOWN)) { else if (id.includes('xbox') && id.includes('360')) {
inputSuccess = this.ui.processInput(Button.DOWN); padConfig = pad_xbox360;
vibrationLength = 5; }
this.setLastProcessedMovementTime(Button.DOWN) else if (id.includes('054c')) {
} else if (this.buttonJustPressed(Button.LEFT) || this.repeatInputDurationJustPassed(Button.LEFT)) { padConfig = pad_dualshock;
inputSuccess = this.ui.processInput(Button.LEFT); }
vibrationLength = 5;
this.setLastProcessedMovementTime(Button.LEFT) return padConfig;
} else if (this.buttonJustPressed(Button.RIGHT) || this.repeatInputDurationJustPassed(Button.RIGHT)) { }
inputSuccess = this.ui.processInput(Button.RIGHT);
vibrationLength = 5; button_up() {
this.setLastProcessedMovementTime(Button.RIGHT) const inputSuccess = this.ui.processInput(Button.UP);
} else if (this.buttonJustPressed(Button.SUBMIT) || this.repeatInputDurationJustPassed(Button.SUBMIT)) { const vibrationLength = 5;
inputSuccess = this.ui.processInput(Button.SUBMIT) || this.ui.processInput(Button.ACTION); this.setLastProcessedMovementTime(Button.UP);
this.setLastProcessedMovementTime(Button.SUBMIT); return [inputSuccess, vibrationLength];
} else if (this.buttonJustPressed(Button.ACTION) || this.repeatInputDurationJustPassed(Button.ACTION)) { }
inputSuccess = this.ui.processInput(Button.ACTION);
this.setLastProcessedMovementTime(Button.ACTION); button_down() {
} else if (this.buttonJustPressed(Button.CANCEL)|| this.repeatInputDurationJustPassed(Button.CANCEL)) { const inputSuccess = this.ui.processInput(Button.DOWN);
inputSuccess = this.ui.processInput(Button.CANCEL); const vibrationLength = 5;
this.setLastProcessedMovementTime(Button.CANCEL); this.setLastProcessedMovementTime(Button.DOWN);
} else if (this.buttonJustPressed(Button.MENU)) { return [inputSuccess, vibrationLength];
if (this.disableMenu) }
return;
switch (this.ui?.getMode()) { button_left() {
case Mode.MESSAGE: const inputSuccess = this.ui.processInput(Button.LEFT);
if (!(this.ui.getHandler() as MessageUiHandler).pendingPrompt) const vibrationLength = 5;
return; this.setLastProcessedMovementTime(Button.LEFT);
case Mode.TITLE: return [inputSuccess, vibrationLength];
case Mode.COMMAND: }
case Mode.FIGHT:
case Mode.BALL: button_right() {
case Mode.TARGET_SELECT: const inputSuccess = this.ui.processInput(Button.RIGHT);
case Mode.SAVE_SLOT: const vibrationLength = 5;
case Mode.PARTY: this.setLastProcessedMovementTime(Button.RIGHT);
case Mode.SUMMARY: return [inputSuccess, vibrationLength];
case Mode.STARTER_SELECT: }
case Mode.CONFIRM:
case Mode.OPTION_SELECT: button_touch() {
this.ui.setOverlayMode(Mode.MENU); const inputSuccess = this.ui.processInput(Button.SUBMIT) || this.ui.processInput(Button.ACTION);
inputSuccess = true; this.setLastProcessedMovementTime(Button.SUBMIT);
break; return inputSuccess;
case Mode.MENU: }
case Mode.SETTINGS:
case Mode.ACHIEVEMENTS: button_action() {
this.ui.revertMode(); const inputSuccess = this.ui.processInput(Button.ACTION);
this.playSound('select'); this.setLastProcessedMovementTime(Button.ACTION);
inputSuccess = true; return inputSuccess;
break; }
default:
return; button_cancel() {
} const inputSuccess = this.ui.processInput(Button.CANCEL);
} else if (this.ui?.getHandler() instanceof StarterSelectUiHandler) { this.setLastProcessedMovementTime(Button.CANCEL);
if (this.buttonJustPressed(Button.CYCLE_SHINY)) { return inputSuccess;
inputSuccess = this.ui.processInput(Button.CYCLE_SHINY); }
this.setLastProcessedMovementTime(Button.CYCLE_SHINY);
} else if (this.buttonJustPressed(Button.CYCLE_FORM)) { button_stats(pressed = true) {
inputSuccess = this.ui.processInput(Button.CYCLE_FORM); if (pressed) {
this.setLastProcessedMovementTime(Button.CYCLE_FORM); for (let p of this.getField().filter(p => p?.isActive(true)))
} else if (this.buttonJustPressed(Button.CYCLE_GENDER)) { p.toggleStats(true);
inputSuccess = this.ui.processInput(Button.CYCLE_GENDER); this.setLastProcessedMovementTime(Button.STATS);
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([]);
}
} else { } else {
let pressed = false; for (let p of this.getField().filter(p => p?.isActive(true)))
if (this.ui && (this.buttonJustReleased(Button.STATS) || (pressed = this.buttonJustPressed(Button.STATS)))) { p.toggleStats(false);
for (let p of this.getField().filter(p => p?.isActive(true))) }
p.toggleStats(pressed); }
if (pressed)
this.setLastProcessedMovementTime(Button.STATS); button_menu() {
} else 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; return;
} }
if (inputSuccess && this.enableVibration && typeof navigator.vibrate !== 'undefined') return inputSuccess;
navigator.vibrate(vibrationLength || 10);
} }
/** button_cycle_option(button) {
* gamepadButtonJustDown returns true if @param button has just been pressed down let inputSuccess;
* or not. It will only return true once, until the key is released and pressed down if (this.ui?.getHandler() instanceof StarterSelectUiHandler) {
* again. inputSuccess = this.ui.processInput(button);
*/ this.setLastProcessedMovementTime(button);
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;
} }
if (this.buttonKeys[button].every(k => k.isUp) && this.gamepadButtonStates.every(b => b == false)) { return inputSuccess;
this.movementButtonLock = null; }
return false;
} button_speed_up() {
if (this.time.now - this.lastProcessedButtonPressTimes.get(button) >= repeatInputDelayMillis) { if (this.gameSpeed < 5) {
return true; 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) { setLastProcessedMovementTime(button: Button) {
this.lastProcessedButtonPressTimes.set(button, this.time.now); this.lastProcessedButtonPressTimes.set(button, this.time.now);
this.movementButtonLock = button; this.movementButtonLock = button;