From 7a75fc925a0ad16139d1fc6ea3ac417b6849c7aa Mon Sep 17 00:00:00 2001 From: Greenlamp Date: Wed, 8 May 2024 16:40:41 +0200 Subject: [PATCH] added gamepad section in Game Settings and related option into it --- src/system/game-data.ts | 18 ++ src/system/settings-gamepad.ts | 30 +++ src/system/settings.ts | 12 -- src/ui-inputs.ts | 3 +- src/ui/settings-gamepad-ui-handler.ts | 252 ++++++++++++++++++++++++++ src/ui/settings-ui-handler.ts | 13 +- src/ui/ui.ts | 3 + 7 files changed, 315 insertions(+), 16 deletions(-) create mode 100644 src/system/settings-gamepad.ts create mode 100644 src/ui/settings-gamepad-ui-handler.ts diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 60fe7ac8e..418965b06 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -29,6 +29,7 @@ import { allMoves } from "../data/move"; import { TrainerVariant } from "../field/trainer"; import { OutdatedPhase, ReloadSessionPhase } from "#app/phases"; import { Variant, variantData } from "#app/data/variant"; +import {setSettingGamepad, SettingGamepad, settingGamepadDefaults} from "./system/settings-gamepad"; const saveKey = 'x0i2O7WRiANTqPmZ'; // Temporary; secure encryption is not yet necessary @@ -464,31 +465,48 @@ export class GameData { public saveSetting(setting: Setting, valueIndex: integer): boolean { let settings: object = {}; + let settingsGamepad: object = {}; if (localStorage.hasOwnProperty('settings')) settings = JSON.parse(localStorage.getItem('settings')); + if (localStorage.hasOwnProperty('settingsGamepad')) + settingsGamepad = JSON.parse(localStorage.getItem('settingsGamepad')); setSetting(this.scene, setting as Setting, valueIndex); + setSettingGamepad(this.scene, settingsGamepad as SettingGamepad, valueIndex); Object.keys(settingDefaults).forEach(s => { if (s === setting) settings[s] = valueIndex; }); + Object.keys(settingGamepadDefaults).forEach(s => { + if (s === setting) + settingsGamepad[s] = valueIndex; + }); + localStorage.setItem('settings', JSON.stringify(settings)); + localStorage.setItem('settingsGamepad', JSON.stringify(settingsGamepad)); return true; } private loadSettings(): boolean { Object.values(Setting).map(setting => setting as Setting).forEach(setting => setSetting(this.scene, setting, settingDefaults[setting])); + Object.values(SettingGamepad).map(setting => setting as SettingGamepad).forEach(setting => setSettingGamepad(this.scene, setting, settingGamepadDefaults[setting])); if (!localStorage.hasOwnProperty('settings')) return false; + if (!localStorage.hasOwnProperty('settingsGamepad')) + return false; const settings = JSON.parse(localStorage.getItem('settings')); + const settingsGamepad = JSON.parse(localStorage.getItem('settingsGamepad')); for (let setting of Object.keys(settings)) setSetting(this.scene, setting as Setting, settings[setting]); + + for (let setting of Object.keys(settingsGamepad)) + setSettingGamepad(this.scene, setting as SettingGamepad, settingGamepadDefaults[setting]); } public saveTutorialFlag(tutorial: Tutorial, flag: boolean): boolean { diff --git a/src/system/settings-gamepad.ts b/src/system/settings-gamepad.ts new file mode 100644 index 000000000..605f02006 --- /dev/null +++ b/src/system/settings-gamepad.ts @@ -0,0 +1,30 @@ +import BattleScene from "../battle-scene"; +import {SettingDefaults, SettingOptions} from "#app/system/settings"; + +export enum SettingGamepad { + Gamepad_Support = "GAMEPAD_SUPPORT", + Swap_A_and_B = "SWAP_A_B", // Swaps which gamepad button handles ACTION and CANCEL +} + +export const settingGamepadOptions: SettingOptions = { + [SettingGamepad.Gamepad_Support]: [ 'Auto', 'Disabled' ], + [SettingGamepad.Swap_A_and_B]: [ 'Enabled', 'Disabled' ], +}; + +export const settingGamepadDefaults: SettingDefaults = { + [SettingGamepad.Gamepad_Support]: 0, + [SettingGamepad.Swap_A_and_B]: 1, // Set to 'Disabled' by default +}; + +export function setSettingGamepad(scene: BattleScene, setting: SettingGamepad, value: integer): boolean { + switch (setting) { + case SettingGamepad.Gamepad_Support: + scene.gamepadSupport = settingGamepadOptions[setting][value] !== 'Disabled'; + break; + case SettingGamepad.Swap_A_and_B: + scene.abSwapped = settingGamepadOptions[setting][value] !== 'Disabled'; + break; + } + + return true; +} diff --git a/src/system/settings.ts b/src/system/settings.ts index 3805a35bb..9196e5945 100644 --- a/src/system/settings.ts +++ b/src/system/settings.ts @@ -24,8 +24,6 @@ export enum Setting { HP_Bar_Speed = "HP_BAR_SPEED", Fusion_Palette_Swaps = "FUSION_PALETTE_SWAPS", Player_Gender = "PLAYER_GENDER", - Gamepad_Support = "GAMEPAD_SUPPORT", - Swap_A_and_B = "SWAP_A_B", // Swaps which gamepad button handles ACTION and CANCEL Touch_Controls = "TOUCH_CONTROLS", Vibration = "VIBRATION" } @@ -56,8 +54,6 @@ export const settingOptions: SettingOptions = { [Setting.HP_Bar_Speed]: [ 'Normal', 'Fast', 'Faster', 'Instant' ], [Setting.Fusion_Palette_Swaps]: [ 'Off', 'On' ], [Setting.Player_Gender]: [ 'Boy', 'Girl' ], - [Setting.Gamepad_Support]: [ 'Auto', 'Disabled' ], - [Setting.Swap_A_and_B]: [ 'Enabled', 'Disabled' ], [Setting.Touch_Controls]: [ 'Auto', 'Disabled' ], [Setting.Vibration]: [ 'Auto', 'Disabled' ] }; @@ -80,8 +76,6 @@ export const settingDefaults: SettingDefaults = { [Setting.HP_Bar_Speed]: 0, [Setting.Fusion_Palette_Swaps]: 1, [Setting.Player_Gender]: 0, - [Setting.Gamepad_Support]: 0, - [Setting.Swap_A_and_B]: 1, // Set to 'Disabled' by default [Setting.Touch_Controls]: 0, [Setting.Vibration]: 0 }; @@ -148,12 +142,6 @@ export function setSetting(scene: BattleScene, setting: Setting, value: integer) } else return false; break; - case Setting.Gamepad_Support: - scene.gamepadSupport = settingOptions[setting][value] !== 'Disabled'; - break; - case Setting.Swap_A_and_B: - scene.abSwapped = settingOptions[setting][value] !== 'Disabled'; - break; case Setting.Touch_Controls: scene.enableTouchControls = settingOptions[setting][value] !== 'Disabled' && hasTouchscreen(); const touchControls = document.getElementById('touchControls'); diff --git a/src/ui-inputs.ts b/src/ui-inputs.ts index b304880d0..9f484355e 100644 --- a/src/ui-inputs.ts +++ b/src/ui-inputs.ts @@ -6,6 +6,7 @@ import StarterSelectUiHandler from "./ui/starter-select-ui-handler"; import {Setting, settingOptions} from "./system/settings"; import SettingsUiHandler from "./ui/settings-ui-handler"; import {Button} from "./enums/buttons"; +import SettingsGamepadUiHandler from "#app/ui/settings-gamepad-ui-handler"; export interface ActionKeys { [key in Button]: () => void; @@ -130,7 +131,7 @@ export class UiInputs { } buttonCycleOption(button: Button): void { - const whitelist = [StarterSelectUiHandler, SettingsUiHandler]; + const whitelist = [StarterSelectUiHandler, SettingsUiHandler, SettingsGamepadUiHandler]; const uiHandler = this.scene.ui?.getHandler(); if (whitelist.some(handler => uiHandler instanceof handler)) { this.scene.ui.processInput(button); diff --git a/src/ui/settings-gamepad-ui-handler.ts b/src/ui/settings-gamepad-ui-handler.ts new file mode 100644 index 000000000..89b6f1c8c --- /dev/null +++ b/src/ui/settings-gamepad-ui-handler.ts @@ -0,0 +1,252 @@ +import BattleScene from "../battle-scene"; +import { hasTouchscreen, isMobile } from "../touch-controls"; +import { TextStyle, addTextObject } from "./text"; +import { Mode } from "./ui"; +import UiHandler from "./ui-handler"; +import { addWindow } from "./ui-theme"; +import {Button} from "../enums/buttons"; +import {SettingGamepad, settingGamepadDefaults, settingGamepadOptions} from "../system/settings-gamepad"; + +export default class SettingsGamepadUiHandler extends UiHandler { + private settingsContainer: Phaser.GameObjects.Container; + private optionsContainer: Phaser.GameObjects.Container; + + private scrollCursor: integer; + + private optionsBg: Phaser.GameObjects.NineSlice; + + private optionCursors: integer[]; + + private settingLabels: Phaser.GameObjects.Text[]; + private optionValueLabels: Phaser.GameObjects.Text[][]; + + private cursorObj: Phaser.GameObjects.NineSlice; + + private reloadRequired: boolean; + private reloadI18n: boolean; + + constructor(scene: BattleScene, mode?: Mode) { + super(scene, mode); + + this.reloadRequired = false; + this.reloadI18n = false; + } + + setup() { + const ui = this.getUi(); + + this.settingsContainer = this.scene.add.container(1, -(this.scene.game.canvas.height / 6) + 1); + + this.settingsContainer.setInteractive(new Phaser.Geom.Rectangle(0, 0, this.scene.game.canvas.width / 6, this.scene.game.canvas.height / 6), Phaser.Geom.Rectangle.Contains); + + const headerBg = addWindow(this.scene, 0, 0, (this.scene.game.canvas.width / 6) - 2, 24); + headerBg.setOrigin(0, 0); + + const headerText = addTextObject(this.scene, 0, 0, 'General', TextStyle.SETTINGS_LABEL); + headerText.setOrigin(0, 0); + headerText.setPositionRelative(headerBg, 8, 4); + + const gamepadText = addTextObject(this.scene, 0, 0, 'Gamepad', TextStyle.SETTINGS_SELECTED); + gamepadText.setOrigin(0, 0); + gamepadText.setPositionRelative(headerBg, 50, 4); + + this.optionsBg = addWindow(this.scene, 0, headerBg.height, (this.scene.game.canvas.width / 6) - 2, (this.scene.game.canvas.height / 6) - headerBg.height - 2); + this.optionsBg.setOrigin(0, 0); + + this.optionsContainer = this.scene.add.container(0, 0); + + this.settingLabels = []; + this.optionValueLabels = []; + + Object.keys(SettingGamepad).forEach((setting, s) => { + let settingName = setting.replace(/\_/g, ' '); + + this.settingLabels[s] = addTextObject(this.scene, 8, 28 + s * 16, settingName, TextStyle.SETTINGS_LABEL); + this.settingLabels[s].setOrigin(0, 0); + + this.optionsContainer.add(this.settingLabels[s]); + + this.optionValueLabels.push(settingGamepadOptions[SettingGamepad[setting]].map((option, o) => { + const valueLabel = addTextObject(this.scene, 0, 0, option, settingGamepadDefaults[SettingGamepad[setting]] === o ? TextStyle.SETTINGS_SELECTED : TextStyle.WINDOW); + valueLabel.setOrigin(0, 0); + + this.optionsContainer.add(valueLabel); + + return valueLabel; + })); + + const totalWidth = this.optionValueLabels[s].map(o => o.width).reduce((total, width) => total += width, 0); + + const labelWidth = Math.max(78, this.settingLabels[s].displayWidth + 8); + + const totalSpace = (300 - labelWidth) - totalWidth / 6; + const optionSpacing = Math.floor(totalSpace / (this.optionValueLabels[s].length - 1)); + + let xOffset = 0; + + for (let value of this.optionValueLabels[s]) { + value.setPositionRelative(this.settingLabels[s], labelWidth + xOffset, 0); + xOffset += value.width / 6 + optionSpacing; + } + }); + + this.optionCursors = Object.values(settingGamepadDefaults); + + this.settingsContainer.add(headerBg); + this.settingsContainer.add(headerText); + this.settingsContainer.add(gamepadText); + this.settingsContainer.add(this.optionsBg); + this.settingsContainer.add(this.optionsContainer); + + ui.add(this.settingsContainer); + + this.setCursor(0); + this.setScrollCursor(0); + + this.settingsContainer.setVisible(false); + } + + show(args: any[]): boolean { + super.show(args); + + const settings: object = localStorage.hasOwnProperty('settings') ? JSON.parse(localStorage.getItem('settings')) : {}; + + Object.keys(settingGamepadDefaults).forEach((setting, s) => this.setOptionCursor(s, settings.hasOwnProperty(setting) ? settings[setting] : settingGamepadDefaults[setting])); + + this.settingsContainer.setVisible(true); + this.setCursor(0); + + this.getUi().moveTo(this.settingsContainer, this.getUi().length - 1); + + this.getUi().hideTooltip(); + + return true; + } + + processInput(button: Button): boolean { + const ui = this.getUi(); + + let success = false; + + if (button === Button.CANCEL) { + success = true; + this.scene.ui.revertMode(); + } else { + const cursor = this.cursor + this.scrollCursor; + switch (button) { + case Button.UP: + if (cursor) { + if (this.cursor) + success = this.setCursor(this.cursor - 1); + else + success = this.setScrollCursor(this.scrollCursor - 1); + } + break; + case Button.DOWN: + if (cursor < this.optionValueLabels.length) { + if (this.cursor < 8) + success = this.setCursor(this.cursor + 1); + else if (this.scrollCursor < this.optionValueLabels.length - 9) + success = this.setScrollCursor(this.scrollCursor + 1); + } + break; + case Button.LEFT: + if (this.optionCursors[cursor]) + success = this.setOptionCursor(cursor, this.optionCursors[cursor] - 1, true); + break; + case Button.RIGHT: + if (this.optionCursors[cursor] < this.optionValueLabels[cursor].length - 1) + success = this.setOptionCursor(cursor, this.optionCursors[cursor] + 1, true); + break; + case Button.LB: + this.scene.ui.setMode(Mode.SETTINGS) + success = true; + case Button.RB: + this.scene.ui.setMode(Mode.SETTINGS) + success = true; + break; + } + } + + if (success) + ui.playSelect(); + + return success; + } + + setCursor(cursor: integer): boolean { + const ret = super.setCursor(cursor); + + if (!this.cursorObj) { + this.cursorObj = this.scene.add.nineslice(0, 0, 'summary_moves_cursor', null, (this.scene.game.canvas.width / 6) - 10, 16, 1, 1, 1, 1); + this.cursorObj.setOrigin(0, 0); + this.optionsContainer.add(this.cursorObj); + } + + this.cursorObj.setPositionRelative(this.optionsBg, 4, 4 + (this.cursor + this.scrollCursor) * 16); + + return ret; + } + + setOptionCursor(settingIndex: integer, cursor: integer, save?: boolean): boolean { + const setting = SettingGamepad[Object.keys(SettingGamepad)[settingIndex]]; + + const lastCursor = this.optionCursors[settingIndex]; + + const lastValueLabel = this.optionValueLabels[settingIndex][lastCursor]; + lastValueLabel.setColor(this.getTextColor(TextStyle.WINDOW)); + lastValueLabel.setShadowColor(this.getTextColor(TextStyle.WINDOW, true)); + + this.optionCursors[settingIndex] = cursor; + + const newValueLabel = this.optionValueLabels[settingIndex][cursor]; + newValueLabel.setColor(this.getTextColor(TextStyle.SETTINGS_SELECTED)); + newValueLabel.setShadowColor(this.getTextColor(TextStyle.SETTINGS_SELECTED, true)); + + if (save) { + this.scene.gameData.saveSetting(setting, cursor) + } + + return true; + } + + setScrollCursor(scrollCursor: integer): boolean { + if (scrollCursor === this.scrollCursor) + return false; + + this.scrollCursor = scrollCursor; + + this.updateSettingsScroll(); + + this.setCursor(this.cursor); + + return true; + } + + updateSettingsScroll(): void { + this.optionsContainer.setY(-16 * this.scrollCursor); + + for (let s = 0; s < this.settingLabels.length; s++) { + const visible = s >= this.scrollCursor && s < this.scrollCursor + 9; + this.settingLabels[s].setVisible(visible); + for (let option of this.optionValueLabels[s]) + option.setVisible(visible); + } + } + + clear() { + super.clear(); + this.settingsContainer.setVisible(false); + this.eraseCursor(); + if (this.reloadRequired) { + this.reloadRequired = false; + this.scene.reset(true, false, true); + } + } + + eraseCursor() { + if (this.cursorObj) + this.cursorObj.destroy(); + this.cursorObj = null; + } +} \ No newline at end of file diff --git a/src/ui/settings-ui-handler.ts b/src/ui/settings-ui-handler.ts index c769068d8..87892bb6d 100644 --- a/src/ui/settings-ui-handler.ts +++ b/src/ui/settings-ui-handler.ts @@ -42,10 +42,14 @@ export default class SettingsUiHandler extends UiHandler { const headerBg = addWindow(this.scene, 0, 0, (this.scene.game.canvas.width / 6) - 2, 24); headerBg.setOrigin(0, 0); - const headerText = addTextObject(this.scene, 0, 0, 'Options', TextStyle.SETTINGS_LABEL); + const headerText = addTextObject(this.scene, 0, 0, 'General', TextStyle.SETTINGS_SELECTED); headerText.setOrigin(0, 0); headerText.setPositionRelative(headerBg, 8, 4); + const gamepadText = addTextObject(this.scene, 0, 0, 'Gamepad', TextStyle.SETTINGS_LABEL); + gamepadText.setOrigin(0, 0); + gamepadText.setPositionRelative(headerBg, 50, 4); + this.optionsBg = addWindow(this.scene, 0, headerBg.height, (this.scene.game.canvas.width / 6) - 2, (this.scene.game.canvas.height / 6) - headerBg.height - 2); this.optionsBg.setOrigin(0, 0); @@ -92,6 +96,7 @@ export default class SettingsUiHandler extends UiHandler { this.settingsContainer.add(headerBg); this.settingsContainer.add(headerText); + this.settingsContainer.add(gamepadText); this.settingsContainer.add(this.optionsBg); this.settingsContainer.add(this.optionsContainer); @@ -156,10 +161,12 @@ export default class SettingsUiHandler extends UiHandler { success = this.setOptionCursor(cursor, this.optionCursors[cursor] + 1, true); break; case Button.LB: - console.log('lb'); + this.scene.ui.setMode(Mode.SETTINGS_GAMEPAD) + success = true; break; case Button.RB: - console.log('rb'); + this.scene.ui.setMode(Mode.SETTINGS_GAMEPAD) + success = true; break; } } diff --git a/src/ui/ui.ts b/src/ui/ui.ts index 09deb2bdd..2b2c8f756 100644 --- a/src/ui/ui.ts +++ b/src/ui/ui.ts @@ -13,6 +13,7 @@ import StarterSelectUiHandler from './starter-select-ui-handler'; import EvolutionSceneHandler from './evolution-scene-handler'; import TargetSelectUiHandler from './target-select-ui-handler'; import SettingsUiHandler from './settings-ui-handler'; +import SettingsGamepadUiHandler from "./settings-gamepad-ui-handler"; import { TextStyle, addTextObject } from './text'; import AchvBar from './achv-bar'; import MenuUiHandler from './menu-ui-handler'; @@ -56,6 +57,7 @@ export enum Mode { MENU, MENU_OPTION_SELECT, SETTINGS, + SETTINGS_GAMEPAD, ACHIEVEMENTS, GAME_STATS, VOUCHERS, @@ -137,6 +139,7 @@ export default class UI extends Phaser.GameObjects.Container { new MenuUiHandler(scene), new OptionSelectUiHandler(scene, Mode.MENU_OPTION_SELECT), new SettingsUiHandler(scene), + new SettingsGamepadUiHandler(scene), new AchvsUiHandler(scene), new GameStatsUiHandler(scene), new VouchersUiHandler(scene),