Compare commits
7 Commits
145a79f8ef
...
f503080167
Author | SHA1 | Date | |
---|---|---|---|
|
f503080167 | ||
|
98cff12fd1 | ||
|
b9575d3ffc | ||
|
e0fd11746f | ||
|
69da96d543 | ||
|
763d2bfeeb | ||
|
ff0e4fbdf0 |
9
package-lock.json
generated
@ -12,6 +12,7 @@
|
|||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"i18next": "^23.11.1",
|
"i18next": "^23.11.1",
|
||||||
"i18next-browser-languagedetector": "^7.2.1",
|
"i18next-browser-languagedetector": "^7.2.1",
|
||||||
|
"i18next-korean-postposition-processor": "^1.0.0",
|
||||||
"json-stable-stringify": "^1.1.0",
|
"json-stable-stringify": "^1.1.0",
|
||||||
"phaser": "^3.70.0",
|
"phaser": "^3.70.0",
|
||||||
"phaser3-rex-plugins": "^1.1.84"
|
"phaser3-rex-plugins": "^1.1.84"
|
||||||
@ -3615,6 +3616,14 @@
|
|||||||
"cross-fetch": "4.0.0"
|
"cross-fetch": "4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/i18next-korean-postposition-processor": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/i18next-korean-postposition-processor/-/i18next-korean-postposition-processor-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-ruNXjI9awsFK6Ie+F9gYaMW8ciLMuCkeRjH9QkSv2Wb8xI0mnm773v3M9eua8dtvAXudIUk4p6Ho7hNkEASXDg==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"i18next": ">=8.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/iconv-lite": {
|
"node_modules/iconv-lite": {
|
||||||
"version": "0.6.3",
|
"version": "0.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"i18next": "^23.11.1",
|
"i18next": "^23.11.1",
|
||||||
"i18next-browser-languagedetector": "^7.2.1",
|
"i18next-browser-languagedetector": "^7.2.1",
|
||||||
|
"i18next-korean-postposition-processor": "^1.0.0",
|
||||||
"json-stable-stringify": "^1.1.0",
|
"json-stable-stringify": "^1.1.0",
|
||||||
"phaser": "^3.70.0",
|
"phaser": "^3.70.0",
|
||||||
"phaser3-rex-plugins": "^1.1.84"
|
"phaser3-rex-plugins": "^1.1.84"
|
||||||
|
BIN
public/images/ui/dawn_icon.png
Normal file
After Width: | Height: | Size: 581 B |
BIN
public/images/ui/day_icon.png
Normal file
After Width: | Height: | Size: 593 B |
BIN
public/images/ui/dusk_icon.png
Normal file
After Width: | Height: | Size: 534 B |
BIN
public/images/ui/legacy/dawn_icon.png
Normal file
After Width: | Height: | Size: 327 B |
BIN
public/images/ui/legacy/day_icon.png
Normal file
After Width: | Height: | Size: 285 B |
BIN
public/images/ui/legacy/dusk_icon.png
Normal file
After Width: | Height: | Size: 300 B |
BIN
public/images/ui/legacy/night_icon.png
Normal file
After Width: | Height: | Size: 288 B |
BIN
public/images/ui/night_icon.png
Normal file
After Width: | Height: | Size: 686 B |
@ -58,6 +58,7 @@ import {InputsController} from "./inputs-controller";
|
|||||||
import {UiInputs} from "./ui-inputs";
|
import {UiInputs} from "./ui-inputs";
|
||||||
import { MoneyFormat } from "./enums/money-format";
|
import { MoneyFormat } from "./enums/money-format";
|
||||||
import { NewArenaEvent } from "./battle-scene-events";
|
import { NewArenaEvent } from "./battle-scene-events";
|
||||||
|
import ArenaFlyout from "./ui/arena-flyout";
|
||||||
|
|
||||||
export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1";
|
export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1";
|
||||||
|
|
||||||
@ -92,6 +93,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
public damageNumbersMode: integer = 0;
|
public damageNumbersMode: integer = 0;
|
||||||
public reroll: boolean = false;
|
public reroll: boolean = false;
|
||||||
public showMovesetFlyout: boolean = true;
|
public showMovesetFlyout: boolean = true;
|
||||||
|
public showArenaFlyout: boolean = true;
|
||||||
public showLevelUpStats: boolean = true;
|
public showLevelUpStats: boolean = true;
|
||||||
public enableTutorials: boolean = import.meta.env.VITE_BYPASS_TUTORIAL === "1";
|
public enableTutorials: boolean = import.meta.env.VITE_BYPASS_TUTORIAL === "1";
|
||||||
public enableRetries: boolean = false;
|
public enableRetries: boolean = false;
|
||||||
@ -178,6 +180,8 @@ export default class BattleScene extends SceneBase {
|
|||||||
private luckText: Phaser.GameObjects.Text;
|
private luckText: Phaser.GameObjects.Text;
|
||||||
private modifierBar: ModifierBar;
|
private modifierBar: ModifierBar;
|
||||||
private enemyModifierBar: ModifierBar;
|
private enemyModifierBar: ModifierBar;
|
||||||
|
public arenaFlyout: ArenaFlyout;
|
||||||
|
|
||||||
private fieldOverlay: Phaser.GameObjects.Rectangle;
|
private fieldOverlay: Phaser.GameObjects.Rectangle;
|
||||||
private modifiers: PersistentModifier[];
|
private modifiers: PersistentModifier[];
|
||||||
private enemyModifiers: PersistentModifier[];
|
private enemyModifiers: PersistentModifier[];
|
||||||
@ -411,6 +415,10 @@ export default class BattleScene extends SceneBase {
|
|||||||
this.luckLabelText.setVisible(false);
|
this.luckLabelText.setVisible(false);
|
||||||
this.fieldUI.add(this.luckLabelText);
|
this.fieldUI.add(this.luckLabelText);
|
||||||
|
|
||||||
|
this.arenaFlyout = new ArenaFlyout(this);
|
||||||
|
this.fieldUI.add(this.arenaFlyout);
|
||||||
|
this.fieldUI.moveBelow<Phaser.GameObjects.GameObject>(this.arenaFlyout, this.fieldOverlay);
|
||||||
|
|
||||||
this.updateUIPositions();
|
this.updateUIPositions();
|
||||||
|
|
||||||
this.damageNumberHandler = new DamageNumberHandler();
|
this.damageNumberHandler = new DamageNumberHandler();
|
||||||
@ -1272,6 +1280,13 @@ export default class BattleScene extends SceneBase {
|
|||||||
return sprite;
|
return sprite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
moveBelowOverlay<T extends Phaser.GameObjects.GameObject>(gameObject: T) {
|
||||||
|
this.fieldUI.moveBelow<any>(gameObject, this.fieldOverlay);
|
||||||
|
}
|
||||||
|
processInfoButton(pressed: boolean): void {
|
||||||
|
this.arenaFlyout.toggleFlyout(pressed);
|
||||||
|
}
|
||||||
|
|
||||||
showFieldOverlay(duration: integer): Promise<void> {
|
showFieldOverlay(duration: integer): Promise<void> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
this.tweens.add({
|
this.tweens.add({
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {Button} from "#app/enums/buttons";
|
import {Button} from "#app/enums/buttons";
|
||||||
import {SettingKeyboard} from "#app/system/settings-keyboard";
|
import {SettingKeyboard} from "#app/system/settings/settings-keyboard";
|
||||||
|
|
||||||
const cfg_keyboard_qwerty = {
|
const cfg_keyboard_qwerty = {
|
||||||
padID: "default",
|
padID: "default",
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {SettingGamepad} from "../../system/settings-gamepad";
|
import {SettingGamepad} from "../../system/settings/settings-gamepad";
|
||||||
import {Button} from "../../enums/buttons";
|
import {Button} from "../../enums/buttons";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {SettingGamepad} from "../../system/settings-gamepad";
|
import {SettingGamepad} from "../../system/settings/settings-gamepad";
|
||||||
import {Button} from "../../enums/buttons";
|
import {Button} from "../../enums/buttons";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {SettingGamepad} from "#app/system/settings-gamepad";
|
import {SettingGamepad} from "#app/system/settings/settings-gamepad.js";
|
||||||
import {Button} from "#app/enums/buttons";
|
import {Button} from "#app/enums/buttons";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {SettingGamepad} from "../../system/settings-gamepad";
|
import {SettingGamepad} from "../../system/settings/settings-gamepad";
|
||||||
import {Button} from "../../enums/buttons";
|
import {Button} from "../../enums/buttons";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {SettingGamepad} from "../../system/settings-gamepad";
|
import {SettingGamepad} from "../../system/settings/settings-gamepad";
|
||||||
import {Button} from "#app/enums/buttons";
|
import {Button} from "#app/enums/buttons";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1341,7 +1341,7 @@ export class CursedTag extends BattlerTag {
|
|||||||
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
|
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
|
||||||
|
|
||||||
if (!cancelled.value) {
|
if (!cancelled.value) {
|
||||||
pokemon.damageAndUpdate(Math.floor(pokemon.getMaxHp() / 4));
|
pokemon.damageAndUpdate(Math.max(Math.floor(pokemon.getMaxHp() / 4), 1));
|
||||||
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` is hurt by the ${this.getMoveName()}!`));
|
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` is hurt by the ${this.getMoveName()}!`));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1201,6 +1201,29 @@ export class BoostHealAttr extends HealAttr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Heals the target only if it is the ally
|
||||||
|
* @extends HealAttr
|
||||||
|
* @see {@linkcode apply}
|
||||||
|
*/
|
||||||
|
export class HealOnAllyAttr extends HealAttr {
|
||||||
|
/**
|
||||||
|
* @param user {@linkcode Pokemon} using the move
|
||||||
|
* @param target {@linkcode Pokemon} target of the move
|
||||||
|
* @param move {@linkcode Move} with this attribute
|
||||||
|
* @param args N/A
|
||||||
|
* @returns true if the function succeeds
|
||||||
|
*/
|
||||||
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
|
if (user.getAlly() === target) {
|
||||||
|
super.apply(user, target, move, args);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Heals user as a side effect of a move that hits a target.
|
* Heals user as a side effect of a move that hits a target.
|
||||||
* Healing is based on {@linkcode healRatio} * the amount of damage dealt or a stat of the target.
|
* Healing is based on {@linkcode healRatio} * the amount of damage dealt or a stat of the target.
|
||||||
@ -3043,6 +3066,31 @@ export class TeraBlastCategoryAttr extends VariableMoveCategoryAttr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the move category to status when used on the ally
|
||||||
|
* @extends VariableMoveCategoryAttr
|
||||||
|
* @see {@linkcode apply}
|
||||||
|
*/
|
||||||
|
export class StatusCategoryOnAllyAttr extends VariableMoveCategoryAttr {
|
||||||
|
/**
|
||||||
|
* @param user {@linkcode Pokemon} using the move
|
||||||
|
* @param target {@linkcode Pokemon} target of the move
|
||||||
|
* @param move {@linkcode Move} with this attribute
|
||||||
|
* @param args [0] {@linkcode Utils.IntegerHolder} The category of the move
|
||||||
|
* @returns true if the function succeeds
|
||||||
|
*/
|
||||||
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
|
const category = (args[0] as Utils.IntegerHolder);
|
||||||
|
|
||||||
|
if (user.getAlly() === target) {
|
||||||
|
category.value = MoveCategory.STATUS;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class ShellSideArmCategoryAttr extends VariableMoveCategoryAttr {
|
export class ShellSideArmCategoryAttr extends VariableMoveCategoryAttr {
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
const category = (args[0] as Utils.IntegerHolder);
|
const category = (args[0] as Utils.IntegerHolder);
|
||||||
@ -6966,8 +7014,9 @@ export function initMoves() {
|
|||||||
new AttackMove(Moves.THROAT_CHOP, Type.DARK, MoveCategory.PHYSICAL, 80, 100, 15, 100, 0, 7)
|
new AttackMove(Moves.THROAT_CHOP, Type.DARK, MoveCategory.PHYSICAL, 80, 100, 15, 100, 0, 7)
|
||||||
.partial(),
|
.partial(),
|
||||||
new AttackMove(Moves.POLLEN_PUFF, Type.BUG, MoveCategory.SPECIAL, 90, 100, 15, -1, 0, 7)
|
new AttackMove(Moves.POLLEN_PUFF, Type.BUG, MoveCategory.SPECIAL, 90, 100, 15, -1, 0, 7)
|
||||||
.ballBombMove()
|
.attr(StatusCategoryOnAllyAttr)
|
||||||
.partial(),
|
.attr(HealOnAllyAttr, 0.5, true, false)
|
||||||
|
.ballBombMove(),
|
||||||
new AttackMove(Moves.ANCHOR_SHOT, Type.STEEL, MoveCategory.PHYSICAL, 80, 100, 20, -1, 0, 7)
|
new AttackMove(Moves.ANCHOR_SHOT, Type.STEEL, MoveCategory.PHYSICAL, 80, 100, 20, -1, 0, 7)
|
||||||
.attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, false, 1),
|
.attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, false, 1),
|
||||||
new StatusMove(Moves.PSYCHIC_TERRAIN, Type.PSYCHIC, -1, 10, -1, 0, 7)
|
new StatusMove(Moves.PSYCHIC_TERRAIN, Type.PSYCHIC, -1, 10, -1, 0, 7)
|
||||||
|
@ -497,11 +497,19 @@ export class EggHatchPhase extends Phase {
|
|||||||
|
|
||||||
const ignoredSpecies = [ Species.PHIONE, Species.MANAPHY, Species.ETERNATUS ];
|
const ignoredSpecies = [ Species.PHIONE, Species.MANAPHY, Species.ETERNATUS ];
|
||||||
|
|
||||||
const speciesPool = Object.keys(speciesStarters)
|
let speciesPool = Object.keys(speciesStarters)
|
||||||
.filter(s => speciesStarters[s] >= minStarterValue && speciesStarters[s] <= maxStarterValue)
|
.filter(s => speciesStarters[s] >= minStarterValue && speciesStarters[s] <= maxStarterValue)
|
||||||
.map(s => parseInt(s) as Species)
|
.map(s => parseInt(s) as Species)
|
||||||
.filter(s => !pokemonPrevolutions.hasOwnProperty(s) && getPokemonSpecies(s).isObtainable() && ignoredSpecies.indexOf(s) === -1);
|
.filter(s => !pokemonPrevolutions.hasOwnProperty(s) && getPokemonSpecies(s).isObtainable() && ignoredSpecies.indexOf(s) === -1);
|
||||||
|
|
||||||
|
// If this is the 10th egg without unlocking something new, attempt to force it.
|
||||||
|
if (this.scene.gameData.unlockPity[this.egg.tier] >= 9) {
|
||||||
|
const lockedPool = speciesPool.filter(s => !this.scene.gameData.dexData[s].caughtAttr);
|
||||||
|
if (lockedPool.length) { // Skip this if everything is unlocked
|
||||||
|
speciesPool = lockedPool;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pokemon that are cheaper in their tier get a weight boost. Regionals get a weight penalty
|
* Pokemon that are cheaper in their tier get a weight boost. Regionals get a weight penalty
|
||||||
* 1 cost mons get 2x
|
* 1 cost mons get 2x
|
||||||
@ -536,6 +544,12 @@ export class EggHatchPhase extends Phase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!!this.scene.gameData.dexData[species].caughtAttr) {
|
||||||
|
this.scene.gameData.unlockPity[this.egg.tier] = Math.min(this.scene.gameData.unlockPity[this.egg.tier] + 1, 10);
|
||||||
|
} else {
|
||||||
|
this.scene.gameData.unlockPity[this.egg.tier] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
const pokemonSpecies = getPokemonSpecies(species);
|
const pokemonSpecies = getPokemonSpecies(species);
|
||||||
|
|
||||||
ret = this.scene.addPlayerPokemon(pokemonSpecies, 1, undefined, undefined, undefined, false);
|
ret = this.scene.addPlayerPokemon(pokemonSpecies, 1, undefined, undefined, undefined, false);
|
||||||
|
@ -10,8 +10,10 @@ export enum ArenaEventType {
|
|||||||
/** Triggers when a {@linkcode TerrainType} is added, overlapped, or removed */
|
/** Triggers when a {@linkcode TerrainType} is added, overlapped, or removed */
|
||||||
TERRAIN_CHANGED = "onTerrainChanged",
|
TERRAIN_CHANGED = "onTerrainChanged",
|
||||||
|
|
||||||
/** Triggers when a {@linkcode ArenaTagType} is added or removed */
|
/** Triggers when a {@linkcode ArenaTagType} is added */
|
||||||
TAG_CHANGED = "onTagChanged",
|
TAG_ADDED = "onTagAdded",
|
||||||
|
/** Triggers when a {@linkcode ArenaTagType} is removed */
|
||||||
|
TAG_REMOVED = "onTagRemoved",
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -59,17 +61,34 @@ export class TerrainChangedEvent extends ArenaEvent {
|
|||||||
this.newTerrainType = newTerrainType;
|
this.newTerrainType = newTerrainType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Container class for {@linkcode ArenaEventType.TAG_CHANGED} events
|
* Container class for {@linkcode ArenaEventType.TAG_ADDED} events
|
||||||
* @extends ArenaEvent
|
* @extends ArenaEvent
|
||||||
*/
|
*/
|
||||||
export class TagChangedEvent extends ArenaEvent {
|
export class TagAddedEvent extends ArenaEvent {
|
||||||
/** The {@linkcode ArenaTagType} being set */
|
/** The {@linkcode ArenaTagType} being added */
|
||||||
public arenaTagType: ArenaTagType;
|
public arenaTagType: ArenaTagType;
|
||||||
/** The {@linkcode ArenaTagSide} the tag is being placed on */
|
/** The {@linkcode ArenaTagSide} the tag is being placed on */
|
||||||
public arenaTagSide: ArenaTagSide;
|
public arenaTagSide: ArenaTagSide;
|
||||||
constructor(arenaTagType: ArenaTagType, arenaTagSide: ArenaTagSide, duration: number) {
|
constructor(arenaTagType: ArenaTagType, arenaTagSide: ArenaTagSide, duration: number) {
|
||||||
super(ArenaEventType.TAG_CHANGED, duration);
|
super(ArenaEventType.TAG_ADDED, duration);
|
||||||
|
|
||||||
|
this.arenaTagType = arenaTagType;
|
||||||
|
this.arenaTagSide = arenaTagSide;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Container class for {@linkcode ArenaEventType.TAG_REMOVED} events
|
||||||
|
* @extends ArenaEvent
|
||||||
|
*/
|
||||||
|
export class TagRemovedEvent extends ArenaEvent {
|
||||||
|
/** The {@linkcode ArenaTagType} being removed */
|
||||||
|
public arenaTagType: ArenaTagType;
|
||||||
|
/** The {@linkcode ArenaTagSide} the tag was being placed on */
|
||||||
|
public arenaTagSide: ArenaTagSide;
|
||||||
|
constructor(arenaTagType: ArenaTagType, arenaTagSide: ArenaTagSide, duration: number) {
|
||||||
|
super(ArenaEventType.TAG_REMOVED, duration);
|
||||||
|
|
||||||
this.arenaTagType = arenaTagType;
|
this.arenaTagType = arenaTagType;
|
||||||
this.arenaTagSide = arenaTagSide;
|
this.arenaTagSide = arenaTagSide;
|
||||||
|
@ -19,7 +19,7 @@ import { Terrain, TerrainType } from "../data/terrain";
|
|||||||
import { PostTerrainChangeAbAttr, PostWeatherChangeAbAttr, applyPostTerrainChangeAbAttrs, applyPostWeatherChangeAbAttrs } from "../data/ability";
|
import { PostTerrainChangeAbAttr, PostWeatherChangeAbAttr, applyPostTerrainChangeAbAttrs, applyPostWeatherChangeAbAttrs } from "../data/ability";
|
||||||
import Pokemon from "./pokemon";
|
import Pokemon from "./pokemon";
|
||||||
import * as Overrides from "../overrides";
|
import * as Overrides from "../overrides";
|
||||||
import { WeatherChangedEvent, TerrainChangedEvent, TagChangedEvent } from "./arena-events";
|
import { WeatherChangedEvent, TerrainChangedEvent, TagAddedEvent, TagRemovedEvent } from "./arena-events";
|
||||||
|
|
||||||
export class Arena {
|
export class Arena {
|
||||||
public scene: BattleScene;
|
public scene: BattleScene;
|
||||||
@ -550,7 +550,7 @@ export class Arena {
|
|||||||
this.tags.push(newTag);
|
this.tags.push(newTag);
|
||||||
newTag.onAdd(this);
|
newTag.onAdd(this);
|
||||||
|
|
||||||
this.eventTarget.dispatchEvent(new TagChangedEvent(newTag.tagType, newTag.side, newTag.turnCount));
|
this.eventTarget.dispatchEvent(new TagAddedEvent(newTag.tagType, newTag.side, newTag.turnCount));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -577,6 +577,8 @@ export class Arena {
|
|||||||
this.tags.filter(t => !(t.lapse(this))).forEach(t => {
|
this.tags.filter(t => !(t.lapse(this))).forEach(t => {
|
||||||
t.onRemove(this);
|
t.onRemove(this);
|
||||||
this.tags.splice(this.tags.indexOf(t), 1);
|
this.tags.splice(this.tags.indexOf(t), 1);
|
||||||
|
|
||||||
|
this.eventTarget.dispatchEvent(new TagRemovedEvent(t.tagType, t.side, t.turnCount));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -586,6 +588,8 @@ export class Arena {
|
|||||||
if (tag) {
|
if (tag) {
|
||||||
tag.onRemove(this);
|
tag.onRemove(this);
|
||||||
tags.splice(tags.indexOf(tag), 1);
|
tags.splice(tags.indexOf(tag), 1);
|
||||||
|
|
||||||
|
this.eventTarget.dispatchEvent(new TagRemovedEvent(tag.tagType, tag.side, tag.turnCount));
|
||||||
}
|
}
|
||||||
return !!tag;
|
return !!tag;
|
||||||
}
|
}
|
||||||
@ -595,6 +599,8 @@ export class Arena {
|
|||||||
if (tag) {
|
if (tag) {
|
||||||
tag.onRemove(this);
|
tag.onRemove(this);
|
||||||
this.tags.splice(this.tags.indexOf(tag), 1);
|
this.tags.splice(this.tags.indexOf(tag), 1);
|
||||||
|
|
||||||
|
this.eventTarget.dispatchEvent(new TagRemovedEvent(tag.tagType, tag.side, tag.turnCount));
|
||||||
}
|
}
|
||||||
return !!tag;
|
return !!tag;
|
||||||
}
|
}
|
||||||
@ -603,6 +609,8 @@ export class Arena {
|
|||||||
removeAllTags(): void {
|
removeAllTags(): void {
|
||||||
while (this.tags.length) {
|
while (this.tags.length) {
|
||||||
this.tags[0].onRemove(this);
|
this.tags[0].onRemove(this);
|
||||||
|
this.eventTarget.dispatchEvent(new TagRemovedEvent(this.tags[0].tagType, this.tags[0].side, this.tags[0].turnCount));
|
||||||
|
|
||||||
this.tags.splice(0, 1);
|
this.tags.splice(0, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,8 +19,8 @@ import {
|
|||||||
getIconForLatestInput, swap,
|
getIconForLatestInput, swap,
|
||||||
} from "#app/configs/inputs/configHandler";
|
} from "#app/configs/inputs/configHandler";
|
||||||
import BattleScene from "./battle-scene";
|
import BattleScene from "./battle-scene";
|
||||||
import {SettingGamepad} from "#app/system/settings-gamepad";
|
import {SettingGamepad} from "#app/system/settings/settings-gamepad.js";
|
||||||
import {SettingKeyboard} from "#app/system/settings-keyboard";
|
import {SettingKeyboard} from "#app/system/settings/settings-keyboard";
|
||||||
|
|
||||||
export interface DeviceMapping {
|
export interface DeviceMapping {
|
||||||
[key: string]: number;
|
[key: string]: number;
|
||||||
|
@ -95,6 +95,11 @@ export class LoadingScene extends SceneBase {
|
|||||||
this.loadImage("type_tera", "ui");
|
this.loadImage("type_tera", "ui");
|
||||||
this.loadAtlas("type_bgs", "ui");
|
this.loadAtlas("type_bgs", "ui");
|
||||||
|
|
||||||
|
this.loadImage("dawn_icon", "ui");
|
||||||
|
this.loadImage("day_icon", "ui");
|
||||||
|
this.loadImage("dusk_icon", "ui");
|
||||||
|
this.loadImage("night_icon", "ui");
|
||||||
|
|
||||||
this.loadImage("pb_tray_overlay_player", "ui");
|
this.loadImage("pb_tray_overlay_player", "ui");
|
||||||
this.loadImage("pb_tray_overlay_enemy", "ui");
|
this.loadImage("pb_tray_overlay_enemy", "ui");
|
||||||
this.loadAtlas("pb_tray_ball", "ui");
|
this.loadAtlas("pb_tray_ball", "ui");
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { SimpleTranslationEntries } from "#app/plugins/i18n";
|
import { SimpleTranslationEntries } from "#app/plugins/i18n";
|
||||||
|
|
||||||
export const abilityTriggers: SimpleTranslationEntries = {
|
export const abilityTriggers: SimpleTranslationEntries = {
|
||||||
"blockRecoilDamage" : "{{pokemonName}}(는)은 {{abilityName}} 때문에\n반동 데미지를 받지 않는다!",
|
"blockRecoilDamage" : "{{pokemonName}}[[는]] {{abilityName}} 때문에\n반동 데미지를 받지 않는다!",
|
||||||
"badDreams": "{{pokemonName}}(는)은\n나이트메어 때문에 시달리고 있다!",
|
"badDreams": "{{pokemonName}}[[는]]\n나이트메어 때문에 시달리고 있다!",
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
import { SimpleTranslationEntries } from "#app/plugins/i18n";
|
import { SimpleTranslationEntries } from "#app/plugins/i18n";
|
||||||
|
|
||||||
export const battle: SimpleTranslationEntries = {
|
export const battle: SimpleTranslationEntries = {
|
||||||
"bossAppeared": "{{bossName}}(이)가 나타났다.",
|
"bossAppeared": "{{bossName}}[[가]] 나타났다.",
|
||||||
"trainerAppeared": "{{trainerName}}(이)가\n승부를 걸어왔다!",
|
"trainerAppeared": "{{trainerName}}[[가]]\n승부를 걸어왔다!",
|
||||||
"trainerAppearedDouble": "{{trainerName}}(이)가\n승부를 걸어왔다!",
|
"trainerAppearedDouble": "{{trainerName}}[[가]]\n승부를 걸어왔다!",
|
||||||
"trainerSendOut": "{{trainerName}}(는)은\n{{pokemonName}}(를)을 내보냈다!",
|
"trainerSendOut": "{{trainerName}}[[는]]\n{{pokemonName}}[[를]] 내보냈다!",
|
||||||
"singleWildAppeared": "앗! 야생 {{pokemonName}}(이)가\n튀어나왔다!",
|
"singleWildAppeared": "앗! 야생 {{pokemonName}}[[가]]\n튀어나왔다!",
|
||||||
"multiWildAppeared": "야생 {{pokemonName1}}(과)와\n{{pokemonName2}}(이)가 튀어나왔다!",
|
"multiWildAppeared": "야생 {{pokemonName1}}[[와]]\n{{pokemonName2}}[[가]] 튀어나왔다!",
|
||||||
"playerComeBack": "돌아와, {{pokemonName}}!",
|
"playerComeBack": "돌아와, {{pokemonName}}!",
|
||||||
"trainerComeBack": "{{trainerName}}(는)은 {{pokemonName}}를(을) 넣어버렸다!",
|
"trainerComeBack": "{{trainerName}}[[는]] {{pokemonName}}[[를]] 넣어버렸다!",
|
||||||
"playerGo": "가랏! {{pokemonName}}!",
|
"playerGo": "가랏! {{pokemonName}}!",
|
||||||
"trainerGo": "{{trainerName}}(는)은 {{pokemonName}}를(을) 내보냈다!",
|
"trainerGo": "{{trainerName}}[[는]] {{pokemonName}}[[를]] 내보냈다!",
|
||||||
"switchQuestion": "{{pokemonName}}를(을)\n교체하시겠습니까?",
|
"switchQuestion": "{{pokemonName}}[[를]]\n교체하시겠습니까?",
|
||||||
"trainerDefeated": "{{trainerName}}과(와)의\n승부에서 이겼다!",
|
"trainerDefeated": "{{trainerName}}[[와]]의\n승부에서 이겼다!",
|
||||||
"moneyWon": "상금으로\n₽{{moneyAmount}}을 손에 넣었다!",
|
"moneyWon": "상금으로\n₽{{moneyAmount}}을 손에 넣었다!",
|
||||||
"pokemonCaught": "신난다-!\n{{pokemonName}}(를)을 잡았다!",
|
"pokemonCaught": "신난다-!\n{{pokemonName}}[[를]] 잡았다!",
|
||||||
"partyFull": "지닌 포켓몬이 가득 찼습니다. {{pokemonName}}를(을)\n대신해 포켓몬을 놓아주시겠습니까?",
|
"partyFull": "지닌 포켓몬이 가득 찼습니다. {{pokemonName}}[[를]]\n대신해 포켓몬을 놓아주시겠습니까?",
|
||||||
"pokemon": "포켓몬",
|
"pokemon": "포켓몬",
|
||||||
"sendOutPokemon": "가랏! {{pokemonName}}!",
|
"sendOutPokemon": "가랏! {{pokemonName}}!",
|
||||||
"hitResultCriticalHit": "급소에 맞았다!",
|
"hitResultCriticalHit": "급소에 맞았다!",
|
||||||
@ -25,22 +25,22 @@ export const battle: SimpleTranslationEntries = {
|
|||||||
"hitResultOneHitKO": "일격필살!",
|
"hitResultOneHitKO": "일격필살!",
|
||||||
"attackFailed": "하지만 실패했다!",
|
"attackFailed": "하지만 실패했다!",
|
||||||
"attackHitsCount": "{{count}}번 맞았다!",
|
"attackHitsCount": "{{count}}번 맞았다!",
|
||||||
"expGain": "{{pokemonName}}(는)은\n{{exp}} 경험치를 얻었다!",
|
"expGain": "{{pokemonName}}[[는]]\n{{exp}} 경험치를 얻었다!",
|
||||||
"levelUp": "{{pokemonName}}(는)은\n레벨 {{level}}(으)로 올랐다!",
|
"levelUp": "{{pokemonName}}[[는]]\n레벨 {{level}}[[로]] 올랐다!",
|
||||||
"learnMove": "{{pokemonName}}(는)은 새로\n{{moveName}}를(을) 배웠다!",
|
"learnMove": "{{pokemonName}}[[는]] 새로\n{{moveName}}[[를]] 배웠다!",
|
||||||
"learnMovePrompt": "{{pokemonName}}(는)은 새로\n{{moveName}}를(을) 배우고 싶다!…",
|
"learnMovePrompt": "{{pokemonName}}[[는]] 새로\n{{moveName}}[[를]] 배우고 싶다!…",
|
||||||
"learnMoveLimitReached": "그러나 {{pokemonName}}(는)은 기술을 4개\n알고 있으므로 더 이상 배울 수 없다!",
|
"learnMoveLimitReached": "그러나 {{pokemonName}}[[는]] 기술을 4개\n알고 있으므로 더 이상 배울 수 없다!",
|
||||||
"learnMoveReplaceQuestion": "{{moveName}} 대신 다른 기술을 잊게 하겠습니까?",
|
"learnMoveReplaceQuestion": "{{moveName}} 대신 다른 기술을 잊게 하겠습니까?",
|
||||||
"learnMoveStopTeaching": "그럼… {{moveName}}를(을)\n배우는 것을 포기하겠습니까?",
|
"learnMoveStopTeaching": "그럼… {{moveName}}[[를]]\n배우는 것을 포기하겠습니까?",
|
||||||
"learnMoveNotLearned": "{{pokemonName}}(는)은 {{moveName}}를(을)\n결국 배우지 않았다!",
|
"learnMoveNotLearned": "{{pokemonName}}[[는]] {{moveName}}[[를]]\n결국 배우지 않았다!",
|
||||||
"learnMoveForgetQuestion": "어느 기술을 잊게 하고싶은가?",
|
"learnMoveForgetQuestion": "어느 기술을 잊게 하고싶은가?",
|
||||||
"learnMoveForgetSuccess": "{{pokemonName}}(는)은 {{moveName}}를(을) 깨끗이 잊었다!",
|
"learnMoveForgetSuccess": "{{pokemonName}}[[는]] {{moveName}}[[를]] 깨끗이 잊었다!",
|
||||||
"countdownPoof": "@d{32}1, @d{15}2, @d{15}… @d{15}… @d{30}@s{pb_bounce_1}짠!",
|
"countdownPoof": "@d{32}1, @d{15}2, @d{15}… @d{15}… @d{30}@s{pb_bounce_1}짠!",
|
||||||
"learnMoveAnd": "그리고…",
|
"learnMoveAnd": "그리고…",
|
||||||
"levelCapUp": "레벨의 최대치가\n{{levelCap}}까지 상승했다!",
|
"levelCapUp": "레벨의 최대치가\n{{levelCap}}까지 상승했다!",
|
||||||
"moveNotImplemented": "{{moveName}}(는)은 아직 구현되지 않아 사용할 수 없다…",
|
"moveNotImplemented": "{{moveName}}[[는]] 아직 구현되지 않아 사용할 수 없다…",
|
||||||
"moveNoPP": "기술의 남은 포인트가 없다!",
|
"moveNoPP": "기술의 남은 포인트가 없다!",
|
||||||
"moveDisabled": "{{moveName}}를(을) 쓸 수 없다!",
|
"moveDisabled": "{{moveName}}[[를]] 쓸 수 없다!",
|
||||||
"noPokeballForce": "본 적 없는 힘이\n볼을 사용하지 못하게 한다.",
|
"noPokeballForce": "본 적 없는 힘이\n볼을 사용하지 못하게 한다.",
|
||||||
"noPokeballTrainer": "다른 트레이너의 포켓몬은 잡을 수 없다!",
|
"noPokeballTrainer": "다른 트레이너의 포켓몬은 잡을 수 없다!",
|
||||||
"noPokeballMulti": "안돼! 2마리 있어서\n목표를 정할 수가 없어…!",
|
"noPokeballMulti": "안돼! 2마리 있어서\n목표를 정할 수가 없어…!",
|
||||||
|
@ -5,5 +5,5 @@ export const commandUiHandler: SimpleTranslationEntries = {
|
|||||||
"ball": "볼",
|
"ball": "볼",
|
||||||
"pokemon": "포켓몬",
|
"pokemon": "포켓몬",
|
||||||
"run": "도망간다",
|
"run": "도망간다",
|
||||||
"actionMessage": "{{pokemonName}}(는)은 무엇을 할까?",
|
"actionMessage": "{{pokemonName}}[[는]] 무엇을 할까?",
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -38,9 +38,9 @@ export const menu: SimpleTranslationEntries = {
|
|||||||
"girl": "여자",
|
"girl": "여자",
|
||||||
"evolving": "…오잉!?\n{{pokemonName}}의 모습이…!",
|
"evolving": "…오잉!?\n{{pokemonName}}의 모습이…!",
|
||||||
"stoppedEvolving": "얼라리…?\n{{pokemonName}}의 변화가 멈췄다!",
|
"stoppedEvolving": "얼라리…?\n{{pokemonName}}의 변화가 멈췄다!",
|
||||||
"pauseEvolutionsQuestion": "{{pokemonName}}를(을) 진화하지 않게 만드시겠습니까?\n포켓몬 화면에서 다시 활성화시킬 수 있습니다.",
|
"pauseEvolutionsQuestion": "{{pokemonName}}[[를]] 진화하지 않게 만드시겠습니까?\n포켓몬 화면에서 다시 활성화시킬 수 있습니다.",
|
||||||
"evolutionsPaused": "{{pokemonName}}의 진화가 비활성화되었다.",
|
"evolutionsPaused": "{{pokemonName}}의 진화가 비활성화되었다.",
|
||||||
"evolutionDone": "축하합니다! {{pokemonName}}(는)은\n{{evolvedPokemonName}}(으)로 진화했습니다!",
|
"evolutionDone": "축하합니다! {{pokemonName}}[[는]]\n{{evolvedPokemonName}}[[로]] 진화했습니다!",
|
||||||
"dailyRankings": "일간 랭킹",
|
"dailyRankings": "일간 랭킹",
|
||||||
"weeklyRankings": "주간 랭킹",
|
"weeklyRankings": "주간 랭킹",
|
||||||
"noRankings": "랭킹 정보 없음",
|
"noRankings": "랭킹 정보 없음",
|
||||||
|
@ -12,8 +12,8 @@ export const modifierType: ModifierTypeTranslationEntries = {
|
|||||||
},
|
},
|
||||||
"PokemonHeldItemModifierType": {
|
"PokemonHeldItemModifierType": {
|
||||||
extra: {
|
extra: {
|
||||||
"inoperable": "{{pokemonName}}(는)은\n이 아이템을 얻을 수 없다!",
|
"inoperable": "{{pokemonName}}[[는]]\n이 아이템을 얻을 수 없다!",
|
||||||
"tooMany": "{{pokemonName}}(는)은\n이 아이템을 너무 많이 갖고 있다!",
|
"tooMany": "{{pokemonName}}[[는]]\n이 아이템을 너무 많이 갖고 있다!",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"PokemonHpRestoreModifierType": {
|
"PokemonHpRestoreModifierType": {
|
||||||
@ -46,13 +46,13 @@ export const modifierType: ModifierTypeTranslationEntries = {
|
|||||||
},
|
},
|
||||||
"PokemonNatureChangeModifierType": {
|
"PokemonNatureChangeModifierType": {
|
||||||
name: "{{natureName}}민트",
|
name: "{{natureName}}민트",
|
||||||
description: "포켓몬의 성격을 {{natureName}}(으)로 바꾸고 스타팅에도 등록한다.",
|
description: "포켓몬의 성격을 {{natureName}}[[로]] 바꾸고 스타팅에도 등록한다.",
|
||||||
},
|
},
|
||||||
"DoubleBattleChanceBoosterModifierType": {
|
"DoubleBattleChanceBoosterModifierType": {
|
||||||
description: "{{battleCount}}번의 배틀 동안 더블 배틀이 등장할 확률 두 배",
|
description: "{{battleCount}}번의 배틀 동안 더블 배틀이 등장할 확률 두 배",
|
||||||
},
|
},
|
||||||
"TempBattleStatBoosterModifierType": {
|
"TempBattleStatBoosterModifierType": {
|
||||||
description: "자신의 모든 포켓몬이 5번의 배틀 동안 {{tempBattleStatName}}(이)가 한 단계 증가"
|
description: "자신의 모든 포켓몬이 5번의 배틀 동안 {{tempBattleStatName}}[[가]] 한 단계 증가"
|
||||||
},
|
},
|
||||||
"AttackTypeBoosterModifierType": {
|
"AttackTypeBoosterModifierType": {
|
||||||
description: "지니게 하면 {{moveType}}타입 기술의 위력이 20% 상승",
|
description: "지니게 하면 {{moveType}}타입 기술의 위력이 20% 상승",
|
||||||
@ -97,7 +97,7 @@ export const modifierType: ModifierTypeTranslationEntries = {
|
|||||||
},
|
},
|
||||||
"TmModifierType": {
|
"TmModifierType": {
|
||||||
name: "No.{{moveId}} {{moveName}}",
|
name: "No.{{moveId}} {{moveName}}",
|
||||||
description: "포켓몬에게 {{moveName}}를(을) 가르침",
|
description: "포켓몬에게 {{moveName}}[[를]] 가르침",
|
||||||
},
|
},
|
||||||
"EvolutionItemModifierType": {
|
"EvolutionItemModifierType": {
|
||||||
description: "어느 특정 포켓몬을 진화",
|
description: "어느 특정 포켓몬을 진화",
|
||||||
|
@ -15,12 +15,12 @@ export const weather: SimpleTranslationEntries = {
|
|||||||
"sandstormStartMessage": "모래바람이 불기 시작했다!",
|
"sandstormStartMessage": "모래바람이 불기 시작했다!",
|
||||||
"sandstormLapseMessage": "모래바람이 세차게 분다",
|
"sandstormLapseMessage": "모래바람이 세차게 분다",
|
||||||
"sandstormClearMessage": "모래바람이 가라앉았다!",
|
"sandstormClearMessage": "모래바람이 가라앉았다!",
|
||||||
"sandstormDamageMessage": "모래바람이\n{{pokemonPrefix}}{{pokemonName}}를(을) 덮쳤다!",
|
"sandstormDamageMessage": "모래바람이\n{{pokemonPrefix}}{{pokemonName}}[[를]] 덮쳤다!",
|
||||||
|
|
||||||
"hailStartMessage": "싸라기눈이 내리기 시작했다!",
|
"hailStartMessage": "싸라기눈이 내리기 시작했다!",
|
||||||
"hailLapseMessage": "싸라기눈이 계속 내리고 있다",
|
"hailLapseMessage": "싸라기눈이 계속 내리고 있다",
|
||||||
"hailClearMessage": "싸라기눈이 그쳤다!",
|
"hailClearMessage": "싸라기눈이 그쳤다!",
|
||||||
"hailDamageMessage": "싸라기눈이\n{{pokemonPrefix}}{{pokemonName}}를(을) 덮쳤다!",
|
"hailDamageMessage": "싸라기눈이\n{{pokemonPrefix}}{{pokemonName}}[[를]] 덮쳤다!",
|
||||||
|
|
||||||
"snowStartMessage": "눈이 내리기 시작했다!",
|
"snowStartMessage": "눈이 내리기 시작했다!",
|
||||||
"snowLapseMessage": "눈이 계속 내리고 있다",
|
"snowLapseMessage": "눈이 계속 내리고 있다",
|
||||||
|
@ -48,7 +48,7 @@ import { addPokeballCaptureStars, addPokeballOpenParticles } from "./field/anims
|
|||||||
import { SpeciesFormChangeActiveTrigger, SpeciesFormChangeManualTrigger, SpeciesFormChangeMoveLearnedTrigger, SpeciesFormChangePostMoveTrigger, SpeciesFormChangePreMoveTrigger } from "./data/pokemon-forms";
|
import { SpeciesFormChangeActiveTrigger, SpeciesFormChangeManualTrigger, SpeciesFormChangeMoveLearnedTrigger, SpeciesFormChangePostMoveTrigger, SpeciesFormChangePreMoveTrigger } from "./data/pokemon-forms";
|
||||||
import { battleSpecDialogue, getCharVariantFromDialogue, miscDialogue } from "./data/dialogue";
|
import { battleSpecDialogue, getCharVariantFromDialogue, miscDialogue } from "./data/dialogue";
|
||||||
import ModifierSelectUiHandler, { SHOP_OPTIONS_ROW_LIMIT } from "./ui/modifier-select-ui-handler";
|
import ModifierSelectUiHandler, { SHOP_OPTIONS_ROW_LIMIT } from "./ui/modifier-select-ui-handler";
|
||||||
import { Setting } from "./system/settings";
|
import { SettingKeys } from "./system/settings/settings";
|
||||||
import { Tutorial, handleTutorial } from "./tutorial";
|
import { Tutorial, handleTutorial } from "./tutorial";
|
||||||
import { TerrainType } from "./data/terrain";
|
import { TerrainType } from "./data/terrain";
|
||||||
import { OptionSelectConfig, OptionSelectItem } from "./ui/abstact-option-select-ui-handler";
|
import { OptionSelectConfig, OptionSelectItem } from "./ui/abstact-option-select-ui-handler";
|
||||||
@ -477,7 +477,7 @@ export class SelectGenderPhase extends Phase {
|
|||||||
label: i18next.t("menu:boy"),
|
label: i18next.t("menu:boy"),
|
||||||
handler: () => {
|
handler: () => {
|
||||||
this.scene.gameData.gender = PlayerGender.MALE;
|
this.scene.gameData.gender = PlayerGender.MALE;
|
||||||
this.scene.gameData.saveSetting(Setting.Player_Gender, 0);
|
this.scene.gameData.saveSetting(SettingKeys.Player_Gender, 0);
|
||||||
this.scene.gameData.saveSystem().then(() => this.end());
|
this.scene.gameData.saveSystem().then(() => this.end());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -486,7 +486,7 @@ export class SelectGenderPhase extends Phase {
|
|||||||
label: i18next.t("menu:girl"),
|
label: i18next.t("menu:girl"),
|
||||||
handler: () => {
|
handler: () => {
|
||||||
this.scene.gameData.gender = PlayerGender.FEMALE;
|
this.scene.gameData.gender = PlayerGender.FEMALE;
|
||||||
this.scene.gameData.saveSetting(Setting.Player_Gender, 1);
|
this.scene.gameData.saveSetting(SettingKeys.Player_Gender, 1);
|
||||||
this.scene.gameData.saveSystem().then(() => this.end());
|
this.scene.gameData.saveSystem().then(() => this.end());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import LanguageDetector from "i18next-browser-languagedetector";
|
import LanguageDetector from "i18next-browser-languagedetector";
|
||||||
|
import processor, { KoreanPostpositionProcessor } from "i18next-korean-postposition-processor";
|
||||||
|
|
||||||
import { deConfig } from "#app/locales/de/config.js";
|
import { deConfig } from "#app/locales/de/config.js";
|
||||||
import { enConfig } from "#app/locales/en/config.js";
|
import { enConfig } from "#app/locales/en/config.js";
|
||||||
@ -148,7 +149,7 @@ export function initI18n(): void {
|
|||||||
* A: In src/system/settings.ts, add a new case to the Setting.Language switch statement.
|
* A: In src/system/settings.ts, add a new case to the Setting.Language switch statement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
i18next.use(LanguageDetector).init({
|
i18next.use(LanguageDetector).use(processor).use(new KoreanPostpositionProcessor()).init({
|
||||||
lng: lang,
|
lng: lang,
|
||||||
nonExplicitSupportedLngs: true,
|
nonExplicitSupportedLngs: true,
|
||||||
fallbackLng: "en",
|
fallbackLng: "en",
|
||||||
@ -186,6 +187,7 @@ export function initI18n(): void {
|
|||||||
...koConfig
|
...koConfig
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
postProcess: ["korean-postposition"],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import { GameModes, gameModes } from "../game-mode";
|
|||||||
import { BattleType } from "../battle";
|
import { BattleType } from "../battle";
|
||||||
import TrainerData from "./trainer-data";
|
import TrainerData from "./trainer-data";
|
||||||
import { trainerConfigs } from "../data/trainer-config";
|
import { trainerConfigs } from "../data/trainer-config";
|
||||||
import { Setting, setSetting, settingDefaults } from "./settings";
|
import { SettingKeys, resetSettings, setSetting } from "./settings/settings";
|
||||||
import { achvs } from "./achv";
|
import { achvs } from "./achv";
|
||||||
import EggData from "./egg-data";
|
import EggData from "./egg-data";
|
||||||
import { Egg } from "../data/egg";
|
import { Egg } from "../data/egg";
|
||||||
@ -30,9 +30,10 @@ import { allMoves } from "../data/move";
|
|||||||
import { TrainerVariant } from "../field/trainer";
|
import { TrainerVariant } from "../field/trainer";
|
||||||
import { OutdatedPhase, ReloadSessionPhase } from "#app/phases";
|
import { OutdatedPhase, ReloadSessionPhase } from "#app/phases";
|
||||||
import { Variant, variantData } from "#app/data/variant";
|
import { Variant, variantData } from "#app/data/variant";
|
||||||
import {setSettingGamepad, SettingGamepad, settingGamepadDefaults} from "./settings-gamepad";
|
import {setSettingGamepad, SettingGamepad, settingGamepadDefaults} from "./settings/settings-gamepad";
|
||||||
import {setSettingKeyboard, SettingKeyboard, settingKeyboardDefaults} from "#app/system/settings-keyboard";
|
import {setSettingKeyboard, SettingKeyboard} from "#app/system/settings/settings-keyboard";
|
||||||
import { TerrainChangedEvent, WeatherChangedEvent } from "#app/field/arena-events.js";
|
import { TerrainChangedEvent, WeatherChangedEvent } from "#app/field/arena-events.js";
|
||||||
|
import { Device } from "#app/enums/devices.js";
|
||||||
|
|
||||||
const saveKey = "x0i2O7WRiANTqPmZ"; // Temporary; secure encryption is not yet necessary
|
const saveKey = "x0i2O7WRiANTqPmZ"; // Temporary; secure encryption is not yet necessary
|
||||||
|
|
||||||
@ -100,6 +101,8 @@ interface SystemSaveData {
|
|||||||
eggs: EggData[];
|
eggs: EggData[];
|
||||||
gameVersion: string;
|
gameVersion: string;
|
||||||
timestamp: integer;
|
timestamp: integer;
|
||||||
|
eggPity: integer[];
|
||||||
|
unlockPity: integer[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SessionSaveData {
|
export interface SessionSaveData {
|
||||||
@ -248,6 +251,8 @@ export class GameData {
|
|||||||
public voucherUnlocks: VoucherUnlocks;
|
public voucherUnlocks: VoucherUnlocks;
|
||||||
public voucherCounts: VoucherCounts;
|
public voucherCounts: VoucherCounts;
|
||||||
public eggs: Egg[];
|
public eggs: Egg[];
|
||||||
|
public eggPity: integer[];
|
||||||
|
public unlockPity: integer[];
|
||||||
|
|
||||||
constructor(scene: BattleScene) {
|
constructor(scene: BattleScene) {
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
@ -272,6 +277,8 @@ export class GameData {
|
|||||||
[VoucherType.GOLDEN]: 0
|
[VoucherType.GOLDEN]: 0
|
||||||
};
|
};
|
||||||
this.eggs = [];
|
this.eggs = [];
|
||||||
|
this.eggPity = [0, 0, 0, 0];
|
||||||
|
this.unlockPity = [0, 0, 0, 0];
|
||||||
this.initDexData();
|
this.initDexData();
|
||||||
this.initStarterData();
|
this.initStarterData();
|
||||||
}
|
}
|
||||||
@ -290,7 +297,9 @@ export class GameData {
|
|||||||
voucherCounts: this.voucherCounts,
|
voucherCounts: this.voucherCounts,
|
||||||
eggs: this.eggs.map(e => new EggData(e)),
|
eggs: this.eggs.map(e => new EggData(e)),
|
||||||
gameVersion: this.scene.game.config.gameVersion,
|
gameVersion: this.scene.game.config.gameVersion,
|
||||||
timestamp: new Date().getTime()
|
timestamp: new Date().getTime(),
|
||||||
|
eggPity: this.eggPity.slice(0),
|
||||||
|
unlockPity: this.unlockPity.slice(0)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -394,7 +403,7 @@ export class GameData {
|
|||||||
|
|
||||||
this.gender = systemData.gender;
|
this.gender = systemData.gender;
|
||||||
|
|
||||||
this.saveSetting(Setting.Player_Gender, systemData.gender === PlayerGender.FEMALE ? 1 : 0);
|
this.saveSetting(SettingKeys.Player_Gender, systemData.gender === PlayerGender.FEMALE ? 1 : 0);
|
||||||
|
|
||||||
const initStarterData = !systemData.starterData;
|
const initStarterData = !systemData.starterData;
|
||||||
|
|
||||||
@ -473,6 +482,9 @@ export class GameData {
|
|||||||
? systemData.eggs.map(e => e.toEgg())
|
? systemData.eggs.map(e => e.toEgg())
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
|
this.eggPity = systemData.eggPity ? systemData.eggPity.slice(0) : [0, 0, 0, 0];
|
||||||
|
this.unlockPity = systemData.unlockPity ? systemData.unlockPity.slice(0) : [0, 0, 0, 0];
|
||||||
|
|
||||||
this.dexData = Object.assign(this.dexData, systemData.dexData);
|
this.dexData = Object.assign(this.dexData, systemData.dexData);
|
||||||
this.consolidateDexData(this.dexData);
|
this.consolidateDexData(this.dexData);
|
||||||
this.defaultDexData = null;
|
this.defaultDexData = null;
|
||||||
@ -557,19 +569,21 @@ export class GameData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public saveSetting(setting: Setting, valueIndex: integer): boolean {
|
/**
|
||||||
|
* Saves a setting to localStorage
|
||||||
|
* @param setting string ideally of SettingKeys
|
||||||
|
* @param valueIndex index of the setting's option
|
||||||
|
* @returns true
|
||||||
|
*/
|
||||||
|
public saveSetting(setting: string, valueIndex: integer): boolean {
|
||||||
let settings: object = {};
|
let settings: object = {};
|
||||||
if (localStorage.hasOwnProperty("settings")) {
|
if (localStorage.hasOwnProperty("settings")) {
|
||||||
settings = JSON.parse(localStorage.getItem("settings"));
|
settings = JSON.parse(localStorage.getItem("settings"));
|
||||||
}
|
}
|
||||||
|
|
||||||
setSetting(this.scene, setting as Setting, valueIndex);
|
setSetting(this.scene, setting, valueIndex);
|
||||||
|
|
||||||
Object.keys(settingDefaults).forEach(s => {
|
settings[setting] = valueIndex;
|
||||||
if (s === setting) {
|
|
||||||
settings[s] = valueIndex;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
localStorage.setItem("settings", JSON.stringify(settings));
|
localStorage.setItem("settings", JSON.stringify(settings));
|
||||||
|
|
||||||
@ -642,61 +656,36 @@ export class GameData {
|
|||||||
* to update the specified setting with the new value. Finally, it saves the updated settings back
|
* to update the specified setting with the new value. Finally, it saves the updated settings back
|
||||||
* to localStorage and returns `true` to indicate success.
|
* to localStorage and returns `true` to indicate success.
|
||||||
*/
|
*/
|
||||||
public saveGamepadSetting(setting: SettingGamepad, valueIndex: integer): boolean {
|
public saveControlSetting(device: Device, localStoragePropertyName: string, setting: SettingGamepad|SettingKeyboard, settingDefaults, valueIndex: integer): boolean {
|
||||||
let settingsGamepad: object = {}; // Initialize an empty object to hold the gamepad settings
|
let settingsControls: object = {}; // Initialize an empty object to hold the gamepad settings
|
||||||
|
|
||||||
if (localStorage.hasOwnProperty("settingsGamepad")) { // Check if 'settingsGamepad' exists in localStorage
|
if (localStorage.hasOwnProperty(localStoragePropertyName)) { // Check if 'settingsControls' exists in localStorage
|
||||||
settingsGamepad = JSON.parse(localStorage.getItem("settingsGamepad")); // Parse the existing 'settingsGamepad' from localStorage
|
settingsControls = JSON.parse(localStorage.getItem(localStoragePropertyName)); // Parse the existing 'settingsControls' from localStorage
|
||||||
}
|
}
|
||||||
|
|
||||||
setSettingGamepad(this.scene, setting as SettingGamepad, valueIndex); // Set the gamepad setting in the current scene
|
if (device === Device.GAMEPAD) {
|
||||||
|
setSettingGamepad(this.scene, setting as SettingGamepad, valueIndex); // Set the gamepad setting in the current scene
|
||||||
|
} else if (device === Device.KEYBOARD) {
|
||||||
|
setSettingKeyboard(this.scene, setting as SettingKeyboard, valueIndex); // Set the keyboard setting in the current scene
|
||||||
|
}
|
||||||
|
|
||||||
Object.keys(settingGamepadDefaults).forEach(s => { // Iterate over the default gamepad settings
|
Object.keys(settingDefaults).forEach(s => { // Iterate over the default gamepad settings
|
||||||
if (s === setting) {// If the current setting matches, update its value
|
if (s === setting) {// If the current setting matches, update its value
|
||||||
settingsGamepad[s] = valueIndex;
|
settingsControls[s] = valueIndex;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
localStorage.setItem("settingsGamepad", JSON.stringify(settingsGamepad)); // Save the updated gamepad settings back to localStorage
|
localStorage.setItem(localStoragePropertyName, JSON.stringify(settingsControls)); // Save the updated gamepad settings back to localStorage
|
||||||
|
|
||||||
return true; // Return true to indicate the operation was successful
|
return true; // Return true to indicate the operation was successful
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves a keyboard setting to localStorage.
|
* Loads Settings from local storage if available
|
||||||
*
|
* @returns true if succesful, false if not
|
||||||
* @param setting - The keyboard setting to save.
|
|
||||||
* @param valueIndex - The index of the value to set for the keyboard setting.
|
|
||||||
* @returns `true` if the setting is successfully saved.
|
|
||||||
*
|
|
||||||
* @remarks
|
|
||||||
* This method initializes an empty object for keyboard settings if none exist in localStorage.
|
|
||||||
* It then updates the setting in the current scene and iterates over the default keyboard settings
|
|
||||||
* to update the specified setting with the new value. Finally, it saves the updated settings back
|
|
||||||
* to localStorage and returns `true` to indicate success.
|
|
||||||
*/
|
*/
|
||||||
public saveKeyboardSetting(setting: SettingKeyboard, valueIndex: integer): boolean {
|
|
||||||
let settingsKeyboard: object = {}; // Initialize an empty object to hold the keyboard settings
|
|
||||||
|
|
||||||
if (localStorage.hasOwnProperty("settingsKeyboard")) { // Check if 'settingsKeyboard' exists in localStorage
|
|
||||||
settingsKeyboard = JSON.parse(localStorage.getItem("settingsKeyboard")); // Parse the existing 'settingsKeyboard' from localStorage
|
|
||||||
}
|
|
||||||
|
|
||||||
setSettingKeyboard(this.scene, setting as SettingKeyboard, valueIndex); // Set the keyboard setting in the current scene
|
|
||||||
|
|
||||||
Object.keys(settingKeyboardDefaults).forEach(s => { // Iterate over the default keyboard settings
|
|
||||||
if (s === setting) {// If the current setting matches, update its value
|
|
||||||
settingsKeyboard[s] = valueIndex;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
localStorage.setItem("settingsKeyboard", JSON.stringify(settingsKeyboard)); // Save the updated keyboard settings back to localStorage
|
|
||||||
|
|
||||||
return true; // Return true to indicate the operation was successful
|
|
||||||
}
|
|
||||||
|
|
||||||
private loadSettings(): boolean {
|
private loadSettings(): boolean {
|
||||||
Object.values(Setting).map(setting => setting as Setting).forEach(setting => setSetting(this.scene, setting, settingDefaults[setting]));
|
resetSettings(this.scene);
|
||||||
|
|
||||||
if (!localStorage.hasOwnProperty("settings")) {
|
if (!localStorage.hasOwnProperty("settings")) {
|
||||||
return false;
|
return false;
|
||||||
@ -705,7 +694,7 @@ export class GameData {
|
|||||||
const settings = JSON.parse(localStorage.getItem("settings"));
|
const settings = JSON.parse(localStorage.getItem("settings"));
|
||||||
|
|
||||||
for (const setting of Object.keys(settings)) {
|
for (const setting of Object.keys(settings)) {
|
||||||
setSetting(this.scene, setting as Setting, settings[setting]);
|
setSetting(this.scene, setting, settings[setting]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,279 +0,0 @@
|
|||||||
import { Mode } from "#app/ui/ui";
|
|
||||||
import i18next from "i18next";
|
|
||||||
import BattleScene from "../battle-scene";
|
|
||||||
import { hasTouchscreen } from "../touch-controls";
|
|
||||||
import { updateWindowType } from "../ui/ui-theme";
|
|
||||||
import { PlayerGender } from "./game-data";
|
|
||||||
import { CandyUpgradeNotificationChangedEvent } from "#app/battle-scene-events.js";
|
|
||||||
import { MoneyFormat } from "../enums/money-format";
|
|
||||||
import SettingsUiHandler from "#app/ui/settings/settings-ui-handler";
|
|
||||||
|
|
||||||
export enum Setting {
|
|
||||||
Game_Speed = "GAME_SPEED",
|
|
||||||
Master_Volume = "MASTER_VOLUME",
|
|
||||||
BGM_Volume = "BGM_VOLUME",
|
|
||||||
SE_Volume = "SE_VOLUME",
|
|
||||||
Language = "LANGUAGE",
|
|
||||||
Damage_Numbers = "DAMAGE_NUMBERS",
|
|
||||||
UI_Theme = "UI_THEME",
|
|
||||||
Window_Type = "WINDOW_TYPE",
|
|
||||||
Tutorials = "TUTORIALS",
|
|
||||||
Enable_Retries = "ENABLE_RETRIES",
|
|
||||||
Skip_Seen_Dialogues = "SKIP_SEEN_DIALOGUES",
|
|
||||||
Candy_Upgrade_Notification = "CANDY_UPGRADE_NOTIFICATION",
|
|
||||||
Candy_Upgrade_Display = "CANDY_UPGRADE_DISPLAY",
|
|
||||||
Money_Format = "MONEY_FORMAT",
|
|
||||||
Sprite_Set = "SPRITE_SET",
|
|
||||||
Move_Animations = "MOVE_ANIMATIONS",
|
|
||||||
Show_Moveset_Flyout = "SHOW_MOVESET_FLYOUT",
|
|
||||||
Show_Stats_on_Level_Up = "SHOW_LEVEL_UP_STATS",
|
|
||||||
EXP_Gains_Speed = "EXP_GAINS_SPEED",
|
|
||||||
EXP_Party_Display = "EXP_PARTY_DISPLAY",
|
|
||||||
HP_Bar_Speed = "HP_BAR_SPEED",
|
|
||||||
Fusion_Palette_Swaps = "FUSION_PALETTE_SWAPS",
|
|
||||||
Player_Gender = "PLAYER_GENDER",
|
|
||||||
Touch_Controls = "TOUCH_CONTROLS",
|
|
||||||
Vibration = "VIBRATION"
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SettingOptions {
|
|
||||||
[key: string]: string[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SettingDefaults {
|
|
||||||
[key: string]: integer
|
|
||||||
}
|
|
||||||
|
|
||||||
export const settingOptions: SettingOptions = {
|
|
||||||
[Setting.Game_Speed]: ["1x", "1.25x", "1.5x", "2x", "2.5x", "3x", "4x", "5x"],
|
|
||||||
[Setting.Master_Volume]: new Array(11).fill(null).map((_, i) => i ? (i * 10).toString() : "Mute"),
|
|
||||||
[Setting.BGM_Volume]: new Array(11).fill(null).map((_, i) => i ? (i * 10).toString() : "Mute"),
|
|
||||||
[Setting.SE_Volume]: new Array(11).fill(null).map((_, i) => i ? (i * 10).toString() : "Mute"),
|
|
||||||
[Setting.Language]: ["English", "Change"],
|
|
||||||
[Setting.Damage_Numbers]: ["Off", "Simple", "Fancy"],
|
|
||||||
[Setting.UI_Theme]: ["Default", "Legacy"],
|
|
||||||
[Setting.Window_Type]: new Array(5).fill(null).map((_, i) => (i + 1).toString()),
|
|
||||||
[Setting.Tutorials]: ["Off", "On"],
|
|
||||||
[Setting.Enable_Retries]: ["Off", "On"],
|
|
||||||
[Setting.Skip_Seen_Dialogues]: ["Off", "On"],
|
|
||||||
[Setting.Candy_Upgrade_Notification]: ["Off", "Passives Only", "On"],
|
|
||||||
[Setting.Candy_Upgrade_Display]: ["Icon", "Animation"],
|
|
||||||
[Setting.Money_Format]: ["Normal", "Abbreviated"],
|
|
||||||
[Setting.Sprite_Set]: ["Consistent", "Mixed Animated"],
|
|
||||||
[Setting.Move_Animations]: ["Off", "On"],
|
|
||||||
[Setting.Show_Moveset_Flyout]: ["Off", "On"],
|
|
||||||
[Setting.Show_Stats_on_Level_Up]: ["Off", "On"],
|
|
||||||
[Setting.EXP_Gains_Speed]: ["Normal", "Fast", "Faster", "Skip"],
|
|
||||||
[Setting.EXP_Party_Display]: ["Normal", "Level Up Notification", "Skip"],
|
|
||||||
[Setting.HP_Bar_Speed]: ["Normal", "Fast", "Faster", "Instant"],
|
|
||||||
[Setting.Fusion_Palette_Swaps]: ["Off", "On"],
|
|
||||||
[Setting.Player_Gender]: ["Boy", "Girl"],
|
|
||||||
[Setting.Touch_Controls]: ["Auto", "Disabled"],
|
|
||||||
[Setting.Vibration]: ["Auto", "Disabled"]
|
|
||||||
};
|
|
||||||
|
|
||||||
export const settingDefaults: SettingDefaults = {
|
|
||||||
[Setting.Game_Speed]: 3,
|
|
||||||
[Setting.Master_Volume]: 5,
|
|
||||||
[Setting.BGM_Volume]: 10,
|
|
||||||
[Setting.SE_Volume]: 10,
|
|
||||||
[Setting.Language]: 0,
|
|
||||||
[Setting.Damage_Numbers]: 0,
|
|
||||||
[Setting.UI_Theme]: 0,
|
|
||||||
[Setting.Window_Type]: 0,
|
|
||||||
[Setting.Tutorials]: 1,
|
|
||||||
[Setting.Enable_Retries]: 0,
|
|
||||||
[Setting.Skip_Seen_Dialogues]: 0,
|
|
||||||
[Setting.Candy_Upgrade_Notification]: 0,
|
|
||||||
[Setting.Candy_Upgrade_Display]: 0,
|
|
||||||
[Setting.Money_Format]: 0,
|
|
||||||
[Setting.Sprite_Set]: 0,
|
|
||||||
[Setting.Move_Animations]: 1,
|
|
||||||
[Setting.Show_Moveset_Flyout]: 1,
|
|
||||||
[Setting.Show_Stats_on_Level_Up]: 1,
|
|
||||||
[Setting.EXP_Gains_Speed]: 0,
|
|
||||||
[Setting.EXP_Party_Display]: 0,
|
|
||||||
[Setting.HP_Bar_Speed]: 0,
|
|
||||||
[Setting.Fusion_Palette_Swaps]: 1,
|
|
||||||
[Setting.Player_Gender]: 0,
|
|
||||||
[Setting.Touch_Controls]: 0,
|
|
||||||
[Setting.Vibration]: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
export const reloadSettings: Setting[] = [Setting.UI_Theme, Setting.Language, Setting.Sprite_Set, Setting.Candy_Upgrade_Display];
|
|
||||||
|
|
||||||
export function setSetting(scene: BattleScene, setting: Setting, value: integer): boolean {
|
|
||||||
switch (setting) {
|
|
||||||
case Setting.Game_Speed:
|
|
||||||
scene.gameSpeed = parseFloat(settingOptions[setting][value].replace("x", ""));
|
|
||||||
break;
|
|
||||||
case Setting.Master_Volume:
|
|
||||||
scene.masterVolume = value ? parseInt(settingOptions[setting][value]) * 0.01 : 0;
|
|
||||||
scene.updateSoundVolume();
|
|
||||||
break;
|
|
||||||
case Setting.BGM_Volume:
|
|
||||||
scene.bgmVolume = value ? parseInt(settingOptions[setting][value]) * 0.01 : 0;
|
|
||||||
scene.updateSoundVolume();
|
|
||||||
break;
|
|
||||||
case Setting.SE_Volume:
|
|
||||||
scene.seVolume = value ? parseInt(settingOptions[setting][value]) * 0.01 : 0;
|
|
||||||
scene.updateSoundVolume();
|
|
||||||
break;
|
|
||||||
case Setting.Damage_Numbers:
|
|
||||||
scene.damageNumbersMode = value;
|
|
||||||
break;
|
|
||||||
case Setting.UI_Theme:
|
|
||||||
scene.uiTheme = value;
|
|
||||||
break;
|
|
||||||
case Setting.Window_Type:
|
|
||||||
updateWindowType(scene, parseInt(settingOptions[setting][value]));
|
|
||||||
break;
|
|
||||||
case Setting.Tutorials:
|
|
||||||
scene.enableTutorials = settingOptions[setting][value] === "On";
|
|
||||||
break;
|
|
||||||
case Setting.Enable_Retries:
|
|
||||||
scene.enableRetries = settingOptions[setting][value] === "On";
|
|
||||||
break;
|
|
||||||
case Setting.Candy_Upgrade_Notification:
|
|
||||||
if (scene.candyUpgradeNotification === value) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
scene.candyUpgradeNotification = value;
|
|
||||||
scene.eventTarget.dispatchEvent(new CandyUpgradeNotificationChangedEvent(value));
|
|
||||||
break;
|
|
||||||
case Setting.Candy_Upgrade_Display:
|
|
||||||
scene.candyUpgradeDisplay = value;
|
|
||||||
case Setting.Money_Format:
|
|
||||||
switch (settingOptions[setting][value]) {
|
|
||||||
case "Normal":
|
|
||||||
scene.moneyFormat = MoneyFormat.NORMAL;
|
|
||||||
break;
|
|
||||||
case "Abbreviated":
|
|
||||||
scene.moneyFormat = MoneyFormat.ABBREVIATED;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
scene.updateMoneyText(false);
|
|
||||||
break;
|
|
||||||
case Setting.Sprite_Set:
|
|
||||||
scene.experimentalSprites = !!value;
|
|
||||||
if (value) {
|
|
||||||
scene.initExpSprites();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Setting.Move_Animations:
|
|
||||||
scene.moveAnimations = settingOptions[setting][value] === "On";
|
|
||||||
break;
|
|
||||||
case Setting.Show_Moveset_Flyout:
|
|
||||||
scene.showMovesetFlyout = settingOptions[setting][value] === "On";
|
|
||||||
break;
|
|
||||||
case Setting.Show_Stats_on_Level_Up:
|
|
||||||
scene.showLevelUpStats = settingOptions[setting][value] === "On";
|
|
||||||
break;
|
|
||||||
case Setting.EXP_Gains_Speed:
|
|
||||||
scene.expGainsSpeed = value;
|
|
||||||
break;
|
|
||||||
case Setting.EXP_Party_Display:
|
|
||||||
scene.expParty = value;
|
|
||||||
break;
|
|
||||||
case Setting.HP_Bar_Speed:
|
|
||||||
scene.hpBarSpeed = value;
|
|
||||||
break;
|
|
||||||
case Setting.Fusion_Palette_Swaps:
|
|
||||||
scene.fusionPaletteSwaps = !!value;
|
|
||||||
break;
|
|
||||||
case Setting.Player_Gender:
|
|
||||||
if (scene.gameData) {
|
|
||||||
const female = settingOptions[setting][value] === "Girl";
|
|
||||||
scene.gameData.gender = female ? PlayerGender.FEMALE : PlayerGender.MALE;
|
|
||||||
scene.trainer.setTexture(scene.trainer.texture.key.replace(female ? "m" : "f", female ? "f" : "m"));
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Setting.Touch_Controls:
|
|
||||||
scene.enableTouchControls = settingOptions[setting][value] !== "Disabled" && hasTouchscreen();
|
|
||||||
const touchControls = document.getElementById("touchControls");
|
|
||||||
if (touchControls) {
|
|
||||||
touchControls.classList.toggle("visible", scene.enableTouchControls);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Setting.Vibration:
|
|
||||||
scene.enableVibration = settingOptions[setting][value] !== "Disabled" && hasTouchscreen();
|
|
||||||
break;
|
|
||||||
case Setting.Skip_Seen_Dialogues:
|
|
||||||
scene.skipSeenDialogues = settingOptions[setting][value] === "On";
|
|
||||||
break;
|
|
||||||
case Setting.Language:
|
|
||||||
if (value) {
|
|
||||||
if (scene.ui) {
|
|
||||||
const cancelHandler = () => {
|
|
||||||
scene.ui.revertMode();
|
|
||||||
(scene.ui.getHandler() as SettingsUiHandler).setOptionCursor(Object.values(Setting).indexOf(Setting.Language), 0, true);
|
|
||||||
};
|
|
||||||
const changeLocaleHandler = (locale: string): boolean => {
|
|
||||||
try {
|
|
||||||
i18next.changeLanguage(locale);
|
|
||||||
localStorage.setItem("prLang", locale);
|
|
||||||
cancelHandler();
|
|
||||||
// Reload the whole game to apply the new locale since also some constants are translated
|
|
||||||
window.location.reload();
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error changing locale:", error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
scene.ui.setOverlayMode(Mode.OPTION_SELECT, {
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: "English",
|
|
||||||
handler: () => changeLocaleHandler("en")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Español",
|
|
||||||
handler: () => changeLocaleHandler("es")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Italiano",
|
|
||||||
handler: () => changeLocaleHandler("it")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Français",
|
|
||||||
handler: () => changeLocaleHandler("fr")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Deutsch",
|
|
||||||
handler: () => changeLocaleHandler("de")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Português (BR)",
|
|
||||||
handler: () => changeLocaleHandler("pt_BR")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "简体中文",
|
|
||||||
handler: () => changeLocaleHandler("zh_CN")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "繁體中文",
|
|
||||||
handler: () => changeLocaleHandler("zh_TW")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "한국어",
|
|
||||||
handler: () => changeLocaleHandler("ko")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Cancel",
|
|
||||||
handler: () => cancelHandler()
|
|
||||||
}
|
|
||||||
],
|
|
||||||
maxOptions: 7
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
@ -1,10 +1,9 @@
|
|||||||
import BattleScene from "../battle-scene";
|
import BattleScene from "../../battle-scene";
|
||||||
import {SettingDefaults, SettingOptions} from "./settings";
|
import SettingsGamepadUiHandler from "../../ui/settings/settings-gamepad-ui-handler";
|
||||||
import SettingsGamepadUiHandler from "../ui/settings/settings-gamepad-ui-handler";
|
import {Mode} from "../../ui/ui";
|
||||||
import {Mode} from "../ui/ui";
|
import {truncateString} from "../../utils";
|
||||||
import {truncateString} from "../utils";
|
import {Button} from "../../enums/buttons";
|
||||||
import {Button} from "../enums/buttons";
|
import {SettingKeyboard} from "#app/system/settings/settings-keyboard";
|
||||||
import {SettingKeyboard} from "#app/system/settings-keyboard";
|
|
||||||
|
|
||||||
export enum SettingGamepad {
|
export enum SettingGamepad {
|
||||||
Controller = "CONTROLLER",
|
Controller = "CONTROLLER",
|
||||||
@ -28,29 +27,31 @@ export enum SettingGamepad {
|
|||||||
Button_Submit = "BUTTON_SUBMIT",
|
Button_Submit = "BUTTON_SUBMIT",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const settingGamepadOptions: SettingOptions = {
|
const pressAction = "Press action to assign";
|
||||||
|
|
||||||
|
export const settingGamepadOptions = {
|
||||||
[SettingGamepad.Controller]: ["Default", "Change"],
|
[SettingGamepad.Controller]: ["Default", "Change"],
|
||||||
[SettingGamepad.Gamepad_Support]: ["Auto", "Disabled"],
|
[SettingGamepad.Gamepad_Support]: ["Auto", "Disabled"],
|
||||||
[SettingGamepad.Button_Up]: [`KEY ${Button.UP.toString()}`, "Press action to assign"],
|
[SettingGamepad.Button_Up]: [`KEY ${Button.UP.toString()}`, pressAction],
|
||||||
[SettingGamepad.Button_Down]: [`KEY ${Button.DOWN.toString()}`, "Press action to assign"],
|
[SettingGamepad.Button_Down]: [`KEY ${Button.DOWN.toString()}`, pressAction],
|
||||||
[SettingGamepad.Button_Left]: [`KEY ${Button.LEFT.toString()}`, "Press action to assign"],
|
[SettingGamepad.Button_Left]: [`KEY ${Button.LEFT.toString()}`, pressAction],
|
||||||
[SettingGamepad.Button_Right]: [`KEY ${Button.RIGHT.toString()}`, "Press action to assign"],
|
[SettingGamepad.Button_Right]: [`KEY ${Button.RIGHT.toString()}`, pressAction],
|
||||||
[SettingGamepad.Button_Action]: [`KEY ${Button.ACTION.toString()}`, "Press action to assign"],
|
[SettingGamepad.Button_Action]: [`KEY ${Button.ACTION.toString()}`, pressAction],
|
||||||
[SettingGamepad.Button_Cancel]: [`KEY ${Button.CANCEL.toString()}`, "Press action to assign"],
|
[SettingGamepad.Button_Cancel]: [`KEY ${Button.CANCEL.toString()}`, pressAction],
|
||||||
[SettingGamepad.Button_Menu]: [`KEY ${Button.MENU.toString()}`, "Press action to assign"],
|
[SettingGamepad.Button_Menu]: [`KEY ${Button.MENU.toString()}`, pressAction],
|
||||||
[SettingGamepad.Button_Stats]: [`KEY ${Button.STATS.toString()}`, "Press action to assign"],
|
[SettingGamepad.Button_Stats]: [`KEY ${Button.STATS.toString()}`, pressAction],
|
||||||
[SettingGamepad.Button_Cycle_Form]: [`KEY ${Button.CYCLE_FORM.toString()}`, "Press action to assign"],
|
[SettingGamepad.Button_Cycle_Form]: [`KEY ${Button.CYCLE_FORM.toString()}`, pressAction],
|
||||||
[SettingGamepad.Button_Cycle_Shiny]: [`KEY ${Button.CYCLE_SHINY.toString()}`, "Press action to assign"],
|
[SettingGamepad.Button_Cycle_Shiny]: [`KEY ${Button.CYCLE_SHINY.toString()}`, pressAction],
|
||||||
[SettingGamepad.Button_Cycle_Gender]: [`KEY ${Button.CYCLE_GENDER.toString()}`, "Press action to assign"],
|
[SettingGamepad.Button_Cycle_Gender]: [`KEY ${Button.CYCLE_GENDER.toString()}`, pressAction],
|
||||||
[SettingGamepad.Button_Cycle_Ability]: [`KEY ${Button.CYCLE_ABILITY.toString()}`, "Press action to assign"],
|
[SettingGamepad.Button_Cycle_Ability]: [`KEY ${Button.CYCLE_ABILITY.toString()}`, pressAction],
|
||||||
[SettingGamepad.Button_Cycle_Nature]: [`KEY ${Button.CYCLE_NATURE.toString()}`, "Press action to assign"],
|
[SettingGamepad.Button_Cycle_Nature]: [`KEY ${Button.CYCLE_NATURE.toString()}`, pressAction],
|
||||||
[SettingGamepad.Button_Cycle_Variant]: [`KEY ${Button.V.toString()}`, "Press action to assign"],
|
[SettingGamepad.Button_Cycle_Variant]: [`KEY ${Button.V.toString()}`, pressAction],
|
||||||
[SettingGamepad.Button_Speed_Up]: [`KEY ${Button.SPEED_UP.toString()}`, "Press action to assign"],
|
[SettingGamepad.Button_Speed_Up]: [`KEY ${Button.SPEED_UP.toString()}`, pressAction],
|
||||||
[SettingGamepad.Button_Slow_Down]: [`KEY ${Button.SLOW_DOWN.toString()}`, "Press action to assign"],
|
[SettingGamepad.Button_Slow_Down]: [`KEY ${Button.SLOW_DOWN.toString()}`, pressAction],
|
||||||
[SettingGamepad.Button_Submit]: [`KEY ${Button.SUBMIT.toString()}`, "Press action to assign"],
|
[SettingGamepad.Button_Submit]: [`KEY ${Button.SUBMIT.toString()}`, pressAction],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const settingGamepadDefaults: SettingDefaults = {
|
export const settingGamepadDefaults = {
|
||||||
[SettingGamepad.Controller]: 0,
|
[SettingGamepad.Controller]: 0,
|
||||||
[SettingGamepad.Gamepad_Support]: 0,
|
[SettingGamepad.Gamepad_Support]: 0,
|
||||||
[SettingGamepad.Button_Up]: 0,
|
[SettingGamepad.Button_Up]: 0,
|
@ -1,4 +1,3 @@
|
|||||||
import {SettingDefaults, SettingOptions} from "#app/system/settings";
|
|
||||||
import {Button} from "#app/enums/buttons";
|
import {Button} from "#app/enums/buttons";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import {Mode} from "#app/ui/ui";
|
import {Mode} from "#app/ui/ui";
|
||||||
@ -42,46 +41,47 @@ export enum SettingKeyboard {
|
|||||||
Alt_Button_Submit = "ALT_BUTTON_SUBMIT",
|
Alt_Button_Submit = "ALT_BUTTON_SUBMIT",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const settingKeyboardOptions: SettingOptions = {
|
const pressAction = "Press action to assign";
|
||||||
// [SettingKeyboard.Default_Layout]: ['Default'],
|
|
||||||
[SettingKeyboard.Button_Up]: [`KEY ${Button.UP.toString()}`, "Press action to assign"],
|
|
||||||
[SettingKeyboard.Button_Down]: [`KEY ${Button.DOWN.toString()}`, "Press action to assign"],
|
|
||||||
[SettingKeyboard.Alt_Button_Up]: [`KEY ${Button.UP.toString()}`, "Press action to assign"],
|
|
||||||
[SettingKeyboard.Button_Left]: [`KEY ${Button.LEFT.toString()}`, "Press action to assign"],
|
|
||||||
[SettingKeyboard.Button_Right]: [`KEY ${Button.RIGHT.toString()}`, "Press action to assign"],
|
|
||||||
[SettingKeyboard.Button_Action]: [`KEY ${Button.ACTION.toString()}`, "Press action to assign"],
|
|
||||||
[SettingKeyboard.Button_Menu]: [`KEY ${Button.MENU.toString()}`, "Press action to assign"],
|
|
||||||
[SettingKeyboard.Button_Submit]: [`KEY ${Button.SUBMIT.toString()}`, "Press action to assign"],
|
|
||||||
|
|
||||||
[SettingKeyboard.Alt_Button_Down]: [`KEY ${Button.DOWN.toString()}`, "Press action to assign"],
|
export const settingKeyboardOptions = {
|
||||||
[SettingKeyboard.Alt_Button_Left]: [`KEY ${Button.LEFT.toString()}`, "Press action to assign"],
|
// [SettingKeyboard.Default_Layout]: ['Default'],
|
||||||
[SettingKeyboard.Alt_Button_Right]: [`KEY ${Button.RIGHT.toString()}`, "Press action to assign"],
|
[SettingKeyboard.Button_Up]: [`KEY ${Button.UP.toString()}`, pressAction],
|
||||||
[SettingKeyboard.Alt_Button_Action]: [`KEY ${Button.ACTION.toString()}`, "Press action to assign"],
|
[SettingKeyboard.Button_Down]: [`KEY ${Button.DOWN.toString()}`, pressAction],
|
||||||
[SettingKeyboard.Button_Cancel]: [`KEY ${Button.CANCEL.toString()}`, "Press action to assign"],
|
[SettingKeyboard.Alt_Button_Up]: [`KEY ${Button.UP.toString()}`, pressAction],
|
||||||
[SettingKeyboard.Alt_Button_Cancel]: [`KEY ${Button.CANCEL.toString()}`, "Press action to assign"],
|
[SettingKeyboard.Button_Left]: [`KEY ${Button.LEFT.toString()}`, pressAction],
|
||||||
[SettingKeyboard.Alt_Button_Menu]: [`KEY ${Button.MENU.toString()}`, "Press action to assign"],
|
[SettingKeyboard.Button_Right]: [`KEY ${Button.RIGHT.toString()}`, pressAction],
|
||||||
[SettingKeyboard.Button_Stats]: [`KEY ${Button.STATS.toString()}`, "Press action to assign"],
|
[SettingKeyboard.Button_Action]: [`KEY ${Button.ACTION.toString()}`, pressAction],
|
||||||
[SettingKeyboard.Alt_Button_Stats]: [`KEY ${Button.STATS.toString()}`, "Press action to assign"],
|
[SettingKeyboard.Button_Menu]: [`KEY ${Button.MENU.toString()}`, pressAction],
|
||||||
[SettingKeyboard.Button_Cycle_Form]: [`KEY ${Button.CYCLE_FORM.toString()}`, "Press action to assign"],
|
[SettingKeyboard.Button_Submit]: [`KEY ${Button.SUBMIT.toString()}`, pressAction],
|
||||||
[SettingKeyboard.Alt_Button_Cycle_Form]: [`KEY ${Button.CYCLE_FORM.toString()}`, "Press action to assign"],
|
[SettingKeyboard.Alt_Button_Down]: [`KEY ${Button.DOWN.toString()}`, pressAction],
|
||||||
[SettingKeyboard.Button_Cycle_Shiny]: [`KEY ${Button.CYCLE_SHINY.toString()}`, "Press action to assign"],
|
[SettingKeyboard.Alt_Button_Left]: [`KEY ${Button.LEFT.toString()}`, pressAction],
|
||||||
[SettingKeyboard.Alt_Button_Cycle_Shiny]: [`KEY ${Button.CYCLE_SHINY.toString()}`, "Press action to assign"],
|
[SettingKeyboard.Alt_Button_Right]: [`KEY ${Button.RIGHT.toString()}`, pressAction],
|
||||||
[SettingKeyboard.Button_Cycle_Gender]: [`KEY ${Button.CYCLE_GENDER.toString()}`, "Press action to assign"],
|
[SettingKeyboard.Alt_Button_Action]: [`KEY ${Button.ACTION.toString()}`, pressAction],
|
||||||
[SettingKeyboard.Alt_Button_Cycle_Gender]: [`KEY ${Button.CYCLE_GENDER.toString()}`, "Press action to assign"],
|
[SettingKeyboard.Button_Cancel]: [`KEY ${Button.CANCEL.toString()}`, pressAction],
|
||||||
[SettingKeyboard.Button_Cycle_Ability]: [`KEY ${Button.CYCLE_ABILITY.toString()}`, "Press action to assign"],
|
[SettingKeyboard.Alt_Button_Cancel]: [`KEY ${Button.CANCEL.toString()}`, pressAction],
|
||||||
[SettingKeyboard.Alt_Button_Cycle_Ability]: [`KEY ${Button.CYCLE_ABILITY.toString()}`, "Press action to assign"],
|
[SettingKeyboard.Alt_Button_Menu]: [`KEY ${Button.MENU.toString()}`, pressAction],
|
||||||
[SettingKeyboard.Button_Cycle_Nature]: [`KEY ${Button.CYCLE_NATURE.toString()}`, "Press action to assign"],
|
[SettingKeyboard.Button_Stats]: [`KEY ${Button.STATS.toString()}`, pressAction],
|
||||||
[SettingKeyboard.Alt_Button_Cycle_Nature]: [`KEY ${Button.CYCLE_NATURE.toString()}`, "Press action to assign"],
|
[SettingKeyboard.Alt_Button_Stats]: [`KEY ${Button.STATS.toString()}`, pressAction],
|
||||||
[SettingKeyboard.Button_Cycle_Variant]: [`KEY ${Button.V.toString()}`, "Press action to assign"],
|
[SettingKeyboard.Button_Cycle_Form]: [`KEY ${Button.CYCLE_FORM.toString()}`, pressAction],
|
||||||
[SettingKeyboard.Alt_Button_Cycle_Variant]: [`KEY ${Button.V.toString()}`, "Press action to assign"],
|
[SettingKeyboard.Alt_Button_Cycle_Form]: [`KEY ${Button.CYCLE_FORM.toString()}`, pressAction],
|
||||||
[SettingKeyboard.Button_Speed_Up]: [`KEY ${Button.SPEED_UP.toString()}`, "Press action to assign"],
|
[SettingKeyboard.Button_Cycle_Shiny]: [`KEY ${Button.CYCLE_SHINY.toString()}`, pressAction],
|
||||||
[SettingKeyboard.Alt_Button_Speed_Up]: [`KEY ${Button.SPEED_UP.toString()}`, "Press action to assign"],
|
[SettingKeyboard.Alt_Button_Cycle_Shiny]: [`KEY ${Button.CYCLE_SHINY.toString()}`, pressAction],
|
||||||
[SettingKeyboard.Button_Slow_Down]: [`KEY ${Button.SLOW_DOWN.toString()}`, "Press action to assign"],
|
[SettingKeyboard.Button_Cycle_Gender]: [`KEY ${Button.CYCLE_GENDER.toString()}`, pressAction],
|
||||||
[SettingKeyboard.Alt_Button_Slow_Down]: [`KEY ${Button.SLOW_DOWN.toString()}`, "Press action to assign"],
|
[SettingKeyboard.Alt_Button_Cycle_Gender]: [`KEY ${Button.CYCLE_GENDER.toString()}`, pressAction],
|
||||||
[SettingKeyboard.Alt_Button_Submit]: [`KEY ${Button.SUBMIT.toString()}`, "Press action to assign"],
|
[SettingKeyboard.Button_Cycle_Ability]: [`KEY ${Button.CYCLE_ABILITY.toString()}`, pressAction],
|
||||||
|
[SettingKeyboard.Alt_Button_Cycle_Ability]: [`KEY ${Button.CYCLE_ABILITY.toString()}`, pressAction],
|
||||||
|
[SettingKeyboard.Button_Cycle_Nature]: [`KEY ${Button.CYCLE_NATURE.toString()}`, pressAction],
|
||||||
|
[SettingKeyboard.Alt_Button_Cycle_Nature]: [`KEY ${Button.CYCLE_NATURE.toString()}`, pressAction],
|
||||||
|
[SettingKeyboard.Button_Cycle_Variant]: [`KEY ${Button.V.toString()}`, pressAction],
|
||||||
|
[SettingKeyboard.Alt_Button_Cycle_Variant]: [`KEY ${Button.V.toString()}`, pressAction],
|
||||||
|
[SettingKeyboard.Button_Speed_Up]: [`KEY ${Button.SPEED_UP.toString()}`, pressAction],
|
||||||
|
[SettingKeyboard.Alt_Button_Speed_Up]: [`KEY ${Button.SPEED_UP.toString()}`, pressAction],
|
||||||
|
[SettingKeyboard.Button_Slow_Down]: [`KEY ${Button.SLOW_DOWN.toString()}`, pressAction],
|
||||||
|
[SettingKeyboard.Alt_Button_Slow_Down]: [`KEY ${Button.SLOW_DOWN.toString()}`, pressAction],
|
||||||
|
[SettingKeyboard.Alt_Button_Submit]: [`KEY ${Button.SUBMIT.toString()}`, pressAction],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const settingKeyboardDefaults: SettingDefaults = {
|
export const settingKeyboardDefaults = {
|
||||||
// [SettingKeyboard.Default_Layout]: 0,
|
// [SettingKeyboard.Default_Layout]: 0,
|
||||||
[SettingKeyboard.Button_Up]: 0,
|
[SettingKeyboard.Button_Up]: 0,
|
||||||
[SettingKeyboard.Button_Down]: 0,
|
[SettingKeyboard.Button_Down]: 0,
|
464
src/system/settings/settings.ts
Normal file
@ -0,0 +1,464 @@
|
|||||||
|
import { Mode } from "#app/ui/ui";
|
||||||
|
import i18next from "i18next";
|
||||||
|
import BattleScene from "../../battle-scene";
|
||||||
|
import { hasTouchscreen } from "../../touch-controls";
|
||||||
|
import { updateWindowType } from "../../ui/ui-theme";
|
||||||
|
import { PlayerGender } from "../game-data";
|
||||||
|
import { CandyUpgradeNotificationChangedEvent } from "#app/battle-scene-events.js";
|
||||||
|
import { MoneyFormat } from "../../enums/money-format";
|
||||||
|
import SettingsUiHandler from "#app/ui/settings/settings-ui-handler";
|
||||||
|
|
||||||
|
const MUTE = "Mute";
|
||||||
|
const VOLUME_OPTIONS = new Array(11).fill(null).map((_, i) => i ? (i * 10).toString() : MUTE);
|
||||||
|
const OFF_ON = ["Off", "On"];
|
||||||
|
const AUTO_DISABLED = ["Auto", "Disabled"];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Types for helping separate settings to different menus
|
||||||
|
*/
|
||||||
|
export enum SettingType {
|
||||||
|
GENERAL,
|
||||||
|
ACCESSIBILITY
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Setting {
|
||||||
|
key: string
|
||||||
|
label: string
|
||||||
|
options: Array<string>
|
||||||
|
default: number
|
||||||
|
type: SettingType
|
||||||
|
requireReload?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setting Keys for existing settings
|
||||||
|
* to be used when trying to find or update Settings
|
||||||
|
*/
|
||||||
|
export const SettingKeys = {
|
||||||
|
Game_Speed: "GAME_SPEED",
|
||||||
|
Master_Volume: "MASTER_VOLUME",
|
||||||
|
BGM_Volume: "BGM_VOLUME",
|
||||||
|
SE_Volume: "SE_VOLUME",
|
||||||
|
Language: "LANGUAGE",
|
||||||
|
Damage_Numbers: "DAMAGE_NUMBERS",
|
||||||
|
UI_Theme: "UI_THEME",
|
||||||
|
Window_Type: "WINDOW_TYPE",
|
||||||
|
Tutorials: "TUTORIALS",
|
||||||
|
Enable_Retries: "ENABLE_RETRIES",
|
||||||
|
Skip_Seen_Dialogues: "SKIP_SEEN_DIALOGUES",
|
||||||
|
Candy_Upgrade_Notification: "CANDY_UPGRADE_NOTIFICATION",
|
||||||
|
Candy_Upgrade_Display: "CANDY_UPGRADE_DISPLAY",
|
||||||
|
Money_Format: "MONEY_FORMAT",
|
||||||
|
Sprite_Set: "SPRITE_SET",
|
||||||
|
Move_Animations: "MOVE_ANIMATIONS",
|
||||||
|
Show_Moveset_Flyout: "SHOW_MOVESET_FLYOUT",
|
||||||
|
Show_Arena_Flyout: "SHOW_ARENA_FLYOUT",
|
||||||
|
Show_Stats_on_Level_Up: "SHOW_LEVEL_UP_STATS",
|
||||||
|
EXP_Gains_Speed: "EXP_GAINS_SPEED",
|
||||||
|
EXP_Party_Display: "EXP_PARTY_DISPLAY",
|
||||||
|
HP_Bar_Speed: "HP_BAR_SPEED",
|
||||||
|
Fusion_Palette_Swaps: "FUSION_PALETTE_SWAPS",
|
||||||
|
Player_Gender: "PLAYER_GENDER",
|
||||||
|
Touch_Controls: "TOUCH_CONTROLS",
|
||||||
|
Vibration: "VIBRATION"
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All Settings not related to controls
|
||||||
|
*/
|
||||||
|
export const Setting: Array<Setting> = [
|
||||||
|
{
|
||||||
|
key: SettingKeys.Game_Speed,
|
||||||
|
label: "Game Speed",
|
||||||
|
options: ["1x", "1.25x", "1.5x", "2x", "2.5x", "3x", "4x", "5x"],
|
||||||
|
default: 3,
|
||||||
|
type: SettingType.GENERAL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingKeys.Master_Volume,
|
||||||
|
label: "Master Volume",
|
||||||
|
options: VOLUME_OPTIONS,
|
||||||
|
default: 5,
|
||||||
|
type: SettingType.GENERAL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingKeys.BGM_Volume,
|
||||||
|
label: "BGM Volume",
|
||||||
|
options: VOLUME_OPTIONS,
|
||||||
|
default: 10,
|
||||||
|
type: SettingType.GENERAL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingKeys.SE_Volume,
|
||||||
|
label: "SE Volume",
|
||||||
|
options: VOLUME_OPTIONS,
|
||||||
|
default: 10,
|
||||||
|
type: SettingType.GENERAL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingKeys.Language,
|
||||||
|
label: "Language",
|
||||||
|
options: ["English", "Change"],
|
||||||
|
default: 0,
|
||||||
|
type: SettingType.GENERAL,
|
||||||
|
requireReload: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingKeys.Damage_Numbers,
|
||||||
|
label: "Damage Numbers",
|
||||||
|
options: ["Off", "Simple", "Fancy"],
|
||||||
|
default: 0,
|
||||||
|
type: SettingType.GENERAL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingKeys.UI_Theme,
|
||||||
|
label: "UI Theme",
|
||||||
|
options: ["Default", "Legacy"],
|
||||||
|
default: 0,
|
||||||
|
type: SettingType.GENERAL,
|
||||||
|
requireReload: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingKeys.Window_Type,
|
||||||
|
label: "Window Type",
|
||||||
|
options: new Array(5).fill(null).map((_, i) => (i + 1).toString()),
|
||||||
|
default: 0,
|
||||||
|
type: SettingType.GENERAL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingKeys.Tutorials,
|
||||||
|
label: "Tutorials",
|
||||||
|
options: OFF_ON,
|
||||||
|
default: 1,
|
||||||
|
type: SettingType.GENERAL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingKeys.Enable_Retries,
|
||||||
|
label: "Enable Retries",
|
||||||
|
options: OFF_ON,
|
||||||
|
default: 0,
|
||||||
|
type: SettingType.ACCESSIBILITY
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingKeys.Skip_Seen_Dialogues,
|
||||||
|
label: "Skip Seen Dialogues",
|
||||||
|
options: OFF_ON,
|
||||||
|
default: 0,
|
||||||
|
type: SettingType.GENERAL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingKeys.Candy_Upgrade_Notification,
|
||||||
|
label: "Candy Upgrade Notification",
|
||||||
|
options: ["Off", "Passives Only", "On"],
|
||||||
|
default: 0,
|
||||||
|
type: SettingType.ACCESSIBILITY
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingKeys.Candy_Upgrade_Display,
|
||||||
|
label: "Candy Upgrade Display",
|
||||||
|
options: ["Icon", "Animation"],
|
||||||
|
default: 0,
|
||||||
|
type: SettingType.ACCESSIBILITY,
|
||||||
|
requireReload: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingKeys.Money_Format,
|
||||||
|
label: "Money Format",
|
||||||
|
options: ["Normal", "Abbreviated"],
|
||||||
|
default: 0,
|
||||||
|
type: SettingType.ACCESSIBILITY
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingKeys.Sprite_Set,
|
||||||
|
label: "Sprite Set",
|
||||||
|
options: ["Consistent", "Mixed Animated"],
|
||||||
|
default: 0,
|
||||||
|
type: SettingType.GENERAL,
|
||||||
|
requireReload: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingKeys.Move_Animations,
|
||||||
|
label: "Move Animations",
|
||||||
|
options: OFF_ON,
|
||||||
|
default: 1,
|
||||||
|
type: SettingType.GENERAL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingKeys.Show_Moveset_Flyout,
|
||||||
|
label: "Show Moveset Flyout",
|
||||||
|
options: OFF_ON,
|
||||||
|
default: 1,
|
||||||
|
type: SettingType.ACCESSIBILITY
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingKeys.Show_Arena_Flyout,
|
||||||
|
label: "Show Battle Effects Flyout",
|
||||||
|
options: OFF_ON,
|
||||||
|
default: 1,
|
||||||
|
type: SettingType.ACCESSIBILITY
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingKeys.Show_Stats_on_Level_Up,
|
||||||
|
label: "Show Stats on Level Up",
|
||||||
|
options: OFF_ON,
|
||||||
|
default: 1,
|
||||||
|
type: SettingType.GENERAL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingKeys.EXP_Gains_Speed,
|
||||||
|
label: "EXP Gains Speed",
|
||||||
|
options: ["Normal", "Fast", "Faster", "Skip"],
|
||||||
|
default: 0,
|
||||||
|
type: SettingType.GENERAL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingKeys.EXP_Party_Display,
|
||||||
|
label: "EXP Party Display",
|
||||||
|
options: ["Normal", "Level Up Notification", "Skip"],
|
||||||
|
default: 0,
|
||||||
|
type: SettingType.GENERAL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingKeys.HP_Bar_Speed,
|
||||||
|
label: "HP Bar Speed",
|
||||||
|
options: ["Normal", "Fast", "Faster", "Skip"],
|
||||||
|
default: 0,
|
||||||
|
type: SettingType.GENERAL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingKeys.Fusion_Palette_Swaps,
|
||||||
|
label: "Fusion Palette Swaps",
|
||||||
|
options: OFF_ON,
|
||||||
|
default: 1,
|
||||||
|
type: SettingType.GENERAL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingKeys.Player_Gender,
|
||||||
|
label: "Player Gender",
|
||||||
|
options: ["Boy", "Girl"],
|
||||||
|
default: 0,
|
||||||
|
type: SettingType.GENERAL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingKeys.Touch_Controls,
|
||||||
|
label: "Touch Controls",
|
||||||
|
options: AUTO_DISABLED,
|
||||||
|
default: 0,
|
||||||
|
type: SettingType.GENERAL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingKeys.Vibration,
|
||||||
|
label: "Vibration",
|
||||||
|
options: AUTO_DISABLED,
|
||||||
|
default: 0,
|
||||||
|
type: SettingType.GENERAL
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the index of a Setting
|
||||||
|
* @param key SettingKey
|
||||||
|
* @returns index or -1 if doesn't exist
|
||||||
|
*/
|
||||||
|
export function settingIndex(key: string) {
|
||||||
|
return Setting.findIndex(s => s.key === key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets all settings to their defaults
|
||||||
|
* @param scene current BattleScene
|
||||||
|
*/
|
||||||
|
export function resetSettings(scene: BattleScene) {
|
||||||
|
Setting.forEach(s => setSetting(scene, s.key, s.default));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a setting for current BattleScene
|
||||||
|
* @param scene current BattleScene
|
||||||
|
* @param setting string ideally from SettingKeys
|
||||||
|
* @param value value to update setting with
|
||||||
|
* @returns true if successful, false if not
|
||||||
|
*/
|
||||||
|
export function setSetting(scene: BattleScene, setting: string, value: integer): boolean {
|
||||||
|
const index: number = settingIndex(setting);
|
||||||
|
if ( index === -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
switch (Setting[index].key) {
|
||||||
|
case SettingKeys.Game_Speed:
|
||||||
|
scene.gameSpeed = parseFloat(Setting[index].options[value].replace("x", ""));
|
||||||
|
break;
|
||||||
|
case SettingKeys.Master_Volume:
|
||||||
|
scene.masterVolume = value ? parseInt(Setting[index].options[value]) * 0.01 : 0;
|
||||||
|
scene.updateSoundVolume();
|
||||||
|
break;
|
||||||
|
case SettingKeys.BGM_Volume:
|
||||||
|
scene.bgmVolume = value ? parseInt(Setting[index].options[value]) * 0.01 : 0;
|
||||||
|
scene.updateSoundVolume();
|
||||||
|
break;
|
||||||
|
case SettingKeys.SE_Volume:
|
||||||
|
scene.seVolume = value ? parseInt(Setting[index].options[value]) * 0.01 : 0;
|
||||||
|
scene.updateSoundVolume();
|
||||||
|
break;
|
||||||
|
case SettingKeys.Damage_Numbers:
|
||||||
|
scene.damageNumbersMode = value;
|
||||||
|
break;
|
||||||
|
case SettingKeys.UI_Theme:
|
||||||
|
scene.uiTheme = value;
|
||||||
|
break;
|
||||||
|
case SettingKeys.Window_Type:
|
||||||
|
updateWindowType(scene, parseInt(Setting[index].options[value]));
|
||||||
|
break;
|
||||||
|
case SettingKeys.Tutorials:
|
||||||
|
scene.enableTutorials = Setting[index].options[value] === "On";
|
||||||
|
break;
|
||||||
|
case SettingKeys.Enable_Retries:
|
||||||
|
scene.enableRetries = Setting[index].options[value] === "On";
|
||||||
|
break;
|
||||||
|
case SettingKeys.Skip_Seen_Dialogues:
|
||||||
|
scene.skipSeenDialogues = Setting[index].options[value] === "On";
|
||||||
|
break;
|
||||||
|
case SettingKeys.Candy_Upgrade_Notification:
|
||||||
|
if (scene.candyUpgradeNotification === value) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
scene.candyUpgradeNotification = value;
|
||||||
|
scene.eventTarget.dispatchEvent(new CandyUpgradeNotificationChangedEvent(value));
|
||||||
|
break;
|
||||||
|
case SettingKeys.Candy_Upgrade_Display:
|
||||||
|
scene.candyUpgradeDisplay = value;
|
||||||
|
case SettingKeys.Money_Format:
|
||||||
|
switch (Setting[index].options[value]) {
|
||||||
|
case "Normal":
|
||||||
|
scene.moneyFormat = MoneyFormat.NORMAL;
|
||||||
|
break;
|
||||||
|
case "Abbreviated":
|
||||||
|
scene.moneyFormat = MoneyFormat.ABBREVIATED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
scene.updateMoneyText(false);
|
||||||
|
break;
|
||||||
|
case SettingKeys.Sprite_Set:
|
||||||
|
scene.experimentalSprites = !!value;
|
||||||
|
if (value) {
|
||||||
|
scene.initExpSprites();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SettingKeys.Move_Animations:
|
||||||
|
scene.moveAnimations = Setting[index].options[value] === "On";
|
||||||
|
break;
|
||||||
|
case SettingKeys.Show_Moveset_Flyout:
|
||||||
|
scene.showMovesetFlyout = Setting[index].options[value] === "On";
|
||||||
|
break;
|
||||||
|
case SettingKeys.Show_Arena_Flyout:
|
||||||
|
scene.showArenaFlyout = Setting[index].options[value] === "On";
|
||||||
|
break;
|
||||||
|
case SettingKeys.Show_Stats_on_Level_Up:
|
||||||
|
scene.showLevelUpStats = Setting[index].options[value] === "On";
|
||||||
|
break;
|
||||||
|
case SettingKeys.EXP_Gains_Speed:
|
||||||
|
scene.expGainsSpeed = value;
|
||||||
|
break;
|
||||||
|
case SettingKeys.EXP_Party_Display:
|
||||||
|
scene.expParty = value;
|
||||||
|
break;
|
||||||
|
case SettingKeys.HP_Bar_Speed:
|
||||||
|
scene.hpBarSpeed = value;
|
||||||
|
break;
|
||||||
|
case SettingKeys.Fusion_Palette_Swaps:
|
||||||
|
scene.fusionPaletteSwaps = !!value;
|
||||||
|
break;
|
||||||
|
case SettingKeys.Player_Gender:
|
||||||
|
if (scene.gameData) {
|
||||||
|
const female = Setting[index].options[value] === "Girl";
|
||||||
|
scene.gameData.gender = female ? PlayerGender.FEMALE : PlayerGender.MALE;
|
||||||
|
scene.trainer.setTexture(scene.trainer.texture.key.replace(female ? "m" : "f", female ? "f" : "m"));
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SettingKeys.Touch_Controls:
|
||||||
|
scene.enableTouchControls = Setting[index].options[value] !== "Disabled" && hasTouchscreen();
|
||||||
|
const touchControls = document.getElementById("touchControls");
|
||||||
|
if (touchControls) {
|
||||||
|
touchControls.classList.toggle("visible", scene.enableTouchControls);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SettingKeys.Vibration:
|
||||||
|
scene.enableVibration = Setting[index].options[value] !== "Disabled" && hasTouchscreen();
|
||||||
|
break;
|
||||||
|
case SettingKeys.Language:
|
||||||
|
if (value) {
|
||||||
|
if (scene.ui) {
|
||||||
|
const cancelHandler = () => {
|
||||||
|
scene.ui.revertMode();
|
||||||
|
const languageSetting = Setting.find(setting => setting.key === SettingKeys.Language);
|
||||||
|
(scene.ui.getHandler() as SettingsUiHandler).setOptionCursor(Setting.indexOf(languageSetting), 0, true);
|
||||||
|
};
|
||||||
|
const changeLocaleHandler = (locale: string): boolean => {
|
||||||
|
try {
|
||||||
|
i18next.changeLanguage(locale);
|
||||||
|
localStorage.setItem("prLang", locale);
|
||||||
|
cancelHandler();
|
||||||
|
// Reload the whole game to apply the new locale since also some constants are translated
|
||||||
|
window.location.reload();
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error changing locale:", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
scene.ui.setOverlayMode(Mode.OPTION_SELECT, {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: "English",
|
||||||
|
handler: () => changeLocaleHandler("en")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Español",
|
||||||
|
handler: () => changeLocaleHandler("es")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Italiano",
|
||||||
|
handler: () => changeLocaleHandler("it")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Français",
|
||||||
|
handler: () => changeLocaleHandler("fr")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Deutsch",
|
||||||
|
handler: () => changeLocaleHandler("de")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Português (BR)",
|
||||||
|
handler: () => changeLocaleHandler("pt_BR")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "简体中文",
|
||||||
|
handler: () => changeLocaleHandler("zh_CN")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "繁體中文",
|
||||||
|
handler: () => changeLocaleHandler("zh_TW")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "한국어",
|
||||||
|
handler: () => changeLocaleHandler("ko")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Cancel",
|
||||||
|
handler: () => cancelHandler()
|
||||||
|
}
|
||||||
|
],
|
||||||
|
maxOptions: 7
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
@ -3,7 +3,7 @@ import {
|
|||||||
getSettingNameWithKeycode
|
getSettingNameWithKeycode
|
||||||
} from "#app/configs/inputs/configHandler";
|
} from "#app/configs/inputs/configHandler";
|
||||||
import {expect} from "vitest";
|
import {expect} from "vitest";
|
||||||
import {SettingKeyboard} from "#app/system/settings-keyboard";
|
import {SettingKeyboard} from "#app/system/settings/settings-keyboard";
|
||||||
|
|
||||||
export class InGameManip {
|
export class InGameManip {
|
||||||
private config;
|
private config;
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
assign,
|
assign,
|
||||||
getSettingNameWithKeycode, canIAssignThisKey, canIDeleteThisKey, canIOverrideThisSetting
|
getSettingNameWithKeycode, canIAssignThisKey, canIDeleteThisKey, canIOverrideThisSetting
|
||||||
} from "#app/configs/inputs/configHandler";
|
} from "#app/configs/inputs/configHandler";
|
||||||
import {SettingKeyboard} from "#app/system/settings-keyboard";
|
import {SettingKeyboard} from "#app/system/settings/settings-keyboard";
|
||||||
|
|
||||||
export class MenuManip {
|
export class MenuManip {
|
||||||
private config;
|
private config;
|
||||||
|
@ -10,7 +10,7 @@ import {InGameManip} from "#app/test/helpers/inGameManip";
|
|||||||
import {Device} from "#app/enums/devices";
|
import {Device} from "#app/enums/devices";
|
||||||
import {InterfaceConfig} from "#app/inputs-controller";
|
import {InterfaceConfig} from "#app/inputs-controller";
|
||||||
import cfg_keyboard_qwerty from "#app/configs/inputs/cfg_keyboard_qwerty";
|
import cfg_keyboard_qwerty from "#app/configs/inputs/cfg_keyboard_qwerty";
|
||||||
import {SettingKeyboard} from "#app/system/settings-keyboard";
|
import {SettingKeyboard} from "#app/system/settings/settings-keyboard";
|
||||||
|
|
||||||
|
|
||||||
describe("Test Rebinding", () => {
|
describe("Test Rebinding", () => {
|
||||||
|
@ -3,12 +3,13 @@ import {Mode} from "./ui/ui";
|
|||||||
import {InputsController} from "./inputs-controller";
|
import {InputsController} from "./inputs-controller";
|
||||||
import MessageUiHandler from "./ui/message-ui-handler";
|
import MessageUiHandler from "./ui/message-ui-handler";
|
||||||
import StarterSelectUiHandler from "./ui/starter-select-ui-handler";
|
import StarterSelectUiHandler from "./ui/starter-select-ui-handler";
|
||||||
import {Setting, settingOptions} from "./system/settings";
|
import {Setting, SettingKeys, settingIndex} from "./system/settings/settings";
|
||||||
import SettingsUiHandler from "./ui/settings/settings-ui-handler";
|
import SettingsUiHandler from "./ui/settings/settings-ui-handler";
|
||||||
import {Button} from "./enums/buttons";
|
import {Button} from "./enums/buttons";
|
||||||
import SettingsGamepadUiHandler from "./ui/settings/settings-gamepad-ui-handler";
|
import SettingsGamepadUiHandler from "./ui/settings/settings-gamepad-ui-handler";
|
||||||
import SettingsKeyboardUiHandler from "#app/ui/settings/settings-keyboard-ui-handler";
|
import SettingsKeyboardUiHandler from "#app/ui/settings/settings-keyboard-ui-handler";
|
||||||
import BattleScene from "./battle-scene";
|
import BattleScene from "./battle-scene";
|
||||||
|
import SettingsAccessibilityUiHandler from "./ui/settings/settings-accessiblity-ui-handler";
|
||||||
|
|
||||||
type ActionKeys = Record<Button, () => void>;
|
type ActionKeys = Record<Button, () => void>;
|
||||||
|
|
||||||
@ -68,7 +69,7 @@ export class UiInputs {
|
|||||||
[Button.CYCLE_GENDER]: () => this.buttonCycleOption(Button.CYCLE_GENDER),
|
[Button.CYCLE_GENDER]: () => this.buttonCycleOption(Button.CYCLE_GENDER),
|
||||||
[Button.CYCLE_ABILITY]: () => this.buttonCycleOption(Button.CYCLE_ABILITY),
|
[Button.CYCLE_ABILITY]: () => this.buttonCycleOption(Button.CYCLE_ABILITY),
|
||||||
[Button.CYCLE_NATURE]: () => this.buttonCycleOption(Button.CYCLE_NATURE),
|
[Button.CYCLE_NATURE]: () => this.buttonCycleOption(Button.CYCLE_NATURE),
|
||||||
[Button.V]: () => this.buttonCycleOption(Button.V),
|
[Button.V]: () => this.buttonCycleOption(Button.V),
|
||||||
[Button.SPEED_UP]: () => this.buttonSpeedChange(),
|
[Button.SPEED_UP]: () => this.buttonSpeedChange(),
|
||||||
[Button.SLOW_DOWN]: () => this.buttonSpeedChange(false),
|
[Button.SLOW_DOWN]: () => this.buttonSpeedChange(false),
|
||||||
};
|
};
|
||||||
@ -118,12 +119,14 @@ export class UiInputs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
buttonInfo(pressed: boolean = true): void {
|
buttonInfo(pressed: boolean = true): void {
|
||||||
if (!this.scene.showMovesetFlyout) {
|
if (this.scene.showMovesetFlyout ) {
|
||||||
return;
|
for (const p of this.scene.getField().filter(p => p?.isActive(true))) {
|
||||||
|
p.toggleFlyout(pressed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const p of this.scene.getField().filter(p => p?.isActive(true))) {
|
if (this.scene.showArenaFlyout) {
|
||||||
p.toggleFlyout(pressed);
|
this.scene.ui.processInfoButton(pressed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,7 +164,7 @@ export class UiInputs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
buttonCycleOption(button: Button): void {
|
buttonCycleOption(button: Button): void {
|
||||||
const whitelist = [StarterSelectUiHandler, SettingsUiHandler, SettingsGamepadUiHandler, SettingsKeyboardUiHandler];
|
const whitelist = [StarterSelectUiHandler, SettingsUiHandler, SettingsAccessibilityUiHandler, SettingsGamepadUiHandler, SettingsKeyboardUiHandler];
|
||||||
const uiHandler = this.scene.ui?.getHandler();
|
const uiHandler = this.scene.ui?.getHandler();
|
||||||
if (whitelist.some(handler => uiHandler instanceof handler)) {
|
if (whitelist.some(handler => uiHandler instanceof handler)) {
|
||||||
this.scene.ui.processInput(button);
|
this.scene.ui.processInput(button);
|
||||||
@ -171,17 +174,14 @@ export class UiInputs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
buttonSpeedChange(up = true): void {
|
buttonSpeedChange(up = true): void {
|
||||||
if (up) {
|
const settingGameSpeed = settingIndex(SettingKeys.Game_Speed);
|
||||||
if (this.scene.gameSpeed < 5) {
|
if (up && this.scene.gameSpeed < 5) {
|
||||||
this.scene.gameData.saveSetting(Setting.Game_Speed, settingOptions[Setting.Game_Speed].indexOf(`${this.scene.gameSpeed}x`) + 1);
|
this.scene.gameData.saveSetting(SettingKeys.Game_Speed, Setting[settingGameSpeed].options.indexOf(`${this.scene.gameSpeed}x`) + 1);
|
||||||
if (this.scene.ui?.getMode() === Mode.SETTINGS) {
|
if (this.scene.ui?.getMode() === Mode.SETTINGS) {
|
||||||
(this.scene.ui.getHandler() as SettingsUiHandler).show([]);
|
(this.scene.ui.getHandler() as SettingsUiHandler).show([]);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return;
|
} else if (!up && this.scene.gameSpeed > 1) {
|
||||||
}
|
this.scene.gameData.saveSetting(SettingKeys.Game_Speed, Math.max(Setting[settingGameSpeed].options.indexOf(`${this.scene.gameSpeed}x`) - 1, 0));
|
||||||
if (this.scene.gameSpeed > 1) {
|
|
||||||
this.scene.gameData.saveSetting(Setting.Game_Speed, Math.max(settingOptions[Setting.Game_Speed].indexOf(`${this.scene.gameSpeed}x`) - 1, 0));
|
|
||||||
if (this.scene.ui?.getMode() === Mode.SETTINGS) {
|
if (this.scene.ui?.getMode() === Mode.SETTINGS) {
|
||||||
(this.scene.ui.getHandler() as SettingsUiHandler).show([]);
|
(this.scene.ui.getHandler() as SettingsUiHandler).show([]);
|
||||||
}
|
}
|
||||||
|
384
src/ui/arena-flyout.ts
Normal file
@ -0,0 +1,384 @@
|
|||||||
|
import * as Utils from "../utils";
|
||||||
|
import { addTextObject, TextStyle } from "./text";
|
||||||
|
import BattleScene from "#app/battle-scene.js";
|
||||||
|
import { ArenaTagSide } from "#app/data/arena-tag.js";
|
||||||
|
import { WeatherType } from "#app/data/weather.js";
|
||||||
|
import { TerrainType } from "#app/data/terrain.js";
|
||||||
|
import { addWindow, WindowVariant } from "./ui-theme";
|
||||||
|
import { ArenaEvent, ArenaEventType, TagAddedEvent, TagRemovedEvent, TerrainChangedEvent, WeatherChangedEvent } from "#app/field/arena-events.js";
|
||||||
|
import { BattleSceneEventType, TurnEndEvent } from "#app/battle-scene-events.js";
|
||||||
|
import { ArenaTagType } from "#app/data/enums/arena-tag-type.js";
|
||||||
|
import { TimeOfDay } from "#app/data/enums/time-of-day.js";
|
||||||
|
|
||||||
|
/** Enum used to differentiate {@linkcode Arena} effects */
|
||||||
|
enum ArenaEffectType {
|
||||||
|
PLAYER,
|
||||||
|
WEATHER,
|
||||||
|
TERRAIN,
|
||||||
|
FIELD,
|
||||||
|
ENEMY,
|
||||||
|
}
|
||||||
|
/** Container for info about an {@linkcode Arena}'s effects */
|
||||||
|
interface ArenaEffectInfo {
|
||||||
|
/** The enum string representation of the effect */
|
||||||
|
name: string;
|
||||||
|
/** {@linkcode ArenaEffectType} type of effect */
|
||||||
|
type: ArenaEffectType,
|
||||||
|
|
||||||
|
/** The maximum duration set by the effect */
|
||||||
|
maxDuration: number;
|
||||||
|
/** The current duration left on the effect */
|
||||||
|
duration: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class ArenaFlyout extends Phaser.GameObjects.Container {
|
||||||
|
/** An alias for the scene typecast to a {@linkcode BattleScene} */
|
||||||
|
private battleScene: BattleScene;
|
||||||
|
|
||||||
|
/** The restricted width of the flyout which should be drawn to */
|
||||||
|
private flyoutWidth = 170;
|
||||||
|
/** The restricted height of the flyout which should be drawn to */
|
||||||
|
private flyoutHeight = 51;
|
||||||
|
|
||||||
|
/** The amount of translation animation on the x-axis */
|
||||||
|
private translationX: number;
|
||||||
|
/** The x-axis point where the flyout should sit when activated */
|
||||||
|
private anchorX: number;
|
||||||
|
/** The y-axis point where the flyout should sit when activated */
|
||||||
|
private anchorY: number;
|
||||||
|
|
||||||
|
/** The initial container which defines where the flyout should be attached */
|
||||||
|
private flyoutParent: Phaser.GameObjects.Container;
|
||||||
|
/** The container which defines the drawable dimensions of the flyout */
|
||||||
|
private flyoutContainer: Phaser.GameObjects.Container;
|
||||||
|
|
||||||
|
/** The background {@linkcode Phaser.GameObjects.NineSlice} window for the flyout */
|
||||||
|
private flyoutWindow: Phaser.GameObjects.NineSlice;
|
||||||
|
|
||||||
|
/** The header {@linkcode Phaser.GameObjects.NineSlice} window for the flyout */
|
||||||
|
private flyoutWindowHeader: Phaser.GameObjects.NineSlice;
|
||||||
|
/** The {@linkcode Phaser.GameObjects.Text} that goes inside of the header */
|
||||||
|
private flyoutTextHeader: Phaser.GameObjects.Text;
|
||||||
|
|
||||||
|
/** The {@linkcode Phaser.GameObjects.Sprite} that represents the current time of day */
|
||||||
|
private timeOfDayIcon: Phaser.GameObjects.Sprite;
|
||||||
|
|
||||||
|
/** The {@linkcode Phaser.GameObjects.Text} header used to indicate the player's effects */
|
||||||
|
private flyoutTextHeaderPlayer: Phaser.GameObjects.Text;
|
||||||
|
/** The {@linkcode Phaser.GameObjects.Text} header used to indicate the enemy's effects */
|
||||||
|
private flyoutTextHeaderEnemy: Phaser.GameObjects.Text;
|
||||||
|
/** The {@linkcode Phaser.GameObjects.Text} header used to indicate neutral effects */
|
||||||
|
private flyoutTextHeaderField: Phaser.GameObjects.Text;
|
||||||
|
|
||||||
|
/** The {@linkcode Phaser.GameObjects.Text} used to indicate the player's effects */
|
||||||
|
private flyoutTextPlayer: Phaser.GameObjects.Text;
|
||||||
|
/** The {@linkcode Phaser.GameObjects.Text} used to indicate the enemy's effects */
|
||||||
|
private flyoutTextEnemy: Phaser.GameObjects.Text;
|
||||||
|
/** The {@linkcode Phaser.GameObjects.Text} used to indicate neutral effects */
|
||||||
|
private flyoutTextField: Phaser.GameObjects.Text;
|
||||||
|
|
||||||
|
/** Container for all field effects observed by this object */
|
||||||
|
private readonly fieldEffectInfo: ArenaEffectInfo[] = [];
|
||||||
|
|
||||||
|
// Stores callbacks in a variable so they can be unsubscribed from when destroyed
|
||||||
|
private onNewArenaEvent = (event: Event) => this.onNewArena(event);
|
||||||
|
private onTurnInitEvent = (event: Event) => this.onTurnInit(event);
|
||||||
|
private onTurnEndEvent = (event: Event) => this.onTurnEnd(event);
|
||||||
|
|
||||||
|
private onFieldEffectChangedEvent = (event: Event) => this.onFieldEffectChanged(event);
|
||||||
|
|
||||||
|
constructor(scene: Phaser.Scene) {
|
||||||
|
super(scene, 0, 0);
|
||||||
|
this.battleScene = this.scene as BattleScene;
|
||||||
|
|
||||||
|
this.translationX = this.flyoutWidth;
|
||||||
|
this.anchorX = 0;
|
||||||
|
this.anchorY = -98;
|
||||||
|
|
||||||
|
this.flyoutParent = this.scene.add.container(this.anchorX - this.translationX, this.anchorY);
|
||||||
|
this.flyoutParent.setAlpha(0);
|
||||||
|
this.add(this.flyoutParent);
|
||||||
|
|
||||||
|
this.flyoutContainer = this.scene.add.container(0, 0);
|
||||||
|
this.flyoutParent.add(this.flyoutContainer);
|
||||||
|
|
||||||
|
this.flyoutWindow = addWindow(this.scene as BattleScene, 0, 0, this.flyoutWidth, this.flyoutHeight, false, false, 0, 0, WindowVariant.THIN);
|
||||||
|
this.flyoutContainer.add(this.flyoutWindow);
|
||||||
|
|
||||||
|
this.flyoutWindowHeader = addWindow(this.scene as BattleScene, this.flyoutWidth / 2, 0, this.flyoutWidth / 2, 14, false, false, 0, 0, WindowVariant.XTHIN);
|
||||||
|
this.flyoutWindowHeader.setOrigin();
|
||||||
|
|
||||||
|
this.flyoutContainer.add(this.flyoutWindowHeader);
|
||||||
|
|
||||||
|
this.flyoutTextHeader = addTextObject(this.scene, this.flyoutWidth / 2, 0, "Active Battle Effects", TextStyle.BATTLE_INFO);
|
||||||
|
this.flyoutTextHeader.setFontSize(54);
|
||||||
|
this.flyoutTextHeader.setAlign("center");
|
||||||
|
this.flyoutTextHeader.setOrigin();
|
||||||
|
|
||||||
|
this.flyoutContainer.add(this.flyoutTextHeader);
|
||||||
|
|
||||||
|
this.timeOfDayIcon = this.scene.add.sprite((this.flyoutWidth / 2) + (this.flyoutWindowHeader.displayWidth / 2), 0, "dawn_icon").setOrigin();
|
||||||
|
this.timeOfDayIcon.setVisible(false);
|
||||||
|
|
||||||
|
this.flyoutContainer.add(this.timeOfDayIcon);
|
||||||
|
|
||||||
|
this.flyoutTextHeaderPlayer = addTextObject(this.scene, 6, 5, "Player", TextStyle.SUMMARY_BLUE);
|
||||||
|
this.flyoutTextHeaderPlayer.setFontSize(54);
|
||||||
|
this.flyoutTextHeaderPlayer.setAlign("left");
|
||||||
|
this.flyoutTextHeaderPlayer.setOrigin(0, 0);
|
||||||
|
|
||||||
|
this.flyoutContainer.add(this.flyoutTextHeaderPlayer);
|
||||||
|
|
||||||
|
this.flyoutTextHeaderField = addTextObject(this.scene, this.flyoutWidth / 2, 5, "Neutral", TextStyle.SUMMARY_GREEN);
|
||||||
|
this.flyoutTextHeaderField.setFontSize(54);
|
||||||
|
this.flyoutTextHeaderField.setAlign("center");
|
||||||
|
this.flyoutTextHeaderField.setOrigin(0.5, 0);
|
||||||
|
|
||||||
|
this.flyoutContainer.add(this.flyoutTextHeaderField);
|
||||||
|
|
||||||
|
this.flyoutTextHeaderEnemy = addTextObject(this.scene, this.flyoutWidth - 6, 5, "Enemy", TextStyle.SUMMARY_RED);
|
||||||
|
this.flyoutTextHeaderEnemy.setFontSize(54);
|
||||||
|
this.flyoutTextHeaderEnemy.setAlign("right");
|
||||||
|
this.flyoutTextHeaderEnemy.setOrigin(1, 0);
|
||||||
|
|
||||||
|
this.flyoutContainer.add(this.flyoutTextHeaderEnemy);
|
||||||
|
|
||||||
|
this.flyoutTextPlayer = addTextObject(this.scene, 6, 13, "", TextStyle.BATTLE_INFO);
|
||||||
|
this.flyoutTextPlayer.setLineSpacing(-1);
|
||||||
|
this.flyoutTextPlayer.setFontSize(48);
|
||||||
|
this.flyoutTextPlayer.setAlign("left");
|
||||||
|
this.flyoutTextPlayer.setOrigin(0, 0);
|
||||||
|
|
||||||
|
this.flyoutContainer.add(this.flyoutTextPlayer);
|
||||||
|
|
||||||
|
this.flyoutTextField = addTextObject(this.scene, this.flyoutWidth / 2, 13, "", TextStyle.BATTLE_INFO);
|
||||||
|
this.flyoutTextField.setLineSpacing(-1);
|
||||||
|
this.flyoutTextField.setFontSize(48);
|
||||||
|
this.flyoutTextField.setAlign("center");
|
||||||
|
this.flyoutTextField.setOrigin(0.5, 0);
|
||||||
|
|
||||||
|
this.flyoutContainer.add(this.flyoutTextField);
|
||||||
|
|
||||||
|
this.flyoutTextEnemy = addTextObject(this.scene, this.flyoutWidth - 6, 13, "", TextStyle.BATTLE_INFO);
|
||||||
|
this.flyoutTextEnemy.setLineSpacing(-1);
|
||||||
|
this.flyoutTextEnemy.setFontSize(48);
|
||||||
|
this.flyoutTextEnemy.setAlign("right");
|
||||||
|
this.flyoutTextEnemy.setOrigin(1, 0);
|
||||||
|
|
||||||
|
this.flyoutContainer.add(this.flyoutTextEnemy);
|
||||||
|
|
||||||
|
this.name = "Fight Flyout";
|
||||||
|
this.flyoutParent.name = "Fight Flyout Parent";
|
||||||
|
|
||||||
|
// Subscribes to required events available on game start
|
||||||
|
this.battleScene.eventTarget.addEventListener(BattleSceneEventType.NEW_ARENA, this.onNewArenaEvent);
|
||||||
|
this.battleScene.eventTarget.addEventListener(BattleSceneEventType.TURN_INIT, this.onTurnInitEvent);
|
||||||
|
this.battleScene.eventTarget.addEventListener(BattleSceneEventType.TURN_END, this.onTurnEndEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private setTimeOfDayIcon() {
|
||||||
|
this.timeOfDayIcon.setTexture(TimeOfDay[this.battleScene.arena.getTimeOfDay()].toLowerCase() + "_icon");
|
||||||
|
}
|
||||||
|
|
||||||
|
private onTurnInit(event: Event) {
|
||||||
|
this.setTimeOfDayIcon();
|
||||||
|
}
|
||||||
|
|
||||||
|
private onNewArena(event: Event) {
|
||||||
|
this.fieldEffectInfo.length = 0;
|
||||||
|
|
||||||
|
// Subscribes to required events available on battle start
|
||||||
|
this.battleScene.arena.eventTarget.addEventListener(ArenaEventType.WEATHER_CHANGED, this.onFieldEffectChangedEvent);
|
||||||
|
this.battleScene.arena.eventTarget.addEventListener(ArenaEventType.TERRAIN_CHANGED, this.onFieldEffectChangedEvent);
|
||||||
|
this.battleScene.arena.eventTarget.addEventListener(ArenaEventType.TAG_ADDED, this.onFieldEffectChangedEvent);
|
||||||
|
this.battleScene.arena.eventTarget.addEventListener(ArenaEventType.TAG_REMOVED, this.onFieldEffectChangedEvent);
|
||||||
|
|
||||||
|
this.setTimeOfDayIcon();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a string to title case
|
||||||
|
* @param unformattedText Text to be formatted
|
||||||
|
* @returns the formatted string
|
||||||
|
*/
|
||||||
|
private formatText(unformattedText: string): string {
|
||||||
|
const text = unformattedText.split("_");
|
||||||
|
for (let i = 0; i < text.length; i++) {
|
||||||
|
text[i] = text[i].charAt(0).toUpperCase() + text[i].substring(1).toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
return text.join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Clears out the current string stored in all arena effect texts */
|
||||||
|
private clearText() {
|
||||||
|
this.flyoutTextPlayer.text = "";
|
||||||
|
this.flyoutTextField.text = "";
|
||||||
|
this.flyoutTextEnemy.text = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Parses through all set Arena Effects and puts them into the proper {@linkcode Phaser.GameObjects.Text} object */
|
||||||
|
private updateFieldText() {
|
||||||
|
this.clearText();
|
||||||
|
|
||||||
|
this.fieldEffectInfo.sort((infoA, infoB) => infoA.duration - infoB.duration);
|
||||||
|
|
||||||
|
for (let i = 0; i < this.fieldEffectInfo.length; i++) {
|
||||||
|
const fieldEffectInfo = this.fieldEffectInfo[i];
|
||||||
|
|
||||||
|
// Creates a proxy object to decide which text object needs to be updated
|
||||||
|
let textObject: Phaser.GameObjects.Text;
|
||||||
|
switch (fieldEffectInfo.type) {
|
||||||
|
case ArenaEffectType.PLAYER:
|
||||||
|
textObject = this.flyoutTextPlayer;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ArenaEffectType.WEATHER:
|
||||||
|
case ArenaEffectType.TERRAIN:
|
||||||
|
case ArenaEffectType.FIELD:
|
||||||
|
textObject = this.flyoutTextField;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ArenaEffectType.ENEMY:
|
||||||
|
textObject = this.flyoutTextEnemy;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
textObject.text += this.formatText(fieldEffectInfo.name);
|
||||||
|
if (fieldEffectInfo.type === ArenaEffectType.TERRAIN) {
|
||||||
|
textObject.text += " Terrain"; // Adds 'Terrain' since the enum does not contain it
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fieldEffectInfo.maxDuration !== 0) {
|
||||||
|
textObject.text += " " + fieldEffectInfo.duration + "/" + fieldEffectInfo.maxDuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
textObject.text += "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the {@linkcode Event} being passed and updates the state of the fieldEffectInfo array
|
||||||
|
* @param event {@linkcode Event} being sent
|
||||||
|
*/
|
||||||
|
private onFieldEffectChanged(event: Event) {
|
||||||
|
const arenaEffectChangedEvent = event as ArenaEvent;
|
||||||
|
if (!arenaEffectChangedEvent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let foundIndex: number;
|
||||||
|
switch (arenaEffectChangedEvent.constructor) {
|
||||||
|
case TagAddedEvent:
|
||||||
|
const tagAddedEvent = arenaEffectChangedEvent as TagAddedEvent;
|
||||||
|
this.fieldEffectInfo.push({
|
||||||
|
name: ArenaTagType[tagAddedEvent.arenaTagType],
|
||||||
|
type: tagAddedEvent.arenaTagSide === ArenaTagSide.BOTH
|
||||||
|
? ArenaEffectType.FIELD
|
||||||
|
: tagAddedEvent.arenaTagSide === ArenaTagSide.PLAYER
|
||||||
|
? ArenaEffectType.PLAYER
|
||||||
|
: ArenaEffectType.ENEMY,
|
||||||
|
maxDuration: tagAddedEvent.duration,
|
||||||
|
duration: tagAddedEvent.duration});
|
||||||
|
break;
|
||||||
|
case TagRemovedEvent:
|
||||||
|
const tagRemovedEvent = arenaEffectChangedEvent as TagRemovedEvent;
|
||||||
|
foundIndex = this.fieldEffectInfo.findIndex(info => info.name === ArenaTagType[tagRemovedEvent.arenaTagType]);
|
||||||
|
if (foundIndex !== -1) { // If the tag was being tracked, remove it
|
||||||
|
this.fieldEffectInfo.splice(foundIndex, 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WeatherChangedEvent:
|
||||||
|
case TerrainChangedEvent:
|
||||||
|
const fieldEffectChangedEvent = arenaEffectChangedEvent as WeatherChangedEvent | TerrainChangedEvent;
|
||||||
|
|
||||||
|
// Stores the old Weather/Terrain name in case it's in the array already
|
||||||
|
const oldName =
|
||||||
|
fieldEffectChangedEvent instanceof WeatherChangedEvent
|
||||||
|
? WeatherType[fieldEffectChangedEvent.oldWeatherType]
|
||||||
|
: TerrainType[fieldEffectChangedEvent.oldTerrainType];
|
||||||
|
// Stores the new Weather/Terrain info
|
||||||
|
const newInfo = {
|
||||||
|
name:
|
||||||
|
fieldEffectChangedEvent instanceof WeatherChangedEvent
|
||||||
|
? WeatherType[fieldEffectChangedEvent.newWeatherType]
|
||||||
|
: TerrainType[fieldEffectChangedEvent.newTerrainType],
|
||||||
|
type: fieldEffectChangedEvent instanceof WeatherChangedEvent
|
||||||
|
? ArenaEffectType.WEATHER
|
||||||
|
: ArenaEffectType.TERRAIN,
|
||||||
|
maxDuration: fieldEffectChangedEvent.duration,
|
||||||
|
duration: fieldEffectChangedEvent.duration};
|
||||||
|
|
||||||
|
foundIndex = this.fieldEffectInfo.findIndex(info => [newInfo.name, oldName].includes(info.name));
|
||||||
|
if (foundIndex === -1) {
|
||||||
|
if (newInfo.name !== undefined) {
|
||||||
|
this.fieldEffectInfo.push(newInfo); // Adds the info to the array if it doesn't already exist and is defined
|
||||||
|
}
|
||||||
|
} else if (!newInfo.name) {
|
||||||
|
this.fieldEffectInfo.splice(foundIndex, 1); // Removes the old info if the new one is undefined
|
||||||
|
} else {
|
||||||
|
this.fieldEffectInfo[foundIndex] = newInfo; // Otherwise, replace the old info
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateFieldText();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates through the fieldEffectInfo array and decrements the duration of each item
|
||||||
|
* @param event {@linkcode Event} being sent
|
||||||
|
*/
|
||||||
|
private onTurnEnd(event: Event) {
|
||||||
|
const turnEndEvent = event as TurnEndEvent;
|
||||||
|
if (!turnEndEvent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fieldEffectInfo: ArenaEffectInfo[] = [];
|
||||||
|
this.fieldEffectInfo.forEach(i => fieldEffectInfo.push(i));
|
||||||
|
|
||||||
|
for (let i = 0; i < fieldEffectInfo.length; i++) {
|
||||||
|
const info = fieldEffectInfo[i];
|
||||||
|
|
||||||
|
if (info.maxDuration === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
--info.duration;
|
||||||
|
if (info.duration <= 0) { // Removes the item if the duration has expired
|
||||||
|
this.fieldEffectInfo.splice(this.fieldEffectInfo.indexOf(info), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateFieldText();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animates the flyout to either show or hide it by applying a fade and translation
|
||||||
|
* @param visible Should the flyout be shown?
|
||||||
|
*/
|
||||||
|
toggleFlyout(visible: boolean): void {
|
||||||
|
this.scene.tweens.add({
|
||||||
|
targets: this.flyoutParent,
|
||||||
|
x: visible ? this.anchorX : this.anchorX - this.translationX,
|
||||||
|
duration: Utils.fixedInt(125),
|
||||||
|
ease: "Sine.easeInOut",
|
||||||
|
alpha: visible ? 1 : 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy(fromScene?: boolean): void {
|
||||||
|
this.battleScene.eventTarget.removeEventListener(BattleSceneEventType.NEW_ARENA, this.onNewArenaEvent);
|
||||||
|
this.battleScene.eventTarget.removeEventListener(BattleSceneEventType.TURN_END, this.onTurnEndEvent);
|
||||||
|
|
||||||
|
this.battleScene.arena.eventTarget.removeEventListener(ArenaEventType.WEATHER_CHANGED, this.onFieldEffectChangedEvent);
|
||||||
|
this.battleScene.arena.eventTarget.removeEventListener(ArenaEventType.TERRAIN_CHANGED, this.onFieldEffectChangedEvent);
|
||||||
|
this.battleScene.arena.eventTarget.removeEventListener(ArenaEventType.TAG_ADDED, this.onFieldEffectChangedEvent);
|
||||||
|
this.battleScene.arena.eventTarget.removeEventListener(ArenaEventType.TAG_REMOVED, this.onFieldEffectChangedEvent);
|
||||||
|
|
||||||
|
super.destroy();
|
||||||
|
}
|
||||||
|
}
|
@ -378,6 +378,20 @@ export default class EggGachaUiHandler extends MessageUiHandler {
|
|||||||
} else if (pullCount >= 10 && !tiers.filter(t => t >= EggTier.GREAT).length) {
|
} else if (pullCount >= 10 && !tiers.filter(t => t >= EggTier.GREAT).length) {
|
||||||
tiers[Utils.randInt(tiers.length)] = EggTier.GREAT;
|
tiers[Utils.randInt(tiers.length)] = EggTier.GREAT;
|
||||||
}
|
}
|
||||||
|
for (let i = 0; i < pullCount; i++) {
|
||||||
|
this.scene.gameData.eggPity[EggTier.GREAT] += 1;
|
||||||
|
this.scene.gameData.eggPity[EggTier.ULTRA] += 1;
|
||||||
|
this.scene.gameData.eggPity[EggTier.MASTER] += 1 + tierValueOffset;
|
||||||
|
// These numbers are roughly the 80% mark. That is, 80% of the time you'll get an egg before this gets triggered.
|
||||||
|
if (this.scene.gameData.eggPity[EggTier.MASTER] >= 412 && tiers[i] === EggTier.COMMON) {
|
||||||
|
tiers[i] = EggTier.MASTER;
|
||||||
|
} else if (this.scene.gameData.eggPity[EggTier.ULTRA] >= 59 && tiers[i] === EggTier.COMMON) {
|
||||||
|
tiers[i] = EggTier.ULTRA;
|
||||||
|
} else if (this.scene.gameData.eggPity[EggTier.GREAT] >= 9 && tiers[i] === EggTier.COMMON) {
|
||||||
|
tiers[i] = EggTier.GREAT;
|
||||||
|
}
|
||||||
|
this.scene.gameData.eggPity[tiers[i]] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
const timestamp = new Date().getTime();
|
const timestamp = new Date().getTime();
|
||||||
|
|
||||||
|
660
src/ui/settings/abstract-control-settings-ui-handler.ts
Normal file
@ -0,0 +1,660 @@
|
|||||||
|
import UiHandler from "../ui-handler";
|
||||||
|
import BattleScene from "../../battle-scene";
|
||||||
|
import {Mode} from "../ui";
|
||||||
|
import {InterfaceConfig} from "../../inputs-controller";
|
||||||
|
import {addWindow} from "../ui-theme";
|
||||||
|
import {addTextObject, TextStyle} from "../text";
|
||||||
|
import {Button} from "../../enums/buttons";
|
||||||
|
import {getIconWithSettingName} from "#app/configs/inputs/configHandler";
|
||||||
|
import NavigationMenu, {NavigationManager} from "#app/ui/settings/navigationMenu";
|
||||||
|
import { Device } from "#app/enums/devices.js";
|
||||||
|
|
||||||
|
export interface InputsIcons {
|
||||||
|
[key: string]: Phaser.GameObjects.Sprite;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LayoutConfig {
|
||||||
|
optionsContainer: Phaser.GameObjects.Container;
|
||||||
|
inputsIcons: InputsIcons;
|
||||||
|
settingLabels: Phaser.GameObjects.Text[];
|
||||||
|
optionValueLabels: Phaser.GameObjects.Text[][];
|
||||||
|
optionCursors: integer[];
|
||||||
|
keys: string[];
|
||||||
|
bindingSettings: Array<String>;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Abstract class for handling UI elements related to control settings.
|
||||||
|
*/
|
||||||
|
export default abstract class AbstractControlSettingsUiHandler extends UiHandler {
|
||||||
|
protected settingsContainer: Phaser.GameObjects.Container;
|
||||||
|
protected optionsContainer: Phaser.GameObjects.Container;
|
||||||
|
protected navigationContainer: NavigationMenu;
|
||||||
|
|
||||||
|
protected scrollCursor: integer;
|
||||||
|
protected optionCursors: integer[];
|
||||||
|
protected cursorObj: Phaser.GameObjects.NineSlice;
|
||||||
|
|
||||||
|
protected optionsBg: Phaser.GameObjects.NineSlice;
|
||||||
|
protected actionsBg: Phaser.GameObjects.NineSlice;
|
||||||
|
|
||||||
|
protected settingLabels: Phaser.GameObjects.Text[];
|
||||||
|
protected optionValueLabels: Phaser.GameObjects.Text[][];
|
||||||
|
|
||||||
|
// layout will contain the 3 Gamepad tab for each config - dualshock, xbox, snes
|
||||||
|
protected layout: Map<string, LayoutConfig> = new Map<string, LayoutConfig>();
|
||||||
|
// Will contain the input icons from the selected layout
|
||||||
|
protected inputsIcons: InputsIcons;
|
||||||
|
protected navigationIcons: InputsIcons;
|
||||||
|
// list all the setting keys used in the selected layout (because dualshock has more buttons than xbox)
|
||||||
|
protected keys: Array<String>;
|
||||||
|
|
||||||
|
// Store the specific settings related to key bindings for the current gamepad configuration.
|
||||||
|
protected bindingSettings: Array<String>;
|
||||||
|
|
||||||
|
protected setting;
|
||||||
|
protected settingBlacklisted;
|
||||||
|
protected settingDeviceDefaults;
|
||||||
|
protected settingDeviceOptions;
|
||||||
|
protected configs;
|
||||||
|
protected commonSettingsCount;
|
||||||
|
protected textureOverride;
|
||||||
|
protected titleSelected;
|
||||||
|
protected localStoragePropertyName;
|
||||||
|
protected rowsToDisplay: number;
|
||||||
|
protected device: Device;
|
||||||
|
|
||||||
|
abstract saveSettingToLocalStorage(setting, cursor): void;
|
||||||
|
abstract setSetting(scene: BattleScene, setting, value: integer): boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for the AbstractSettingsUiHandler.
|
||||||
|
*
|
||||||
|
* @param scene - The BattleScene instance.
|
||||||
|
* @param mode - The UI mode.
|
||||||
|
*/
|
||||||
|
constructor(scene: BattleScene, mode?: Mode) {
|
||||||
|
super(scene, mode);
|
||||||
|
this.rowsToDisplay = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
getLocalStorageSetting(): object {
|
||||||
|
// Retrieve the settings from local storage or use an empty object if none exist.
|
||||||
|
const settings: object = localStorage.hasOwnProperty(this.localStoragePropertyName) ? JSON.parse(localStorage.getItem(this.localStoragePropertyName)) : {};
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup UI elements.
|
||||||
|
*/
|
||||||
|
setup() {
|
||||||
|
const ui = this.getUi();
|
||||||
|
this.navigationIcons = {};
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
this.navigationContainer = new NavigationMenu(this.scene, 0, 0);
|
||||||
|
|
||||||
|
this.optionsBg = addWindow(this.scene, 0, this.navigationContainer.height, (this.scene.game.canvas.width / 6) - 2, (this.scene.game.canvas.height / 6) - 16 - this.navigationContainer.height - 2);
|
||||||
|
this.optionsBg.setOrigin(0, 0);
|
||||||
|
|
||||||
|
|
||||||
|
this.actionsBg = addWindow(this.scene, 0, (this.scene.game.canvas.height / 6) - this.navigationContainer.height, (this.scene.game.canvas.width / 6) - 2, 22);
|
||||||
|
this.actionsBg.setOrigin(0, 0);
|
||||||
|
|
||||||
|
const iconAction = this.scene.add.sprite(0, 0, "keyboard");
|
||||||
|
iconAction.setOrigin(0, -0.1);
|
||||||
|
iconAction.setPositionRelative(this.actionsBg, this.navigationContainer.width - 32, 4);
|
||||||
|
this.navigationIcons["BUTTON_ACTION"] = iconAction;
|
||||||
|
|
||||||
|
const actionText = addTextObject(this.scene, 0, 0, "Action", TextStyle.SETTINGS_LABEL);
|
||||||
|
actionText.setOrigin(0, 0.15);
|
||||||
|
actionText.setPositionRelative(iconAction, -actionText.width/6-2, 0);
|
||||||
|
|
||||||
|
const iconCancel = this.scene.add.sprite(0, 0, "keyboard");
|
||||||
|
iconCancel.setOrigin(0, -0.1);
|
||||||
|
iconCancel.setPositionRelative(this.actionsBg, this.navigationContainer.width - 100, 4);
|
||||||
|
this.navigationIcons["BUTTON_CANCEL"] = iconCancel;
|
||||||
|
|
||||||
|
const cancelText = addTextObject(this.scene, 0, 0, "Cancel", TextStyle.SETTINGS_LABEL);
|
||||||
|
cancelText.setOrigin(0, 0.15);
|
||||||
|
cancelText.setPositionRelative(iconCancel, -cancelText.width/6-2, 0);
|
||||||
|
|
||||||
|
const iconReset = this.scene.add.sprite(0, 0, "keyboard");
|
||||||
|
iconReset.setOrigin(0, -0.1);
|
||||||
|
iconReset.setPositionRelative(this.actionsBg, this.navigationContainer.width - 180, 4);
|
||||||
|
this.navigationIcons["BUTTON_HOME"] = iconReset;
|
||||||
|
|
||||||
|
const resetText = addTextObject(this.scene, 0, 0, "Reset all", TextStyle.SETTINGS_LABEL);
|
||||||
|
resetText.setOrigin(0, 0.15);
|
||||||
|
resetText.setPositionRelative(iconReset, -resetText.width/6-2, 0);
|
||||||
|
|
||||||
|
this.settingsContainer.add(this.optionsBg);
|
||||||
|
this.settingsContainer.add(this.actionsBg);
|
||||||
|
this.settingsContainer.add(this.navigationContainer);
|
||||||
|
this.settingsContainer.add(iconAction);
|
||||||
|
this.settingsContainer.add(iconCancel);
|
||||||
|
this.settingsContainer.add(iconReset);
|
||||||
|
this.settingsContainer.add(actionText);
|
||||||
|
this.settingsContainer.add(cancelText);
|
||||||
|
this.settingsContainer.add(resetText);
|
||||||
|
|
||||||
|
/// Initialize a new configuration "screen" for each type of gamepad.
|
||||||
|
for (const config of this.configs) {
|
||||||
|
// Create a map to store layout settings based on the pad type.
|
||||||
|
this.layout[config.padType] = new Map();
|
||||||
|
// Create a container for gamepad options in the scene, initially hidden.
|
||||||
|
|
||||||
|
const optionsContainer = this.scene.add.container(0, 0);
|
||||||
|
optionsContainer.setVisible(false);
|
||||||
|
|
||||||
|
// Gather all binding settings from the configuration.
|
||||||
|
const bindingSettings = Object.keys(config.settings);
|
||||||
|
|
||||||
|
// Array to hold labels for different settings such as 'Controller', 'Gamepad Support', etc.
|
||||||
|
const settingLabels: Phaser.GameObjects.Text[] = [];
|
||||||
|
|
||||||
|
// Array to hold options for each setting, e.g., 'Auto', 'Disabled'.
|
||||||
|
const optionValueLabels: Phaser.GameObjects.GameObject[][] = [];
|
||||||
|
|
||||||
|
// Object to store sprites for each button configuration.
|
||||||
|
const inputsIcons: InputsIcons = {};
|
||||||
|
|
||||||
|
// Fetch common setting keys such as 'Controller' and 'Gamepad Support' from gamepad settings.
|
||||||
|
const commonSettingKeys = Object.keys(this.setting).slice(0, this.commonSettingsCount).map(key => this.setting[key]);
|
||||||
|
// Combine common and specific bindings into a single array.
|
||||||
|
const specificBindingKeys = [...commonSettingKeys, ...Object.keys(config.settings)];
|
||||||
|
// Fetch default values for these settings and prepare to highlight selected options.
|
||||||
|
const optionCursors = Object.values(Object.keys(this.settingDeviceDefaults).filter(s => specificBindingKeys.includes(s)).map(k => this.settingDeviceDefaults[k]));
|
||||||
|
// Filter out settings that are not relevant to the current gamepad configuration.
|
||||||
|
const settingFiltered = Object.keys(this.setting).filter(_key => specificBindingKeys.includes(this.setting[_key]));
|
||||||
|
// Loop through the filtered settings to manage display and options.
|
||||||
|
|
||||||
|
settingFiltered.forEach((setting, s) => {
|
||||||
|
// Convert the setting key from format 'Key_Name' to 'Key name' for display.
|
||||||
|
const settingName = setting.replace(/\_/g, " ");
|
||||||
|
|
||||||
|
// Create and add a text object for the setting name to the scene.
|
||||||
|
const isLock = this.settingBlacklisted.includes(this.setting[setting]);
|
||||||
|
const labelStyle = isLock ? TextStyle.SETTINGS_LOCKED : TextStyle.SETTINGS_LABEL;
|
||||||
|
settingLabels[s] = addTextObject(this.scene, 8, 28 + s * 16, settingName, labelStyle);
|
||||||
|
settingLabels[s].setOrigin(0, 0);
|
||||||
|
optionsContainer.add(settingLabels[s]);
|
||||||
|
|
||||||
|
// Initialize an array to store the option labels for this setting.
|
||||||
|
const valueLabels: Phaser.GameObjects.GameObject[] = [];
|
||||||
|
|
||||||
|
// Process each option for the current setting.
|
||||||
|
for (const [o, option] of this.settingDeviceOptions[this.setting[setting]].entries()) {
|
||||||
|
// Check if the current setting is for binding keys.
|
||||||
|
if (bindingSettings.includes(this.setting[setting])) {
|
||||||
|
// Create a label for non-null options, typically indicating actionable options like 'change'.
|
||||||
|
if (o) {
|
||||||
|
const valueLabel = addTextObject(this.scene, 0, 0, isLock ? "" : option, TextStyle.WINDOW);
|
||||||
|
valueLabel.setOrigin(0, 0);
|
||||||
|
optionsContainer.add(valueLabel);
|
||||||
|
valueLabels.push(valueLabel);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// For null options, add an icon for the key.
|
||||||
|
const icon = this.scene.add.sprite(0, 0, this.textureOverride ? this.textureOverride : config.padType);
|
||||||
|
icon.setOrigin(0, -0.15);
|
||||||
|
inputsIcons[this.setting[setting]] = icon;
|
||||||
|
optionsContainer.add(icon);
|
||||||
|
valueLabels.push(icon);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// For regular settings like 'Gamepad support', create a label and determine if it is selected.
|
||||||
|
const valueLabel = addTextObject(this.scene, 0, 0, option, this.settingDeviceDefaults[this.setting[setting]] === o ? TextStyle.SETTINGS_SELECTED : TextStyle.WINDOW);
|
||||||
|
valueLabel.setOrigin(0, 0);
|
||||||
|
|
||||||
|
optionsContainer.add(valueLabel);
|
||||||
|
|
||||||
|
//if a setting has 2 options, valueLabels will be an array of 2 elements
|
||||||
|
valueLabels.push(valueLabel);
|
||||||
|
}
|
||||||
|
// Collect all option labels for this setting into the main array.
|
||||||
|
optionValueLabels.push(valueLabels);
|
||||||
|
|
||||||
|
// Calculate the total width of all option labels within a specific setting
|
||||||
|
// This is achieved by summing the width of each option label
|
||||||
|
const totalWidth = optionValueLabels[s].map((o) => (o as Phaser.GameObjects.Text).width).reduce((total, width) => total += width, 0);
|
||||||
|
|
||||||
|
// Define the minimum width for a label, ensuring it's at least 78 pixels wide or the width of the setting label plus some padding
|
||||||
|
const labelWidth = Math.max(130, settingLabels[s].displayWidth + 8);
|
||||||
|
|
||||||
|
// Calculate the total available space for placing option labels next to their setting label
|
||||||
|
// We reserve space for the setting label and then distribute the remaining space evenly
|
||||||
|
const totalSpace = (300 - labelWidth) - totalWidth / 6;
|
||||||
|
// Calculate the spacing between options based on the available space divided by the number of gaps between labels
|
||||||
|
const optionSpacing = Math.floor(totalSpace / (optionValueLabels[s].length - 1));
|
||||||
|
|
||||||
|
// Initialize xOffset to zero, which will be used to position each option label horizontally
|
||||||
|
let xOffset = 0;
|
||||||
|
|
||||||
|
// Start positioning each option label one by one
|
||||||
|
for (const value of optionValueLabels[s]) {
|
||||||
|
// Set the option label's position right next to the setting label, adjusted by xOffset
|
||||||
|
(value as Phaser.GameObjects.Text).setPositionRelative(settingLabels[s], labelWidth + xOffset, 0);
|
||||||
|
// Move the xOffset to the right for the next label, ensuring each label is spaced evenly
|
||||||
|
xOffset += (value as Phaser.GameObjects.Text).width / 6 + optionSpacing;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assigning the newly created components to the layout map under the specific gamepad type.
|
||||||
|
this.layout[config.padType].optionsContainer = optionsContainer; // Container for this pad's options.
|
||||||
|
this.layout[config.padType].inputsIcons = inputsIcons; // Icons for each input specific to this pad.
|
||||||
|
this.layout[config.padType].settingLabels = settingLabels; // Text labels for each setting available on this pad.
|
||||||
|
this.layout[config.padType].optionValueLabels = optionValueLabels; // Labels for values corresponding to each setting.
|
||||||
|
this.layout[config.padType].optionCursors = optionCursors; // Cursors to navigate through the options.
|
||||||
|
this.layout[config.padType].keys = specificBindingKeys; // Keys that identify each setting specifically bound to this pad.
|
||||||
|
this.layout[config.padType].bindingSettings = bindingSettings; // Settings that define how the keys are bound.
|
||||||
|
|
||||||
|
// Add the options container to the overall settings container to be displayed in the UI.
|
||||||
|
this.settingsContainer.add(optionsContainer);
|
||||||
|
}
|
||||||
|
// Add the settings container to the UI.
|
||||||
|
ui.add(this.settingsContainer);
|
||||||
|
|
||||||
|
// Initially hide the settings container until needed (e.g., when a gamepad is connected or a button is pressed).
|
||||||
|
this.settingsContainer.setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the active configuration.
|
||||||
|
*
|
||||||
|
* @returns The active configuration for current device
|
||||||
|
*/
|
||||||
|
getActiveConfig(): InterfaceConfig {
|
||||||
|
return this.scene.inputController.getActiveConfig(this.device);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the bindings for the current active device configuration.
|
||||||
|
*/
|
||||||
|
updateBindings(): void {
|
||||||
|
// Hide the options container for all layouts to reset the UI visibility.
|
||||||
|
Object.keys(this.layout).forEach((key) => this.layout[key].optionsContainer.setVisible(false));
|
||||||
|
// Fetch the active gamepad configuration from the input controller.
|
||||||
|
const activeConfig = this.getActiveConfig();
|
||||||
|
|
||||||
|
// Set the UI layout for the active configuration. If unsuccessful, exit the function early.
|
||||||
|
if (!this.setLayout(activeConfig)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve the gamepad settings from local storage or use an empty object if none exist.
|
||||||
|
const settings: object = this.getLocalStorageSetting();
|
||||||
|
|
||||||
|
// Update the cursor for each key based on the stored settings or default cursors.
|
||||||
|
this.keys.forEach((key, index) => {
|
||||||
|
this.setOptionCursor(index, settings.hasOwnProperty(key as string) ? settings[key as string] : this.optionCursors[index]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// If the active configuration has no custom bindings set, exit the function early.
|
||||||
|
// by default, if custom does not exists, a default is assigned to it
|
||||||
|
// it only means the gamepad is not yet initalized
|
||||||
|
if (!activeConfig.custom) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For each element in the binding settings, update the icon according to the current assignment.
|
||||||
|
for (const elm of this.bindingSettings) {
|
||||||
|
const icon = getIconWithSettingName(activeConfig, elm);
|
||||||
|
if (icon) {
|
||||||
|
this.inputsIcons[elm as string].setFrame(icon);
|
||||||
|
this.inputsIcons[elm as string].alpha = 1;
|
||||||
|
} else {
|
||||||
|
this.inputsIcons[elm as string].alpha = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the cursor and scroll cursor to their initial positions.
|
||||||
|
this.setCursor(this.cursor);
|
||||||
|
this.setScrollCursor(this.scrollCursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateNavigationDisplay() {
|
||||||
|
const specialIcons = {
|
||||||
|
"BUTTON_HOME": "HOME.png",
|
||||||
|
"BUTTON_DELETE": "DEL.png",
|
||||||
|
};
|
||||||
|
for (const settingName of Object.keys(this.navigationIcons)) {
|
||||||
|
if (Object.keys(specialIcons).includes(settingName)) {
|
||||||
|
this.navigationIcons[settingName].setTexture("keyboard");
|
||||||
|
this.navigationIcons[settingName].setFrame(specialIcons[settingName]);
|
||||||
|
this.navigationIcons[settingName].alpha = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const icon = this.scene.inputController?.getIconForLatestInputRecorded(settingName);
|
||||||
|
if (icon) {
|
||||||
|
const type = this.scene.inputController?.getLastSourceType();
|
||||||
|
this.navigationIcons[settingName].setTexture(type);
|
||||||
|
this.navigationIcons[settingName].setFrame(icon);
|
||||||
|
this.navigationIcons[settingName].alpha = 1;
|
||||||
|
} else {
|
||||||
|
this.navigationIcons[settingName].alpha = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the UI with the provided arguments.
|
||||||
|
*
|
||||||
|
* @param args - Arguments to be passed to the show method.
|
||||||
|
* @returns `true` if successful.
|
||||||
|
*/
|
||||||
|
show(args: any[]): boolean {
|
||||||
|
super.show(args);
|
||||||
|
|
||||||
|
this.updateNavigationDisplay();
|
||||||
|
NavigationManager.getInstance().updateIcons();
|
||||||
|
// Update the bindings for the current active gamepad configuration.
|
||||||
|
this.updateBindings();
|
||||||
|
|
||||||
|
// Make the settings container visible to the user.
|
||||||
|
this.settingsContainer.setVisible(true);
|
||||||
|
// Reset the scroll cursor to the top of the settings container.
|
||||||
|
this.resetScroll();
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// Hide any tooltips that might be visible before showing the settings container.
|
||||||
|
this.getUi().hideTooltip();
|
||||||
|
|
||||||
|
// Return true to indicate the UI was successfully shown.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the UI layout for the active device configuration.
|
||||||
|
*
|
||||||
|
* @param activeConfig - The active device configuration.
|
||||||
|
* @returns `true` if the layout was successfully applied, otherwise `false`.
|
||||||
|
*/
|
||||||
|
setLayout(activeConfig: InterfaceConfig): boolean {
|
||||||
|
// Check if there is no active configuration (e.g., no gamepad connected).
|
||||||
|
if (!activeConfig) {
|
||||||
|
// Retrieve the layout for when no gamepads are connected.
|
||||||
|
const layout = this.layout["noGamepads"];
|
||||||
|
// Make the options container visible to show message.
|
||||||
|
layout.optionsContainer.setVisible(true);
|
||||||
|
// Return false indicating the layout application was not successful due to lack of gamepad.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Extract the type of the gamepad from the active configuration.
|
||||||
|
const configType = activeConfig.padType;
|
||||||
|
|
||||||
|
// Retrieve the layout settings based on the type of the gamepad.
|
||||||
|
const layout = this.layout[configType];
|
||||||
|
// Update the main controller with configuration details from the selected layout.
|
||||||
|
this.keys = layout.keys;
|
||||||
|
this.optionsContainer = layout.optionsContainer;
|
||||||
|
this.optionsContainer.setVisible(true);
|
||||||
|
this.settingLabels = layout.settingLabels;
|
||||||
|
this.optionValueLabels = layout.optionValueLabels;
|
||||||
|
this.optionCursors = layout.optionCursors;
|
||||||
|
this.inputsIcons = layout.inputsIcons;
|
||||||
|
this.bindingSettings = layout.bindingSettings;
|
||||||
|
|
||||||
|
// Return true indicating the layout was successfully applied.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process the input for the given button.
|
||||||
|
*
|
||||||
|
* @param button - The button to process.
|
||||||
|
* @returns `true` if the input was processed successfully.
|
||||||
|
*/
|
||||||
|
processInput(button: Button): boolean {
|
||||||
|
const ui = this.getUi();
|
||||||
|
// Defines the maximum number of rows that can be displayed on the screen.
|
||||||
|
let success = false;
|
||||||
|
this.updateNavigationDisplay();
|
||||||
|
|
||||||
|
// Handle the input based on the button pressed.
|
||||||
|
if (button === Button.CANCEL) {
|
||||||
|
// Handle cancel button press, reverting UI mode to previous state.
|
||||||
|
success = true;
|
||||||
|
NavigationManager.getInstance().reset();
|
||||||
|
this.scene.ui.revertMode();
|
||||||
|
} else {
|
||||||
|
const cursor = this.cursor + this.scrollCursor; // Calculate the absolute cursor position.
|
||||||
|
const setting = this.setting[Object.keys(this.setting)[cursor]];
|
||||||
|
switch (button) {
|
||||||
|
case Button.ACTION:
|
||||||
|
if (!this.optionCursors || !this.optionValueLabels) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.settingBlacklisted.includes(setting) || !setting.includes("BUTTON_")) {
|
||||||
|
success = false;
|
||||||
|
} else {
|
||||||
|
success = this.setSetting(this.scene, setting, 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Button.UP: // Move up in the menu.
|
||||||
|
if (!this.optionValueLabels) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (cursor) { // If not at the top, move the cursor up.
|
||||||
|
if (this.cursor) {
|
||||||
|
success = this.setCursor(this.cursor - 1);
|
||||||
|
} else {// If at the top of the visible items, scroll up.
|
||||||
|
success = this.setScrollCursor(this.scrollCursor - 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// When at the top of the menu and pressing UP, move to the bottommost item.
|
||||||
|
// First, set the cursor to the last visible element, preparing for the scroll to the end.
|
||||||
|
const successA = this.setCursor(this.rowsToDisplay - 1);
|
||||||
|
// Then, adjust the scroll to display the bottommost elements of the menu.
|
||||||
|
const successB = this.setScrollCursor(this.optionValueLabels.length - this.rowsToDisplay);
|
||||||
|
success = successA && successB; // success is just there to play the little validation sound effect
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Button.DOWN: // Move down in the menu.
|
||||||
|
if (!this.optionValueLabels) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (cursor < this.optionValueLabels.length - 1) {
|
||||||
|
if (this.cursor < this.rowsToDisplay - 1) {
|
||||||
|
success = this.setCursor(this.cursor + 1);
|
||||||
|
} else if (this.scrollCursor < this.optionValueLabels.length - this.rowsToDisplay) {
|
||||||
|
success = this.setScrollCursor(this.scrollCursor + 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// When at the bottom of the menu and pressing DOWN, move to the topmost item.
|
||||||
|
// First, set the cursor to the first visible element, resetting the scroll to the top.
|
||||||
|
const successA = this.setCursor(0);
|
||||||
|
// Then, reset the scroll to start from the first element of the menu.
|
||||||
|
const successB = this.setScrollCursor(0);
|
||||||
|
success = successA && successB; // Indicates a successful cursor and scroll adjustment.
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Button.LEFT: // Move selection left within the current option set.
|
||||||
|
if (!this.optionCursors || !this.optionValueLabels) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.settingBlacklisted.includes(setting) || setting.includes("BUTTON_")) {
|
||||||
|
success = false;
|
||||||
|
} else if (this.optionCursors[cursor]) {
|
||||||
|
success = this.setOptionCursor(cursor, this.optionCursors[cursor] - 1, true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Button.RIGHT: // Move selection right within the current option set.
|
||||||
|
if (!this.optionCursors || !this.optionValueLabels) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.settingBlacklisted.includes(setting) || setting.includes("BUTTON_")) {
|
||||||
|
success = false;
|
||||||
|
} else if (this.optionCursors[cursor] < this.optionValueLabels[cursor].length - 1) {
|
||||||
|
success = this.setOptionCursor(cursor, this.optionCursors[cursor] + 1, true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Button.CYCLE_FORM:
|
||||||
|
case Button.CYCLE_SHINY:
|
||||||
|
success = this.navigationContainer.navigate(button);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a change occurred, play the selection sound.
|
||||||
|
if (success) {
|
||||||
|
ui.playSelect();
|
||||||
|
}
|
||||||
|
|
||||||
|
return success; // Return whether the input resulted in a successful action.
|
||||||
|
}
|
||||||
|
|
||||||
|
resetScroll() {
|
||||||
|
this.cursorObj?.destroy();
|
||||||
|
this.cursorObj = null;
|
||||||
|
this.cursor = null;
|
||||||
|
this.setCursor(0);
|
||||||
|
this.setScrollCursor(0);
|
||||||
|
this.updateSettingsScroll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the cursor to the specified position.
|
||||||
|
*
|
||||||
|
* @param cursor - The cursor position to set.
|
||||||
|
* @returns `true` if the cursor was set successfully.
|
||||||
|
*/
|
||||||
|
setCursor(cursor: integer): boolean {
|
||||||
|
const ret = super.setCursor(cursor);
|
||||||
|
// If the optionsContainer is not initialized, return the result from the parent class directly.
|
||||||
|
if (!this.optionsContainer) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the cursor object exists, if not, create it.
|
||||||
|
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); // Set the origin to the top-left corner.
|
||||||
|
this.optionsContainer.add(this.cursorObj); // Add the cursor to the options container.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the position of the cursor object relative to the options background based on the current cursor and scroll positions.
|
||||||
|
this.cursorObj.setPositionRelative(this.optionsBg, 4, 4 + (this.cursor + this.scrollCursor) * 16);
|
||||||
|
|
||||||
|
return ret; // Return the result from the parent class's setCursor method.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the scroll cursor to the specified position.
|
||||||
|
*
|
||||||
|
* @param scrollCursor - The scroll cursor position to set.
|
||||||
|
* @returns `true` if the scroll cursor was set successfully.
|
||||||
|
*/
|
||||||
|
setScrollCursor(scrollCursor: integer): boolean {
|
||||||
|
// Check if the new scroll position is the same as the current one; if so, do not update.
|
||||||
|
if (scrollCursor === this.scrollCursor) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the internal scroll cursor state
|
||||||
|
this.scrollCursor = scrollCursor;
|
||||||
|
|
||||||
|
// Apply the new scroll position to the settings UI.
|
||||||
|
this.updateSettingsScroll();
|
||||||
|
|
||||||
|
// Reset the cursor to its current position to adjust its visibility after scrolling.
|
||||||
|
this.setCursor(this.cursor);
|
||||||
|
|
||||||
|
return true; // Return true to indicate the scroll cursor was successfully updated.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the option cursor to the specified position.
|
||||||
|
*
|
||||||
|
* @param settingIndex - The index of the setting.
|
||||||
|
* @param cursor - The cursor position to set.
|
||||||
|
* @param save - Whether to save the setting to local storage.
|
||||||
|
* @returns `true` if the option cursor was set successfully.
|
||||||
|
*/
|
||||||
|
setOptionCursor(settingIndex: integer, cursor: integer, save?: boolean): boolean {
|
||||||
|
// Retrieve the specific setting using the settingIndex from the settingDevice enumeration.
|
||||||
|
const setting = this.setting[Object.keys(this.setting)[settingIndex]];
|
||||||
|
|
||||||
|
// Get the current cursor position for this setting.
|
||||||
|
const lastCursor = this.optionCursors[settingIndex];
|
||||||
|
|
||||||
|
// Check if the setting is not part of the bindings (i.e., it's a regular setting).
|
||||||
|
if (!this.bindingSettings.includes(setting) && !setting.includes("BUTTON_")) {
|
||||||
|
// Get the label of the last selected option and revert its color to the default.
|
||||||
|
const lastValueLabel = this.optionValueLabels[settingIndex][lastCursor];
|
||||||
|
lastValueLabel.setColor(this.getTextColor(TextStyle.WINDOW));
|
||||||
|
lastValueLabel.setShadowColor(this.getTextColor(TextStyle.WINDOW, true));
|
||||||
|
|
||||||
|
// Update the cursor for the setting to the new position.
|
||||||
|
this.optionCursors[settingIndex] = cursor;
|
||||||
|
|
||||||
|
// Change the color of the new selected option to indicate it's selected.
|
||||||
|
const newValueLabel = this.optionValueLabels[settingIndex][cursor];
|
||||||
|
newValueLabel.setColor(this.getTextColor(TextStyle.SETTINGS_SELECTED));
|
||||||
|
newValueLabel.setShadowColor(this.getTextColor(TextStyle.SETTINGS_SELECTED, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the save flag is set, save the setting to local storage
|
||||||
|
if (save) {
|
||||||
|
this.saveSettingToLocalStorage(setting, cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; // Return true to indicate the cursor was successfully updated.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the scroll position of the settings UI.
|
||||||
|
*/
|
||||||
|
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 + this.rowsToDisplay;
|
||||||
|
|
||||||
|
// Set the visibility of the setting label and its corresponding options.
|
||||||
|
this.settingLabels[s].setVisible(visible);
|
||||||
|
for (const option of this.optionValueLabels[s]) {
|
||||||
|
option.setVisible(visible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the UI elements and state.
|
||||||
|
*/
|
||||||
|
clear(): void {
|
||||||
|
super.clear();
|
||||||
|
|
||||||
|
// Hide the settings container to remove it from the view.
|
||||||
|
this.settingsContainer.setVisible(false);
|
||||||
|
|
||||||
|
// Remove the cursor from the UI.
|
||||||
|
this.eraseCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erase the cursor from the UI.
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,107 +1,74 @@
|
|||||||
import UiHandler from "../ui-handler";
|
|
||||||
import BattleScene from "../../battle-scene";
|
import BattleScene from "../../battle-scene";
|
||||||
import {Mode} from "../ui";
|
import { hasTouchscreen, isMobile } from "../../touch-controls";
|
||||||
import {InterfaceConfig} from "../../inputs-controller";
|
import { TextStyle, addTextObject } from "../text";
|
||||||
import {addWindow} from "../ui-theme";
|
import { Mode } from "../ui";
|
||||||
import {addTextObject, TextStyle} from "../text";
|
import UiHandler from "../ui-handler";
|
||||||
|
import { addWindow } from "../ui-theme";
|
||||||
import {Button} from "../../enums/buttons";
|
import {Button} from "../../enums/buttons";
|
||||||
import {getIconWithSettingName} from "#app/configs/inputs/configHandler";
|
import {InputsIcons} from "#app/ui/settings/abstract-control-settings-ui-handler.js";
|
||||||
import NavigationMenu, {NavigationManager} from "#app/ui/settings/navigationMenu";
|
import NavigationMenu, {NavigationManager} from "#app/ui/settings/navigationMenu";
|
||||||
|
import { Setting, SettingKeys } from "#app/system/settings/settings";
|
||||||
|
|
||||||
export interface InputsIcons {
|
|
||||||
[key: string]: Phaser.GameObjects.Sprite;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LayoutConfig {
|
|
||||||
optionsContainer: Phaser.GameObjects.Container;
|
|
||||||
inputsIcons: InputsIcons;
|
|
||||||
settingLabels: Phaser.GameObjects.Text[];
|
|
||||||
optionValueLabels: Phaser.GameObjects.Text[][];
|
|
||||||
optionCursors: integer[];
|
|
||||||
keys: string[];
|
|
||||||
bindingSettings: Array<String>;
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Abstract class for handling UI elements related to settings.
|
* Abstract class for handling UI elements related to settings.
|
||||||
*/
|
*/
|
||||||
export default abstract class AbstractSettingsUiUiHandler extends UiHandler {
|
export default class AbstractSettingsUiHandler extends UiHandler {
|
||||||
protected settingsContainer: Phaser.GameObjects.Container;
|
private settingsContainer: Phaser.GameObjects.Container;
|
||||||
protected optionsContainer: Phaser.GameObjects.Container;
|
private optionsContainer: Phaser.GameObjects.Container;
|
||||||
protected navigationContainer: NavigationMenu;
|
private navigationContainer: NavigationMenu;
|
||||||
|
|
||||||
protected scrollCursor: integer;
|
private scrollCursor: integer;
|
||||||
protected optionCursors: integer[];
|
|
||||||
protected cursorObj: Phaser.GameObjects.NineSlice;
|
|
||||||
|
|
||||||
protected optionsBg: Phaser.GameObjects.NineSlice;
|
private optionsBg: Phaser.GameObjects.NineSlice;
|
||||||
protected actionsBg: Phaser.GameObjects.NineSlice;
|
|
||||||
|
|
||||||
protected settingLabels: Phaser.GameObjects.Text[];
|
private optionCursors: integer[];
|
||||||
protected optionValueLabels: Phaser.GameObjects.Text[][];
|
|
||||||
|
private settingLabels: Phaser.GameObjects.Text[];
|
||||||
|
private optionValueLabels: Phaser.GameObjects.Text[][];
|
||||||
|
|
||||||
// layout will contain the 3 Gamepad tab for each config - dualshock, xbox, snes
|
|
||||||
protected layout: Map<string, LayoutConfig> = new Map<string, LayoutConfig>();
|
|
||||||
// Will contain the input icons from the selected layout
|
|
||||||
protected inputsIcons: InputsIcons;
|
|
||||||
protected navigationIcons: InputsIcons;
|
protected navigationIcons: InputsIcons;
|
||||||
// list all the setting keys used in the selected layout (because dualshock has more buttons than xbox)
|
|
||||||
protected keys: Array<String>;
|
|
||||||
|
|
||||||
// Store the specific settings related to key bindings for the current gamepad configuration.
|
private cursorObj: Phaser.GameObjects.NineSlice;
|
||||||
protected bindingSettings: Array<String>;
|
|
||||||
|
|
||||||
protected settingDevice;
|
private reloadSettings: Array<Setting>;
|
||||||
protected settingBlacklisted;
|
private reloadRequired: boolean;
|
||||||
protected settingDeviceDefaults;
|
private rowsToDisplay: number;
|
||||||
protected settingDeviceOptions;
|
|
||||||
protected configs;
|
|
||||||
protected commonSettingsCount;
|
|
||||||
protected textureOverride;
|
|
||||||
protected titleSelected;
|
|
||||||
protected localStoragePropertyName;
|
|
||||||
protected rowsToDisplay: number;
|
|
||||||
|
|
||||||
abstract getLocalStorageSetting(): object;
|
protected title: string;
|
||||||
abstract navigateMenuLeft(): boolean;
|
protected settings: Array<Setting>;
|
||||||
abstract navigateMenuRight(): boolean;
|
protected localStorageKey: string;
|
||||||
abstract saveSettingToLocalStorage(setting, cursor): void;
|
|
||||||
abstract getActiveConfig(): InterfaceConfig;
|
|
||||||
abstract setSetting(scene: BattleScene, setting, value: integer): boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor for the AbstractSettingsUiUiHandler.
|
|
||||||
*
|
|
||||||
* @param scene - The BattleScene instance.
|
|
||||||
* @param mode - The UI mode.
|
|
||||||
*/
|
|
||||||
constructor(scene: BattleScene, mode?: Mode) {
|
constructor(scene: BattleScene, mode?: Mode) {
|
||||||
super(scene, mode);
|
super(scene, mode);
|
||||||
|
|
||||||
|
this.reloadRequired = false;
|
||||||
this.rowsToDisplay = 8;
|
this.rowsToDisplay = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setup UI elements.
|
* Setup UI elements
|
||||||
*/
|
*/
|
||||||
setup() {
|
setup() {
|
||||||
const ui = this.getUi();
|
const ui = this.getUi();
|
||||||
this.navigationIcons = {};
|
|
||||||
|
|
||||||
this.settingsContainer = this.scene.add.container(1, -(this.scene.game.canvas.height / 6) + 1);
|
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);
|
this.settingsContainer.setInteractive(new Phaser.Geom.Rectangle(0, 0, this.scene.game.canvas.width / 6, this.scene.game.canvas.height / 6 - 20), Phaser.Geom.Rectangle.Contains);
|
||||||
|
|
||||||
|
this.navigationIcons = {};
|
||||||
|
|
||||||
this.navigationContainer = new NavigationMenu(this.scene, 0, 0);
|
this.navigationContainer = new NavigationMenu(this.scene, 0, 0);
|
||||||
|
|
||||||
this.optionsBg = addWindow(this.scene, 0, this.navigationContainer.height, (this.scene.game.canvas.width / 6) - 2, (this.scene.game.canvas.height / 6) - 16 - this.navigationContainer.height - 2);
|
this.optionsBg = addWindow(this.scene, 0, this.navigationContainer.height, (this.scene.game.canvas.width / 6) - 2, (this.scene.game.canvas.height / 6) - 16 - this.navigationContainer.height - 2);
|
||||||
this.optionsBg.setOrigin(0, 0);
|
this.optionsBg.setOrigin(0, 0);
|
||||||
|
|
||||||
|
const actionsBg = addWindow(this.scene, 0, (this.scene.game.canvas.height / 6) - this.navigationContainer.height, (this.scene.game.canvas.width / 6) - 2, 22);
|
||||||
this.actionsBg = addWindow(this.scene, 0, (this.scene.game.canvas.height / 6) - this.navigationContainer.height, (this.scene.game.canvas.width / 6) - 2, 22);
|
actionsBg.setOrigin(0, 0);
|
||||||
this.actionsBg.setOrigin(0, 0);
|
|
||||||
|
|
||||||
const iconAction = this.scene.add.sprite(0, 0, "keyboard");
|
const iconAction = this.scene.add.sprite(0, 0, "keyboard");
|
||||||
iconAction.setOrigin(0, -0.1);
|
iconAction.setOrigin(0, -0.1);
|
||||||
iconAction.setPositionRelative(this.actionsBg, this.navigationContainer.width - 32, 4);
|
iconAction.setPositionRelative(actionsBg, this.navigationContainer.width - 32, 4);
|
||||||
this.navigationIcons["BUTTON_ACTION"] = iconAction;
|
this.navigationIcons["BUTTON_ACTION"] = iconAction;
|
||||||
|
|
||||||
const actionText = addTextObject(this.scene, 0, 0, "Action", TextStyle.SETTINGS_LABEL);
|
const actionText = addTextObject(this.scene, 0, 0, "Action", TextStyle.SETTINGS_LABEL);
|
||||||
@ -110,207 +77,81 @@ export default abstract class AbstractSettingsUiUiHandler extends UiHandler {
|
|||||||
|
|
||||||
const iconCancel = this.scene.add.sprite(0, 0, "keyboard");
|
const iconCancel = this.scene.add.sprite(0, 0, "keyboard");
|
||||||
iconCancel.setOrigin(0, -0.1);
|
iconCancel.setOrigin(0, -0.1);
|
||||||
iconCancel.setPositionRelative(this.actionsBg, this.navigationContainer.width - 100, 4);
|
iconCancel.setPositionRelative(actionsBg, this.navigationContainer.width - 100, 4);
|
||||||
this.navigationIcons["BUTTON_CANCEL"] = iconCancel;
|
this.navigationIcons["BUTTON_CANCEL"] = iconCancel;
|
||||||
|
|
||||||
const cancelText = addTextObject(this.scene, 0, 0, "Cancel", TextStyle.SETTINGS_LABEL);
|
const cancelText = addTextObject(this.scene, 0, 0, "Cancel", TextStyle.SETTINGS_LABEL);
|
||||||
cancelText.setOrigin(0, 0.15);
|
cancelText.setOrigin(0, 0.15);
|
||||||
cancelText.setPositionRelative(iconCancel, -cancelText.width/6-2, 0);
|
cancelText.setPositionRelative(iconCancel, -cancelText.width/6-2, 0);
|
||||||
|
|
||||||
const iconReset = this.scene.add.sprite(0, 0, "keyboard");
|
this.optionsContainer = this.scene.add.container(0, 0);
|
||||||
iconReset.setOrigin(0, -0.1);
|
|
||||||
iconReset.setPositionRelative(this.actionsBg, this.navigationContainer.width - 180, 4);
|
|
||||||
this.navigationIcons["BUTTON_HOME"] = iconReset;
|
|
||||||
|
|
||||||
const resetText = addTextObject(this.scene, 0, 0, "Reset all", TextStyle.SETTINGS_LABEL);
|
this.settingLabels = [];
|
||||||
resetText.setOrigin(0, 0.15);
|
this.optionValueLabels = [];
|
||||||
resetText.setPositionRelative(iconReset, -resetText.width/6-2, 0);
|
|
||||||
|
|
||||||
this.settingsContainer.add(this.optionsBg);
|
this.reloadSettings = this.settings.filter(s => s?.requireReload);
|
||||||
this.settingsContainer.add(this.actionsBg);
|
|
||||||
this.settingsContainer.add(this.navigationContainer);
|
|
||||||
this.settingsContainer.add(iconAction);
|
|
||||||
this.settingsContainer.add(iconCancel);
|
|
||||||
this.settingsContainer.add(iconReset);
|
|
||||||
this.settingsContainer.add(actionText);
|
|
||||||
this.settingsContainer.add(cancelText);
|
|
||||||
this.settingsContainer.add(resetText);
|
|
||||||
|
|
||||||
/// Initialize a new configuration "screen" for each type of gamepad.
|
this.settings
|
||||||
for (const config of this.configs) {
|
.forEach((setting, s) => {
|
||||||
// Create a map to store layout settings based on the pad type.
|
let settingName = setting.label;
|
||||||
this.layout[config.padType] = new Map();
|
if (setting?.requireReload) {
|
||||||
// Create a container for gamepad options in the scene, initially hidden.
|
settingName += " (Requires Reload)";
|
||||||
|
}
|
||||||
|
|
||||||
const optionsContainer = this.scene.add.container(0, 0);
|
this.settingLabels[s] = addTextObject(this.scene, 8, 28 + s * 16, settingName, TextStyle.SETTINGS_LABEL);
|
||||||
optionsContainer.setVisible(false);
|
this.settingLabels[s].setOrigin(0, 0);
|
||||||
|
|
||||||
// Gather all binding settings from the configuration.
|
this.optionsContainer.add(this.settingLabels[s]);
|
||||||
const bindingSettings = Object.keys(config.settings);
|
this.optionValueLabels.push(setting.options.map((option, o) => {
|
||||||
|
const valueLabel = addTextObject(this.scene, 0, 0, option, setting.default === o ? TextStyle.SETTINGS_SELECTED : TextStyle.WINDOW);
|
||||||
// Array to hold labels for different settings such as 'Controller', 'Gamepad Support', etc.
|
|
||||||
const settingLabels: Phaser.GameObjects.Text[] = [];
|
|
||||||
|
|
||||||
// Array to hold options for each setting, e.g., 'Auto', 'Disabled'.
|
|
||||||
const optionValueLabels: Phaser.GameObjects.GameObject[][] = [];
|
|
||||||
|
|
||||||
// Object to store sprites for each button configuration.
|
|
||||||
const inputsIcons: InputsIcons = {};
|
|
||||||
|
|
||||||
// Fetch common setting keys such as 'Controller' and 'Gamepad Support' from gamepad settings.
|
|
||||||
const commonSettingKeys = Object.keys(this.settingDevice).slice(0, this.commonSettingsCount).map(key => this.settingDevice[key]);
|
|
||||||
// Combine common and specific bindings into a single array.
|
|
||||||
const specificBindingKeys = [...commonSettingKeys, ...Object.keys(config.settings)];
|
|
||||||
// Fetch default values for these settings and prepare to highlight selected options.
|
|
||||||
const optionCursors = Object.values(Object.keys(this.settingDeviceDefaults).filter(s => specificBindingKeys.includes(s)).map(k => this.settingDeviceDefaults[k]));
|
|
||||||
// Filter out settings that are not relevant to the current gamepad configuration.
|
|
||||||
const settingFiltered = Object.keys(this.settingDevice).filter(_key => specificBindingKeys.includes(this.settingDevice[_key]));
|
|
||||||
// Loop through the filtered settings to manage display and options.
|
|
||||||
|
|
||||||
settingFiltered.forEach((setting, s) => {
|
|
||||||
// Convert the setting key from format 'Key_Name' to 'Key name' for display.
|
|
||||||
const settingName = setting.replace(/\_/g, " ");
|
|
||||||
|
|
||||||
// Create and add a text object for the setting name to the scene.
|
|
||||||
const isLock = this.settingBlacklisted.includes(this.settingDevice[setting]);
|
|
||||||
const labelStyle = isLock ? TextStyle.SETTINGS_LOCKED : TextStyle.SETTINGS_LABEL;
|
|
||||||
settingLabels[s] = addTextObject(this.scene, 8, 28 + s * 16, settingName, labelStyle);
|
|
||||||
settingLabels[s].setOrigin(0, 0);
|
|
||||||
optionsContainer.add(settingLabels[s]);
|
|
||||||
|
|
||||||
// Initialize an array to store the option labels for this setting.
|
|
||||||
const valueLabels: Phaser.GameObjects.GameObject[] = [];
|
|
||||||
|
|
||||||
// Process each option for the current setting.
|
|
||||||
for (const [o, option] of this.settingDeviceOptions[this.settingDevice[setting]].entries()) {
|
|
||||||
// Check if the current setting is for binding keys.
|
|
||||||
if (bindingSettings.includes(this.settingDevice[setting])) {
|
|
||||||
// Create a label for non-null options, typically indicating actionable options like 'change'.
|
|
||||||
if (o) {
|
|
||||||
const valueLabel = addTextObject(this.scene, 0, 0, isLock ? "" : option, TextStyle.WINDOW);
|
|
||||||
valueLabel.setOrigin(0, 0);
|
|
||||||
optionsContainer.add(valueLabel);
|
|
||||||
valueLabels.push(valueLabel);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// For null options, add an icon for the key.
|
|
||||||
const icon = this.scene.add.sprite(0, 0, this.textureOverride ? this.textureOverride : config.padType);
|
|
||||||
icon.setOrigin(0, -0.15);
|
|
||||||
inputsIcons[this.settingDevice[setting]] = icon;
|
|
||||||
optionsContainer.add(icon);
|
|
||||||
valueLabels.push(icon);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// For regular settings like 'Gamepad support', create a label and determine if it is selected.
|
|
||||||
const valueLabel = addTextObject(this.scene, 0, 0, option, this.settingDeviceDefaults[this.settingDevice[setting]] === o ? TextStyle.SETTINGS_SELECTED : TextStyle.WINDOW);
|
|
||||||
valueLabel.setOrigin(0, 0);
|
valueLabel.setOrigin(0, 0);
|
||||||
|
|
||||||
optionsContainer.add(valueLabel);
|
this.optionsContainer.add(valueLabel);
|
||||||
|
|
||||||
//if a setting has 2 options, valueLabels will be an array of 2 elements
|
return valueLabel;
|
||||||
valueLabels.push(valueLabel);
|
}));
|
||||||
}
|
|
||||||
// Collect all option labels for this setting into the main array.
|
|
||||||
optionValueLabels.push(valueLabels);
|
|
||||||
|
|
||||||
// Calculate the total width of all option labels within a specific setting
|
const totalWidth = this.optionValueLabels[s].map(o => o.width).reduce((total, width) => total += width, 0);
|
||||||
// This is achieved by summing the width of each option label
|
|
||||||
const totalWidth = optionValueLabels[s].map((o) => (o as Phaser.GameObjects.Text).width).reduce((total, width) => total += width, 0);
|
|
||||||
|
|
||||||
// Define the minimum width for a label, ensuring it's at least 78 pixels wide or the width of the setting label plus some padding
|
const labelWidth = Math.max(78, this.settingLabels[s].displayWidth + 8);
|
||||||
const labelWidth = Math.max(130, settingLabels[s].displayWidth + 8);
|
|
||||||
|
|
||||||
// Calculate the total available space for placing option labels next to their setting label
|
|
||||||
// We reserve space for the setting label and then distribute the remaining space evenly
|
|
||||||
const totalSpace = (300 - labelWidth) - totalWidth / 6;
|
const totalSpace = (300 - labelWidth) - totalWidth / 6;
|
||||||
// Calculate the spacing between options based on the available space divided by the number of gaps between labels
|
const optionSpacing = Math.floor(totalSpace / (this.optionValueLabels[s].length - 1));
|
||||||
const optionSpacing = Math.floor(totalSpace / (optionValueLabels[s].length - 1));
|
|
||||||
|
|
||||||
// Initialize xOffset to zero, which will be used to position each option label horizontally
|
|
||||||
let xOffset = 0;
|
let xOffset = 0;
|
||||||
|
|
||||||
// Start positioning each option label one by one
|
for (const value of this.optionValueLabels[s]) {
|
||||||
for (const value of optionValueLabels[s]) {
|
value.setPositionRelative(this.settingLabels[s], labelWidth + xOffset, 0);
|
||||||
// Set the option label's position right next to the setting label, adjusted by xOffset
|
xOffset += value.width / 6 + optionSpacing;
|
||||||
(value as Phaser.GameObjects.Text).setPositionRelative(settingLabels[s], labelWidth + xOffset, 0);
|
|
||||||
// Move the xOffset to the right for the next label, ensuring each label is spaced evenly
|
|
||||||
xOffset += (value as Phaser.GameObjects.Text).width / 6 + optionSpacing;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Assigning the newly created components to the layout map under the specific gamepad type.
|
this.optionCursors = this.settings.map(setting => setting.default);
|
||||||
this.layout[config.padType].optionsContainer = optionsContainer; // Container for this pad's options.
|
|
||||||
this.layout[config.padType].inputsIcons = inputsIcons; // Icons for each input specific to this pad.
|
this.settingsContainer.add(this.optionsBg);
|
||||||
this.layout[config.padType].settingLabels = settingLabels; // Text labels for each setting available on this pad.
|
this.settingsContainer.add(this.navigationContainer);
|
||||||
this.layout[config.padType].optionValueLabels = optionValueLabels; // Labels for values corresponding to each setting.
|
this.settingsContainer.add(actionsBg);
|
||||||
this.layout[config.padType].optionCursors = optionCursors; // Cursors to navigate through the options.
|
this.settingsContainer.add(this.optionsContainer);
|
||||||
this.layout[config.padType].keys = specificBindingKeys; // Keys that identify each setting specifically bound to this pad.
|
this.settingsContainer.add(iconAction);
|
||||||
this.layout[config.padType].bindingSettings = bindingSettings; // Settings that define how the keys are bound.
|
this.settingsContainer.add(iconCancel);
|
||||||
|
this.settingsContainer.add(actionText);
|
||||||
|
this.settingsContainer.add(cancelText);
|
||||||
|
|
||||||
// Add the options container to the overall settings container to be displayed in the UI.
|
|
||||||
this.settingsContainer.add(optionsContainer);
|
|
||||||
}
|
|
||||||
// Add the settings container to the UI.
|
|
||||||
ui.add(this.settingsContainer);
|
ui.add(this.settingsContainer);
|
||||||
|
|
||||||
// Initially hide the settings container until needed (e.g., when a gamepad is connected or a button is pressed).
|
this.setCursor(0);
|
||||||
|
this.setScrollCursor(0);
|
||||||
|
|
||||||
this.settingsContainer.setVisible(false);
|
this.settingsContainer.setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the bindings for the current active device configuration.
|
* Update the bindings for the current active device configuration.
|
||||||
*/
|
*/
|
||||||
updateBindings(): void {
|
updateBindings(): void {
|
||||||
// Hide the options container for all layouts to reset the UI visibility.
|
|
||||||
Object.keys(this.layout).forEach((key) => this.layout[key].optionsContainer.setVisible(false));
|
|
||||||
// Fetch the active gamepad configuration from the input controller.
|
|
||||||
const activeConfig = this.getActiveConfig();
|
|
||||||
|
|
||||||
// Set the UI layout for the active configuration. If unsuccessful, exit the function early.
|
|
||||||
if (!this.setLayout(activeConfig)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve the gamepad settings from local storage or use an empty object if none exist.
|
|
||||||
const settings: object = this.getLocalStorageSetting();
|
|
||||||
|
|
||||||
// Update the cursor for each key based on the stored settings or default cursors.
|
|
||||||
this.keys.forEach((key, index) => {
|
|
||||||
this.setOptionCursor(index, settings.hasOwnProperty(key as string) ? settings[key as string] : this.optionCursors[index]);
|
|
||||||
});
|
|
||||||
|
|
||||||
// If the active configuration has no custom bindings set, exit the function early.
|
|
||||||
// by default, if custom does not exists, a default is assigned to it
|
|
||||||
// it only means the gamepad is not yet initalized
|
|
||||||
if (!activeConfig.custom) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For each element in the binding settings, update the icon according to the current assignment.
|
|
||||||
for (const elm of this.bindingSettings) {
|
|
||||||
const icon = getIconWithSettingName(activeConfig, elm);
|
|
||||||
if (icon) {
|
|
||||||
this.inputsIcons[elm as string].setFrame(icon);
|
|
||||||
this.inputsIcons[elm as string].alpha = 1;
|
|
||||||
} else {
|
|
||||||
this.inputsIcons[elm as string].alpha = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the cursor and scroll cursor to their initial positions.
|
|
||||||
this.setCursor(this.cursor);
|
|
||||||
this.setScrollCursor(this.scrollCursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateNavigationDisplay() {
|
|
||||||
const specialIcons = {
|
|
||||||
"BUTTON_HOME": "HOME.png",
|
|
||||||
"BUTTON_DELETE": "DEL.png",
|
|
||||||
};
|
|
||||||
for (const settingName of Object.keys(this.navigationIcons)) {
|
for (const settingName of Object.keys(this.navigationIcons)) {
|
||||||
if (Object.keys(specialIcons).includes(settingName)) {
|
if (settingName === "BUTTON_HOME") {
|
||||||
this.navigationIcons[settingName].setTexture("keyboard");
|
this.navigationIcons[settingName].setTexture("keyboard");
|
||||||
this.navigationIcons[settingName].setFrame(specialIcons[settingName]);
|
this.navigationIcons[settingName].setFrame("HOME.png");
|
||||||
this.navigationIcons[settingName].alpha = 1;
|
this.navigationIcons[settingName].alpha = 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -324,112 +165,61 @@ export default abstract class AbstractSettingsUiUiHandler extends UiHandler {
|
|||||||
this.navigationIcons[settingName].alpha = 0;
|
this.navigationIcons[settingName].alpha = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
NavigationManager.getInstance().updateIcons();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show the UI with the provided arguments.
|
* Show the UI with the provided arguments.
|
||||||
*
|
*
|
||||||
* @param args - Arguments to be passed to the show method.
|
* @param args - Arguments to be passed to the show method.
|
||||||
* @returns `true` if successful.
|
* @returns `true` if successful.
|
||||||
*/
|
*/
|
||||||
show(args: any[]): boolean {
|
show(args: any[]): boolean {
|
||||||
super.show(args);
|
super.show(args);
|
||||||
|
|
||||||
this.updateNavigationDisplay();
|
|
||||||
NavigationManager.getInstance().updateIcons();
|
|
||||||
// Update the bindings for the current active gamepad configuration.
|
|
||||||
this.updateBindings();
|
this.updateBindings();
|
||||||
|
|
||||||
// Make the settings container visible to the user.
|
const settings: object = localStorage.hasOwnProperty(this.localStorageKey) ? JSON.parse(localStorage.getItem(this.localStorageKey)) : {};
|
||||||
this.settingsContainer.setVisible(true);
|
|
||||||
// Reset the scroll cursor to the top of the settings container.
|
this.settings.forEach((setting, s) => this.setOptionCursor(s, settings.hasOwnProperty(setting.key) ? settings[setting.key] : this.settings[s].default));
|
||||||
this.resetScroll();
|
|
||||||
|
this.settingsContainer.setVisible(true);
|
||||||
|
this.setCursor(0);
|
||||||
|
|
||||||
// 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);
|
this.getUi().moveTo(this.settingsContainer, this.getUi().length - 1);
|
||||||
|
|
||||||
// Hide any tooltips that might be visible before showing the settings container.
|
|
||||||
this.getUi().hideTooltip();
|
this.getUi().hideTooltip();
|
||||||
|
|
||||||
// Return true to indicate the UI was successfully shown.
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the UI layout for the active device configuration.
|
* Processes input from a specified button.
|
||||||
*
|
* This method handles navigation through a UI menu, including movement through menu items
|
||||||
* @param activeConfig - The active device configuration.
|
* and handling special actions like cancellation. Each button press may adjust the cursor
|
||||||
* @returns `true` if the layout was successfully applied, otherwise `false`.
|
* position or the menu scroll, and plays a sound effect if the action was successful.
|
||||||
*/
|
*
|
||||||
setLayout(activeConfig: InterfaceConfig): boolean {
|
* @param button - The button pressed by the user.
|
||||||
// Check if there is no active configuration (e.g., no gamepad connected).
|
* @returns `true` if the action associated with the button was successfully processed, `false` otherwise.
|
||||||
if (!activeConfig) {
|
*/
|
||||||
// Retrieve the layout for when no gamepads are connected.
|
|
||||||
const layout = this.layout["noGamepads"];
|
|
||||||
// Make the options container visible to show message.
|
|
||||||
layout.optionsContainer.setVisible(true);
|
|
||||||
// Return false indicating the layout application was not successful due to lack of gamepad.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Extract the type of the gamepad from the active configuration.
|
|
||||||
const configType = activeConfig.padType;
|
|
||||||
|
|
||||||
// Retrieve the layout settings based on the type of the gamepad.
|
|
||||||
const layout = this.layout[configType];
|
|
||||||
// Update the main controller with configuration details from the selected layout.
|
|
||||||
this.keys = layout.keys;
|
|
||||||
this.optionsContainer = layout.optionsContainer;
|
|
||||||
this.optionsContainer.setVisible(true);
|
|
||||||
this.settingLabels = layout.settingLabels;
|
|
||||||
this.optionValueLabels = layout.optionValueLabels;
|
|
||||||
this.optionCursors = layout.optionCursors;
|
|
||||||
this.inputsIcons = layout.inputsIcons;
|
|
||||||
this.bindingSettings = layout.bindingSettings;
|
|
||||||
|
|
||||||
// Return true indicating the layout was successfully applied.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process the input for the given button.
|
|
||||||
*
|
|
||||||
* @param button - The button to process.
|
|
||||||
* @returns `true` if the input was processed successfully.
|
|
||||||
*/
|
|
||||||
processInput(button: Button): boolean {
|
processInput(button: Button): boolean {
|
||||||
const ui = this.getUi();
|
const ui = this.getUi();
|
||||||
// Defines the maximum number of rows that can be displayed on the screen.
|
// Defines the maximum number of rows that can be displayed on the screen.
|
||||||
let success = false;
|
|
||||||
this.updateNavigationDisplay();
|
|
||||||
|
|
||||||
// Handle the input based on the button pressed.
|
let success = false;
|
||||||
|
|
||||||
if (button === Button.CANCEL) {
|
if (button === Button.CANCEL) {
|
||||||
// Handle cancel button press, reverting UI mode to previous state.
|
|
||||||
success = true;
|
success = true;
|
||||||
NavigationManager.getInstance().reset();
|
NavigationManager.getInstance().reset();
|
||||||
|
// Reverts UI to its previous state on cancel.
|
||||||
this.scene.ui.revertMode();
|
this.scene.ui.revertMode();
|
||||||
} else {
|
} else {
|
||||||
const cursor = this.cursor + this.scrollCursor; // Calculate the absolute cursor position.
|
const cursor = this.cursor + this.scrollCursor;
|
||||||
const setting = this.settingDevice[Object.keys(this.settingDevice)[cursor]];
|
|
||||||
switch (button) {
|
switch (button) {
|
||||||
case Button.ACTION:
|
case Button.UP:
|
||||||
if (!this.optionCursors || !this.optionValueLabels) {
|
if (cursor) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.settingBlacklisted.includes(setting) || !setting.includes("BUTTON_")) {
|
|
||||||
success = false;
|
|
||||||
} else {
|
|
||||||
success = this.setSetting(this.scene, setting, 1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Button.UP: // Move up in the menu.
|
|
||||||
if (!this.optionValueLabels) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (cursor) { // If not at the top, move the cursor up.
|
|
||||||
if (this.cursor) {
|
if (this.cursor) {
|
||||||
success = this.setCursor(this.cursor - 1);
|
success = this.setCursor(this.cursor - 1);
|
||||||
} else {// If at the top of the visible items, scroll up.
|
} else {
|
||||||
success = this.setScrollCursor(this.scrollCursor - 1);
|
success = this.setScrollCursor(this.scrollCursor - 1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -441,12 +231,9 @@ export default abstract class AbstractSettingsUiUiHandler extends UiHandler {
|
|||||||
success = successA && successB; // success is just there to play the little validation sound effect
|
success = successA && successB; // success is just there to play the little validation sound effect
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Button.DOWN: // Move down in the menu.
|
case Button.DOWN:
|
||||||
if (!this.optionValueLabels) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (cursor < this.optionValueLabels.length - 1) {
|
if (cursor < this.optionValueLabels.length - 1) {
|
||||||
if (this.cursor < this.rowsToDisplay - 1) {
|
if (this.cursor < this.rowsToDisplay - 1) {// if the visual cursor is in the frame of 0 to 8
|
||||||
success = this.setCursor(this.cursor + 1);
|
success = this.setCursor(this.cursor + 1);
|
||||||
} else if (this.scrollCursor < this.optionValueLabels.length - this.rowsToDisplay) {
|
} else if (this.scrollCursor < this.optionValueLabels.length - this.rowsToDisplay) {
|
||||||
success = this.setScrollCursor(this.scrollCursor + 1);
|
success = this.setScrollCursor(this.scrollCursor + 1);
|
||||||
@ -460,23 +247,14 @@ export default abstract class AbstractSettingsUiUiHandler extends UiHandler {
|
|||||||
success = successA && successB; // Indicates a successful cursor and scroll adjustment.
|
success = successA && successB; // Indicates a successful cursor and scroll adjustment.
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Button.LEFT: // Move selection left within the current option set.
|
case Button.LEFT:
|
||||||
if (!this.optionCursors || !this.optionValueLabels) {
|
if (this.optionCursors[cursor]) {// Moves the option cursor left, if possible.
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.settingBlacklisted.includes(setting) || setting.includes("BUTTON_")) {
|
|
||||||
success = false;
|
|
||||||
} else if (this.optionCursors[cursor]) {
|
|
||||||
success = this.setOptionCursor(cursor, this.optionCursors[cursor] - 1, true);
|
success = this.setOptionCursor(cursor, this.optionCursors[cursor] - 1, true);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Button.RIGHT: // Move selection right within the current option set.
|
case Button.RIGHT:
|
||||||
if (!this.optionCursors || !this.optionValueLabels) {
|
// Moves the option cursor right, if possible.
|
||||||
return;
|
if (this.optionCursors[cursor] < this.optionValueLabels[cursor].length - 1) {
|
||||||
}
|
|
||||||
if (this.settingBlacklisted.includes(setting) || setting.includes("BUTTON_")) {
|
|
||||||
success = false;
|
|
||||||
} else if (this.optionCursors[cursor] < this.optionValueLabels[cursor].length - 1) {
|
|
||||||
success = this.setOptionCursor(cursor, this.optionCursors[cursor] + 1, true);
|
success = this.setOptionCursor(cursor, this.optionCursors[cursor] + 1, true);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -487,130 +265,100 @@ export default abstract class AbstractSettingsUiUiHandler extends UiHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a change occurred, play the selection sound.
|
// Plays a select sound effect if an action was successfully processed.
|
||||||
if (success) {
|
if (success) {
|
||||||
ui.playSelect();
|
ui.playSelect();
|
||||||
}
|
}
|
||||||
|
|
||||||
return success; // Return whether the input resulted in a successful action.
|
return success;
|
||||||
}
|
|
||||||
|
|
||||||
resetScroll() {
|
|
||||||
this.cursorObj?.destroy();
|
|
||||||
this.cursorObj = null;
|
|
||||||
this.cursor = null;
|
|
||||||
this.setCursor(0);
|
|
||||||
this.setScrollCursor(0);
|
|
||||||
this.updateSettingsScroll();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the cursor to the specified position.
|
* Set the cursor to the specified position.
|
||||||
*
|
*
|
||||||
* @param cursor - The cursor position to set.
|
* @param cursor - The cursor position to set.
|
||||||
* @returns `true` if the cursor was set successfully.
|
* @returns `true` if the cursor was set successfully.
|
||||||
*/
|
*/
|
||||||
setCursor(cursor: integer): boolean {
|
setCursor(cursor: integer): boolean {
|
||||||
const ret = super.setCursor(cursor);
|
const ret = super.setCursor(cursor);
|
||||||
// If the optionsContainer is not initialized, return the result from the parent class directly.
|
|
||||||
if (!this.optionsContainer) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the cursor object exists, if not, create it.
|
|
||||||
if (!this.cursorObj) {
|
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 = 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); // Set the origin to the top-left corner.
|
this.cursorObj.setOrigin(0, 0);
|
||||||
this.optionsContainer.add(this.cursorObj); // Add the cursor to the options container.
|
this.optionsContainer.add(this.cursorObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the position of the cursor object relative to the options background based on the current cursor and scroll positions.
|
|
||||||
this.cursorObj.setPositionRelative(this.optionsBg, 4, 4 + (this.cursor + this.scrollCursor) * 16);
|
this.cursorObj.setPositionRelative(this.optionsBg, 4, 4 + (this.cursor + this.scrollCursor) * 16);
|
||||||
|
|
||||||
return ret; // Return the result from the parent class's setCursor method.
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the scroll cursor to the specified position.
|
* Set the option cursor to the specified position.
|
||||||
*
|
*
|
||||||
* @param scrollCursor - The scroll cursor position to set.
|
* @param settingIndex - The index of the setting.
|
||||||
* @returns `true` if the scroll cursor was set successfully.
|
* @param cursor - The cursor position to set.
|
||||||
*/
|
* @param save - Whether to save the setting to local storage.
|
||||||
|
* @returns `true` if the option cursor was set successfully.
|
||||||
|
*/
|
||||||
|
setOptionCursor(settingIndex: integer, cursor: integer, save?: boolean): boolean {
|
||||||
|
const setting = this.settings[settingIndex];
|
||||||
|
|
||||||
|
if (setting.key === SettingKeys.Touch_Controls && cursor && hasTouchscreen() && isMobile()) {
|
||||||
|
this.getUi().playError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.key, cursor);
|
||||||
|
if (this.reloadSettings.includes(setting)) {
|
||||||
|
this.reloadRequired = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the scroll cursor to the specified position.
|
||||||
|
*
|
||||||
|
* @param scrollCursor - The scroll cursor position to set.
|
||||||
|
* @returns `true` if the scroll cursor was set successfully.
|
||||||
|
*/
|
||||||
setScrollCursor(scrollCursor: integer): boolean {
|
setScrollCursor(scrollCursor: integer): boolean {
|
||||||
// Check if the new scroll position is the same as the current one; if so, do not update.
|
|
||||||
if (scrollCursor === this.scrollCursor) {
|
if (scrollCursor === this.scrollCursor) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the internal scroll cursor state
|
|
||||||
this.scrollCursor = scrollCursor;
|
this.scrollCursor = scrollCursor;
|
||||||
|
|
||||||
// Apply the new scroll position to the settings UI.
|
|
||||||
this.updateSettingsScroll();
|
this.updateSettingsScroll();
|
||||||
|
|
||||||
// Reset the cursor to its current position to adjust its visibility after scrolling.
|
|
||||||
this.setCursor(this.cursor);
|
this.setCursor(this.cursor);
|
||||||
|
|
||||||
return true; // Return true to indicate the scroll cursor was successfully updated.
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the option cursor to the specified position.
|
* Update the scroll position of the settings UI.
|
||||||
*
|
*/
|
||||||
* @param settingIndex - The index of the setting.
|
|
||||||
* @param cursor - The cursor position to set.
|
|
||||||
* @param save - Whether to save the setting to local storage.
|
|
||||||
* @returns `true` if the option cursor was set successfully.
|
|
||||||
*/
|
|
||||||
setOptionCursor(settingIndex: integer, cursor: integer, save?: boolean): boolean {
|
|
||||||
// Retrieve the specific setting using the settingIndex from the settingDevice enumeration.
|
|
||||||
const setting = this.settingDevice[Object.keys(this.settingDevice)[settingIndex]];
|
|
||||||
|
|
||||||
// Get the current cursor position for this setting.
|
|
||||||
const lastCursor = this.optionCursors[settingIndex];
|
|
||||||
|
|
||||||
// Check if the setting is not part of the bindings (i.e., it's a regular setting).
|
|
||||||
if (!this.bindingSettings.includes(setting) && !setting.includes("BUTTON_")) {
|
|
||||||
// Get the label of the last selected option and revert its color to the default.
|
|
||||||
const lastValueLabel = this.optionValueLabels[settingIndex][lastCursor];
|
|
||||||
lastValueLabel.setColor(this.getTextColor(TextStyle.WINDOW));
|
|
||||||
lastValueLabel.setShadowColor(this.getTextColor(TextStyle.WINDOW, true));
|
|
||||||
|
|
||||||
// Update the cursor for the setting to the new position.
|
|
||||||
this.optionCursors[settingIndex] = cursor;
|
|
||||||
|
|
||||||
// Change the color of the new selected option to indicate it's selected.
|
|
||||||
const newValueLabel = this.optionValueLabels[settingIndex][cursor];
|
|
||||||
newValueLabel.setColor(this.getTextColor(TextStyle.SETTINGS_SELECTED));
|
|
||||||
newValueLabel.setShadowColor(this.getTextColor(TextStyle.SETTINGS_SELECTED, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the save flag is set, save the setting to local storage
|
|
||||||
if (save) {
|
|
||||||
this.saveSettingToLocalStorage(setting, cursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true; // Return true to indicate the cursor was successfully updated.
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the scroll position of the settings UI.
|
|
||||||
*/
|
|
||||||
updateSettingsScroll(): void {
|
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);
|
this.optionsContainer.setY(-16 * this.scrollCursor);
|
||||||
|
|
||||||
// Iterate over all setting labels to update their visibility.
|
|
||||||
for (let s = 0; s < this.settingLabels.length; s++) {
|
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 + this.rowsToDisplay;
|
const visible = s >= this.scrollCursor && s < this.scrollCursor + this.rowsToDisplay;
|
||||||
|
|
||||||
// Set the visibility of the setting label and its corresponding options.
|
|
||||||
this.settingLabels[s].setVisible(visible);
|
this.settingLabels[s].setVisible(visible);
|
||||||
for (const option of this.optionValueLabels[s]) {
|
for (const option of this.optionValueLabels[s]) {
|
||||||
option.setVisible(visible);
|
option.setVisible(visible);
|
||||||
@ -619,29 +367,25 @@ export default abstract class AbstractSettingsUiUiHandler extends UiHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear the UI elements and state.
|
* Clear the UI elements and state.
|
||||||
*/
|
*/
|
||||||
clear(): void {
|
clear() {
|
||||||
super.clear();
|
super.clear();
|
||||||
|
|
||||||
// Hide the settings container to remove it from the view.
|
|
||||||
this.settingsContainer.setVisible(false);
|
this.settingsContainer.setVisible(false);
|
||||||
|
|
||||||
// Remove the cursor from the UI.
|
|
||||||
this.eraseCursor();
|
this.eraseCursor();
|
||||||
|
if (this.reloadRequired) {
|
||||||
|
this.reloadRequired = false;
|
||||||
|
this.scene.reset(true, false, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Erase the cursor from the UI.
|
* Erase the cursor from the UI.
|
||||||
*/
|
*/
|
||||||
eraseCursor(): void {
|
eraseCursor() {
|
||||||
// Check if a cursor object exists.
|
|
||||||
if (this.cursorObj) {
|
if (this.cursorObj) {
|
||||||
this.cursorObj.destroy();
|
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.cursorObj = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import {Mode} from "#app/ui/ui";
|
import {Mode} from "#app/ui/ui";
|
||||||
import {InputsIcons} from "#app/ui/settings/abstract-settings-ui-handler";
|
import {InputsIcons} from "#app/ui/settings/abstract-control-settings-ui-handler.js";
|
||||||
import {addTextObject, setTextStyle, TextStyle} from "#app/ui/text";
|
import {addTextObject, setTextStyle, TextStyle} from "#app/ui/text";
|
||||||
import {addWindow} from "#app/ui/ui-theme";
|
import {addWindow} from "#app/ui/ui-theme";
|
||||||
import {Button} from "#app/enums/buttons";
|
import {Button} from "#app/enums/buttons";
|
||||||
|
|
||||||
|
const LEFT = "LEFT";
|
||||||
|
const RIGHT = "RIGHT";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages navigation and menus tabs within the setting menu.
|
* Manages navigation and menus tabs within the setting menu.
|
||||||
*/
|
*/
|
||||||
@ -24,14 +27,16 @@ export class NavigationManager {
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.modes = [
|
this.modes = [
|
||||||
Mode.SETTINGS,
|
Mode.SETTINGS,
|
||||||
|
Mode.SETTINGS_ACCESSIBILITY,
|
||||||
Mode.SETTINGS_GAMEPAD,
|
Mode.SETTINGS_GAMEPAD,
|
||||||
Mode.SETTINGS_KEYBOARD,
|
Mode.SETTINGS_KEYBOARD,
|
||||||
];
|
];
|
||||||
this.labels = ["General", "Gamepad", "Keyboard"];
|
this.labels = ["General", "Accessibility", "Gamepad", "Keyboard"];
|
||||||
}
|
}
|
||||||
|
|
||||||
public reset() {
|
public reset() {
|
||||||
this.selectedMode = Mode.SETTINGS;
|
this.selectedMode = Mode.SETTINGS;
|
||||||
|
this.updateNavigationMenus();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -46,32 +51,20 @@ export class NavigationManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Navigates to the previous mode in the modes array.
|
* Navigates modes based on given direction
|
||||||
* @param scene The current BattleScene instance.
|
* @param scene The current BattleScene instance
|
||||||
|
* @param direction LEFT or RIGHT
|
||||||
*/
|
*/
|
||||||
public navigateLeft(scene) {
|
public navigate(scene, direction) {
|
||||||
const pos = this.modes.indexOf(this.selectedMode);
|
const pos = this.modes.indexOf(this.selectedMode);
|
||||||
const maxPos = this.modes.length - 1;
|
const maxPos = this.modes.length - 1;
|
||||||
if (pos === 0) {
|
const increment = direction === LEFT ? -1 : 1;
|
||||||
|
if (pos === 0 && direction === LEFT) {
|
||||||
this.selectedMode = this.modes[maxPos];
|
this.selectedMode = this.modes[maxPos];
|
||||||
} else {
|
} else if (pos === maxPos && direction === RIGHT) {
|
||||||
this.selectedMode = this.modes[pos - 1];
|
|
||||||
}
|
|
||||||
scene.ui.setMode(this.selectedMode);
|
|
||||||
this.updateNavigationMenus();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Navigates to the next mode in the modes array.
|
|
||||||
* @param scene The current BattleScene instance.
|
|
||||||
*/
|
|
||||||
public navigateRight(scene) {
|
|
||||||
const pos = this.modes.indexOf(this.selectedMode);
|
|
||||||
const maxPos = this.modes.length - 1;
|
|
||||||
if (pos === maxPos) {
|
|
||||||
this.selectedMode = this.modes[0];
|
this.selectedMode = this.modes[0];
|
||||||
} else {
|
} else {
|
||||||
this.selectedMode = this.modes[pos + 1];
|
this.selectedMode = this.modes[pos + increment];
|
||||||
}
|
}
|
||||||
scene.ui.setMode(this.selectedMode);
|
scene.ui.setMode(this.selectedMode);
|
||||||
this.updateNavigationMenus();
|
this.updateNavigationMenus();
|
||||||
@ -204,13 +197,11 @@ export default class NavigationMenu extends Phaser.GameObjects.Container {
|
|||||||
const navigationManager = NavigationManager.getInstance();
|
const navigationManager = NavigationManager.getInstance();
|
||||||
switch (button) {
|
switch (button) {
|
||||||
case Button.CYCLE_FORM:
|
case Button.CYCLE_FORM:
|
||||||
navigationManager.navigateLeft(this.scene);
|
navigationManager.navigate(this.scene, LEFT);
|
||||||
return true;
|
return true;
|
||||||
break;
|
|
||||||
case Button.CYCLE_SHINY:
|
case Button.CYCLE_SHINY:
|
||||||
navigationManager.navigateRight(this.scene);
|
navigationManager.navigate(this.scene, RIGHT);
|
||||||
return true;
|
return true;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
20
src/ui/settings/settings-accessiblity-ui-handler.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import BattleScene from "../../battle-scene";
|
||||||
|
import { Mode } from "../ui";
|
||||||
|
"#app/inputs-controller.js";
|
||||||
|
import AbstractSettingsUiHandler from "./abstract-settings-ui-handler";
|
||||||
|
import { Setting, SettingType } from "#app/system/settings/settings";
|
||||||
|
|
||||||
|
export default class SettingsAccessibilityUiHandler extends AbstractSettingsUiHandler {
|
||||||
|
/**
|
||||||
|
* Creates an instance of SettingsGamepadUiHandler.
|
||||||
|
*
|
||||||
|
* @param scene - The BattleScene instance.
|
||||||
|
* @param mode - The UI mode, optional.
|
||||||
|
*/
|
||||||
|
constructor(scene: BattleScene, mode?: Mode) {
|
||||||
|
super(scene, mode);
|
||||||
|
this.title = "Accessibility";
|
||||||
|
this.settings = Setting.filter(s => s.type === SettingType.ACCESSIBILITY);
|
||||||
|
this.localStorageKey = "settings";
|
||||||
|
}
|
||||||
|
}
|
@ -7,22 +7,22 @@ import {
|
|||||||
settingGamepadBlackList,
|
settingGamepadBlackList,
|
||||||
settingGamepadDefaults,
|
settingGamepadDefaults,
|
||||||
settingGamepadOptions
|
settingGamepadOptions
|
||||||
} from "../../system/settings-gamepad";
|
} from "../../system/settings/settings-gamepad";
|
||||||
import pad_xbox360 from "#app/configs/inputs/pad_xbox360";
|
import pad_xbox360 from "#app/configs/inputs/pad_xbox360";
|
||||||
import pad_dualshock from "#app/configs/inputs/pad_dualshock";
|
import pad_dualshock from "#app/configs/inputs/pad_dualshock";
|
||||||
import pad_unlicensedSNES from "#app/configs/inputs/pad_unlicensedSNES";
|
import pad_unlicensedSNES from "#app/configs/inputs/pad_unlicensedSNES";
|
||||||
import {InterfaceConfig} from "#app/inputs-controller";
|
import {InterfaceConfig} from "#app/inputs-controller";
|
||||||
import AbstractSettingsUiUiHandler from "#app/ui/settings/abstract-settings-ui-handler";
|
import AbstractControlSettingsUiHandler from "#app/ui/settings/abstract-control-settings-ui-handler.js";
|
||||||
import {Device} from "#app/enums/devices";
|
import {Device} from "#app/enums/devices";
|
||||||
import {truncateString} from "#app/utils";
|
import {truncateString} from "#app/utils";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class representing the settings UI handler for gamepads.
|
* Class representing the settings UI handler for gamepads.
|
||||||
*
|
*
|
||||||
* @extends AbstractSettingsUiUiHandler
|
* @extends AbstractControlSettingsUiHandler
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default class SettingsGamepadUiHandler extends AbstractSettingsUiUiHandler {
|
export default class SettingsGamepadUiHandler extends AbstractControlSettingsUiHandler {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance of SettingsGamepadUiHandler.
|
* Creates an instance of SettingsGamepadUiHandler.
|
||||||
@ -33,18 +33,17 @@ export default class SettingsGamepadUiHandler extends AbstractSettingsUiUiHandle
|
|||||||
constructor(scene: BattleScene, mode?: Mode) {
|
constructor(scene: BattleScene, mode?: Mode) {
|
||||||
super(scene, mode);
|
super(scene, mode);
|
||||||
this.titleSelected = "Gamepad";
|
this.titleSelected = "Gamepad";
|
||||||
this.settingDevice = SettingGamepad;
|
this.setting = SettingGamepad;
|
||||||
this.settingDeviceDefaults = settingGamepadDefaults;
|
this.settingDeviceDefaults = settingGamepadDefaults;
|
||||||
this.settingDeviceOptions = settingGamepadOptions;
|
this.settingDeviceOptions = settingGamepadOptions;
|
||||||
this.configs = [pad_xbox360, pad_dualshock, pad_unlicensedSNES];
|
this.configs = [pad_xbox360, pad_dualshock, pad_unlicensedSNES];
|
||||||
this.commonSettingsCount = 2;
|
this.commonSettingsCount = 2;
|
||||||
this.localStoragePropertyName = "settingsGamepad";
|
this.localStoragePropertyName = "settingsGamepad";
|
||||||
this.settingBlacklisted = settingGamepadBlackList;
|
this.settingBlacklisted = settingGamepadBlackList;
|
||||||
|
this.device = Device.GAMEPAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
setSetting(scene: BattleScene, setting, value: integer): boolean {
|
setSetting = setSettingGamepad;
|
||||||
return setSettingGamepad(scene, setting, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setup UI elements.
|
* Setup UI elements.
|
||||||
@ -65,26 +64,6 @@ export default class SettingsGamepadUiHandler extends AbstractSettingsUiUiHandle
|
|||||||
this.layout["noGamepads"].label = label;
|
this.layout["noGamepads"].label = label;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the active configuration.
|
|
||||||
*
|
|
||||||
* @returns The active gamepad configuration.
|
|
||||||
*/
|
|
||||||
getActiveConfig(): InterfaceConfig {
|
|
||||||
return this.scene.inputController.getActiveConfig(Device.GAMEPAD);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the gamepad settings from local storage.
|
|
||||||
*
|
|
||||||
* @returns The gamepad settings from local storage.
|
|
||||||
*/
|
|
||||||
getLocalStorageSetting(): object {
|
|
||||||
// Retrieve the gamepad settings from local storage or use an empty object if none exist.
|
|
||||||
const settings: object = localStorage.hasOwnProperty("settingsGamepad") ? JSON.parse(localStorage.getItem("settingsGamepad")) : {};
|
|
||||||
return settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the layout for the active configuration.
|
* Set the layout for the active configuration.
|
||||||
*
|
*
|
||||||
@ -105,27 +84,6 @@ export default class SettingsGamepadUiHandler extends AbstractSettingsUiUiHandle
|
|||||||
return super.setLayout(activeConfig);
|
return super.setLayout(activeConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Navigate to the left menu tab.
|
|
||||||
*
|
|
||||||
* @returns `true` indicating the navigation was successful.
|
|
||||||
*/
|
|
||||||
navigateMenuLeft(): boolean {
|
|
||||||
this.scene.ui.setMode(Mode.SETTINGS);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Navigate to the right menu tab.
|
|
||||||
*
|
|
||||||
* @returns `true` indicating the navigation was successful.
|
|
||||||
*/
|
|
||||||
navigateMenuRight(): boolean {
|
|
||||||
this.scene.ui.setMode(Mode.SETTINGS_KEYBOARD);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the display of the chosen gamepad.
|
* Update the display of the chosen gamepad.
|
||||||
*/
|
*/
|
||||||
@ -135,11 +93,11 @@ export default class SettingsGamepadUiHandler extends AbstractSettingsUiUiHandle
|
|||||||
this.resetScroll();
|
this.resetScroll();
|
||||||
|
|
||||||
// Iterate over the keys in the settingDevice enumeration.
|
// Iterate over the keys in the settingDevice enumeration.
|
||||||
for (const [index, key] of Object.keys(this.settingDevice).entries()) {
|
for (const [index, key] of Object.keys(this.setting).entries()) {
|
||||||
const setting = this.settingDevice[key]; // Get the actual setting value using the key.
|
const setting = this.setting[key]; // Get the actual setting value using the key.
|
||||||
|
|
||||||
// Check if the current setting corresponds to the controller setting.
|
// Check if the current setting corresponds to the controller setting.
|
||||||
if (setting === this.settingDevice.Controller) {
|
if (setting === this.setting.Controller) {
|
||||||
// Iterate over all layouts excluding the 'noGamepads' special case.
|
// Iterate over all layouts excluding the 'noGamepads' special case.
|
||||||
for (const _key of Object.keys(this.layout)) {
|
for (const _key of Object.keys(this.layout)) {
|
||||||
if (_key === "noGamepads") {
|
if (_key === "noGamepads") {
|
||||||
@ -157,12 +115,12 @@ export default class SettingsGamepadUiHandler extends AbstractSettingsUiUiHandle
|
|||||||
/**
|
/**
|
||||||
* Save the setting to local storage.
|
* Save the setting to local storage.
|
||||||
*
|
*
|
||||||
* @param setting - The setting to save.
|
* @param settingName - The setting to save.
|
||||||
* @param cursor - The cursor position to save.
|
* @param cursor - The cursor position to save.
|
||||||
*/
|
*/
|
||||||
saveSettingToLocalStorage(setting, cursor): void {
|
saveSettingToLocalStorage(settingName, cursor): void {
|
||||||
if (this.settingDevice[setting] !== this.settingDevice.Controller) {
|
if (this.setting[settingName] !== this.setting.Controller) {
|
||||||
this.scene.gameData.saveGamepadSetting(setting, cursor);
|
this.scene.gameData.saveControlSetting(this.device, this.localStoragePropertyName, settingName, this.settingDeviceDefaults, cursor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,9 @@ import {
|
|||||||
settingKeyboardBlackList,
|
settingKeyboardBlackList,
|
||||||
settingKeyboardDefaults,
|
settingKeyboardDefaults,
|
||||||
settingKeyboardOptions
|
settingKeyboardOptions
|
||||||
} from "#app/system/settings-keyboard";
|
} from "#app/system/settings/settings-keyboard";
|
||||||
import {reverseValueToKeySetting, truncateString} from "#app/utils";
|
import {reverseValueToKeySetting, truncateString} from "#app/utils";
|
||||||
import AbstractSettingsUiUiHandler from "#app/ui/settings/abstract-settings-ui-handler";
|
import AbstractControlSettingsUiHandler from "#app/ui/settings/abstract-control-settings-ui-handler.js";
|
||||||
import {InterfaceConfig} from "#app/inputs-controller";
|
import {InterfaceConfig} from "#app/inputs-controller";
|
||||||
import {addTextObject, TextStyle} from "#app/ui/text";
|
import {addTextObject, TextStyle} from "#app/ui/text";
|
||||||
import {deleteBind} from "#app/configs/inputs/configHandler";
|
import {deleteBind} from "#app/configs/inputs/configHandler";
|
||||||
@ -19,9 +19,9 @@ import {NavigationManager} from "#app/ui/settings/navigationMenu";
|
|||||||
/**
|
/**
|
||||||
* Class representing the settings UI handler for keyboards.
|
* Class representing the settings UI handler for keyboards.
|
||||||
*
|
*
|
||||||
* @extends AbstractSettingsUiUiHandler
|
* @extends AbstractControlSettingsUiHandler
|
||||||
*/
|
*/
|
||||||
export default class SettingsKeyboardUiHandler extends AbstractSettingsUiUiHandler {
|
export default class SettingsKeyboardUiHandler extends AbstractControlSettingsUiHandler {
|
||||||
/**
|
/**
|
||||||
* Creates an instance of SettingsKeyboardUiHandler.
|
* Creates an instance of SettingsKeyboardUiHandler.
|
||||||
*
|
*
|
||||||
@ -31,7 +31,7 @@ export default class SettingsKeyboardUiHandler extends AbstractSettingsUiUiHandl
|
|||||||
constructor(scene: BattleScene, mode?: Mode) {
|
constructor(scene: BattleScene, mode?: Mode) {
|
||||||
super(scene, mode);
|
super(scene, mode);
|
||||||
this.titleSelected = "Keyboard";
|
this.titleSelected = "Keyboard";
|
||||||
this.settingDevice = SettingKeyboard;
|
this.setting = SettingKeyboard;
|
||||||
this.settingDeviceDefaults = settingKeyboardDefaults;
|
this.settingDeviceDefaults = settingKeyboardDefaults;
|
||||||
this.settingDeviceOptions = settingKeyboardOptions;
|
this.settingDeviceOptions = settingKeyboardOptions;
|
||||||
this.configs = [cfg_keyboard_qwerty];
|
this.configs = [cfg_keyboard_qwerty];
|
||||||
@ -39,6 +39,7 @@ export default class SettingsKeyboardUiHandler extends AbstractSettingsUiUiHandl
|
|||||||
this.textureOverride = "keyboard";
|
this.textureOverride = "keyboard";
|
||||||
this.localStoragePropertyName = "settingsKeyboard";
|
this.localStoragePropertyName = "settingsKeyboard";
|
||||||
this.settingBlacklisted = settingKeyboardBlackList;
|
this.settingBlacklisted = settingKeyboardBlackList;
|
||||||
|
this.device = Device.KEYBOARD;
|
||||||
|
|
||||||
const deleteEvent = scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.DELETE);
|
const deleteEvent = scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.DELETE);
|
||||||
const restoreDefaultEvent = scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.HOME);
|
const restoreDefaultEvent = scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.HOME);
|
||||||
@ -46,9 +47,7 @@ export default class SettingsKeyboardUiHandler extends AbstractSettingsUiUiHandl
|
|||||||
restoreDefaultEvent.on("up", this.onHomeDown, this);
|
restoreDefaultEvent.on("up", this.onHomeDown, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
setSetting(scene: BattleScene, setting, value: integer): boolean {
|
setSetting = setSettingKeyboard;
|
||||||
return setSettingKeyboard(scene, setting, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setup UI elements.
|
* Setup UI elements.
|
||||||
@ -114,26 +113,6 @@ export default class SettingsKeyboardUiHandler extends AbstractSettingsUiUiHandl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the active configuration.
|
|
||||||
*
|
|
||||||
* @returns The active keyboard configuration.
|
|
||||||
*/
|
|
||||||
getActiveConfig(): InterfaceConfig {
|
|
||||||
return this.scene.inputController.getActiveConfig(Device.KEYBOARD);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the keyboard settings from local storage.
|
|
||||||
*
|
|
||||||
* @returns The keyboard settings from local storage.
|
|
||||||
*/
|
|
||||||
getLocalStorageSetting(): object {
|
|
||||||
// Retrieve the gamepad settings from local storage or use an empty object if none exist.
|
|
||||||
const settings: object = localStorage.hasOwnProperty("settingsKeyboard") ? JSON.parse(localStorage.getItem("settingsKeyboard")) : {};
|
|
||||||
return settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the layout for the active configuration.
|
* Set the layout for the active configuration.
|
||||||
*
|
*
|
||||||
@ -154,26 +133,6 @@ export default class SettingsKeyboardUiHandler extends AbstractSettingsUiUiHandl
|
|||||||
return super.setLayout(activeConfig);
|
return super.setLayout(activeConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Navigate to the left menu tab.
|
|
||||||
*
|
|
||||||
* @returns `true` indicating the navigation was successful.
|
|
||||||
*/
|
|
||||||
navigateMenuLeft(): boolean {
|
|
||||||
this.scene.ui.setMode(Mode.SETTINGS_GAMEPAD);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Navigate to the right menu tab.
|
|
||||||
*
|
|
||||||
* @returns `true` indicating the navigation was successful.
|
|
||||||
*/
|
|
||||||
navigateMenuRight(): boolean {
|
|
||||||
this.scene.ui.setMode(Mode.SETTINGS);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the display of the chosen keyboard layout.
|
* Update the display of the chosen keyboard layout.
|
||||||
*/
|
*/
|
||||||
@ -182,11 +141,11 @@ export default class SettingsKeyboardUiHandler extends AbstractSettingsUiUiHandl
|
|||||||
this.updateBindings();
|
this.updateBindings();
|
||||||
|
|
||||||
// Iterate over the keys in the settingDevice enumeration.
|
// Iterate over the keys in the settingDevice enumeration.
|
||||||
for (const [index, key] of Object.keys(this.settingDevice).entries()) {
|
for (const [index, key] of Object.keys(this.setting).entries()) {
|
||||||
const setting = this.settingDevice[key]; // Get the actual setting value using the key.
|
const setting = this.setting[key]; // Get the actual setting value using the key.
|
||||||
|
|
||||||
// Check if the current setting corresponds to the layout setting.
|
// Check if the current setting corresponds to the layout setting.
|
||||||
if (setting === this.settingDevice.Default_Layout) {
|
if (setting === this.setting.Default_Layout) {
|
||||||
// Iterate over all layouts excluding the 'noGamepads' special case.
|
// Iterate over all layouts excluding the 'noGamepads' special case.
|
||||||
for (const _key of Object.keys(this.layout)) {
|
for (const _key of Object.keys(this.layout)) {
|
||||||
if (_key === "noKeyboard") {
|
if (_key === "noKeyboard") {
|
||||||
@ -217,8 +176,8 @@ export default class SettingsKeyboardUiHandler extends AbstractSettingsUiUiHandl
|
|||||||
* @param cursor - The cursor position to save.
|
* @param cursor - The cursor position to save.
|
||||||
*/
|
*/
|
||||||
saveSettingToLocalStorage(settingName, cursor): void {
|
saveSettingToLocalStorage(settingName, cursor): void {
|
||||||
if (this.settingDevice[settingName] !== this.settingDevice.Default_Layout) {
|
if (this.setting[settingName] !== this.setting.Default_Layout) {
|
||||||
this.scene.gameData.saveKeyboardSetting(settingName, cursor);
|
this.scene.gameData.saveControlSetting(this.device, this.localStoragePropertyName, settingName, this.settingDeviceDefaults, cursor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,345 +1,19 @@
|
|||||||
import BattleScene from "../../battle-scene";
|
import BattleScene from "../../battle-scene";
|
||||||
import {Setting, reloadSettings, settingDefaults, settingOptions} from "../../system/settings";
|
import {Setting, SettingType} from "../../system/settings/settings";
|
||||||
import { hasTouchscreen, isMobile } from "../../touch-controls";
|
|
||||||
import { TextStyle, addTextObject } from "../text";
|
|
||||||
import { Mode } from "../ui";
|
import { Mode } from "../ui";
|
||||||
import UiHandler from "../ui-handler";
|
import AbstractSettingsUiHandler from "./abstract-settings-ui-handler";
|
||||||
import { addWindow } from "../ui-theme";
|
|
||||||
import {Button} from "../../enums/buttons";
|
|
||||||
import {InputsIcons} from "#app/ui/settings/abstract-settings-ui-handler";
|
|
||||||
import NavigationMenu, {NavigationManager} from "#app/ui/settings/navigationMenu";
|
|
||||||
|
|
||||||
export default class SettingsUiHandler extends UiHandler {
|
|
||||||
private settingsContainer: Phaser.GameObjects.Container;
|
|
||||||
private optionsContainer: Phaser.GameObjects.Container;
|
|
||||||
private navigationContainer: NavigationMenu;
|
|
||||||
|
|
||||||
private scrollCursor: integer;
|
|
||||||
|
|
||||||
private optionsBg: Phaser.GameObjects.NineSlice;
|
|
||||||
|
|
||||||
private optionCursors: integer[];
|
|
||||||
|
|
||||||
private settingLabels: Phaser.GameObjects.Text[];
|
|
||||||
private optionValueLabels: Phaser.GameObjects.Text[][];
|
|
||||||
|
|
||||||
protected navigationIcons: InputsIcons;
|
|
||||||
|
|
||||||
private cursorObj: Phaser.GameObjects.NineSlice;
|
|
||||||
|
|
||||||
private reloadRequired: boolean;
|
|
||||||
private reloadI18n: boolean;
|
|
||||||
private rowsToDisplay: number;
|
|
||||||
|
|
||||||
|
export default class SettingsUiHandler extends AbstractSettingsUiHandler {
|
||||||
|
/**
|
||||||
|
* Creates an instance of SettingsGamepadUiHandler.
|
||||||
|
*
|
||||||
|
* @param scene - The BattleScene instance.
|
||||||
|
* @param mode - The UI mode, optional.
|
||||||
|
*/
|
||||||
constructor(scene: BattleScene, mode?: Mode) {
|
constructor(scene: BattleScene, mode?: Mode) {
|
||||||
super(scene, mode);
|
super(scene, mode);
|
||||||
|
this.title = "General";
|
||||||
this.reloadRequired = false;
|
this.settings = Setting.filter(s => s.type === SettingType.GENERAL);
|
||||||
this.reloadI18n = false;
|
this.localStorageKey = "settings";
|
||||||
this.rowsToDisplay = 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 - 20), Phaser.Geom.Rectangle.Contains);
|
|
||||||
|
|
||||||
this.navigationIcons = {};
|
|
||||||
|
|
||||||
this.navigationContainer = new NavigationMenu(this.scene, 0, 0);
|
|
||||||
|
|
||||||
this.optionsBg = addWindow(this.scene, 0, this.navigationContainer.height, (this.scene.game.canvas.width / 6) - 2, (this.scene.game.canvas.height / 6) - 16 - this.navigationContainer.height - 2);
|
|
||||||
this.optionsBg.setOrigin(0, 0);
|
|
||||||
|
|
||||||
const actionsBg = addWindow(this.scene, 0, (this.scene.game.canvas.height / 6) - this.navigationContainer.height, (this.scene.game.canvas.width / 6) - 2, 22);
|
|
||||||
actionsBg.setOrigin(0, 0);
|
|
||||||
|
|
||||||
const iconAction = this.scene.add.sprite(0, 0, "keyboard");
|
|
||||||
iconAction.setOrigin(0, -0.1);
|
|
||||||
iconAction.setPositionRelative(actionsBg, this.navigationContainer.width - 32, 4);
|
|
||||||
this.navigationIcons["BUTTON_ACTION"] = iconAction;
|
|
||||||
|
|
||||||
const actionText = addTextObject(this.scene, 0, 0, "Action", TextStyle.SETTINGS_LABEL);
|
|
||||||
actionText.setOrigin(0, 0.15);
|
|
||||||
actionText.setPositionRelative(iconAction, -actionText.width/6-2, 0);
|
|
||||||
|
|
||||||
const iconCancel = this.scene.add.sprite(0, 0, "keyboard");
|
|
||||||
iconCancel.setOrigin(0, -0.1);
|
|
||||||
iconCancel.setPositionRelative(actionsBg, this.navigationContainer.width - 100, 4);
|
|
||||||
this.navigationIcons["BUTTON_CANCEL"] = iconCancel;
|
|
||||||
|
|
||||||
const cancelText = addTextObject(this.scene, 0, 0, "Cancel", TextStyle.SETTINGS_LABEL);
|
|
||||||
cancelText.setOrigin(0, 0.15);
|
|
||||||
cancelText.setPositionRelative(iconCancel, -cancelText.width/6-2, 0);
|
|
||||||
|
|
||||||
this.optionsContainer = this.scene.add.container(0, 0);
|
|
||||||
|
|
||||||
this.settingLabels = [];
|
|
||||||
this.optionValueLabels = [];
|
|
||||||
|
|
||||||
Object.keys(Setting).forEach((setting, s) => {
|
|
||||||
let settingName = setting.replace(/\_/g, " ");
|
|
||||||
if (reloadSettings.includes(Setting[setting])) {
|
|
||||||
settingName += " (Requires Reload)";
|
|
||||||
}
|
|
||||||
|
|
||||||
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(settingOptions[Setting[setting]].map((option, o) => {
|
|
||||||
const valueLabel = addTextObject(this.scene, 0, 0, option, settingDefaults[Setting[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 (const value of this.optionValueLabels[s]) {
|
|
||||||
value.setPositionRelative(this.settingLabels[s], labelWidth + xOffset, 0);
|
|
||||||
xOffset += value.width / 6 + optionSpacing;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.optionCursors = Object.values(settingDefaults);
|
|
||||||
|
|
||||||
this.settingsContainer.add(this.optionsBg);
|
|
||||||
this.settingsContainer.add(this.navigationContainer);
|
|
||||||
this.settingsContainer.add(actionsBg);
|
|
||||||
this.settingsContainer.add(this.optionsContainer);
|
|
||||||
this.settingsContainer.add(iconAction);
|
|
||||||
this.settingsContainer.add(iconCancel);
|
|
||||||
this.settingsContainer.add(actionText);
|
|
||||||
this.settingsContainer.add(cancelText);
|
|
||||||
|
|
||||||
ui.add(this.settingsContainer);
|
|
||||||
|
|
||||||
this.setCursor(0);
|
|
||||||
this.setScrollCursor(0);
|
|
||||||
|
|
||||||
this.settingsContainer.setVisible(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateBindings(): void {
|
|
||||||
for (const settingName of Object.keys(this.navigationIcons)) {
|
|
||||||
if (settingName === "BUTTON_HOME") {
|
|
||||||
this.navigationIcons[settingName].setTexture("keyboard");
|
|
||||||
this.navigationIcons[settingName].setFrame("HOME.png");
|
|
||||||
this.navigationIcons[settingName].alpha = 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const icon = this.scene.inputController?.getIconForLatestInputRecorded(settingName);
|
|
||||||
if (icon) {
|
|
||||||
const type = this.scene.inputController?.getLastSourceType();
|
|
||||||
this.navigationIcons[settingName].setTexture(type);
|
|
||||||
this.navigationIcons[settingName].setFrame(icon);
|
|
||||||
this.navigationIcons[settingName].alpha = 1;
|
|
||||||
} else {
|
|
||||||
this.navigationIcons[settingName].alpha = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NavigationManager.getInstance().updateIcons();
|
|
||||||
}
|
|
||||||
|
|
||||||
show(args: any[]): boolean {
|
|
||||||
super.show(args);
|
|
||||||
this.updateBindings();
|
|
||||||
|
|
||||||
const settings: object = localStorage.hasOwnProperty("settings") ? JSON.parse(localStorage.getItem("settings")) : {};
|
|
||||||
|
|
||||||
Object.keys(settingDefaults).forEach((setting, s) => this.setOptionCursor(s, settings.hasOwnProperty(setting) ? settings[setting] : settingDefaults[setting]));
|
|
||||||
|
|
||||||
this.settingsContainer.setVisible(true);
|
|
||||||
this.setCursor(0);
|
|
||||||
|
|
||||||
this.getUi().moveTo(this.settingsContainer, this.getUi().length - 1);
|
|
||||||
|
|
||||||
this.getUi().hideTooltip();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Processes input from a specified button.
|
|
||||||
* This method handles navigation through a UI menu, including movement through menu items
|
|
||||||
* and handling special actions like cancellation. Each button press may adjust the cursor
|
|
||||||
* position or the menu scroll, and plays a sound effect if the action was successful.
|
|
||||||
*
|
|
||||||
* @param button - The button pressed by the user.
|
|
||||||
* @returns `true` if the action associated with the button was successfully processed, `false` otherwise.
|
|
||||||
*/
|
|
||||||
processInput(button: Button): boolean {
|
|
||||||
const ui = this.getUi();
|
|
||||||
// Defines the maximum number of rows that can be displayed on the screen.
|
|
||||||
|
|
||||||
let success = false;
|
|
||||||
|
|
||||||
if (button === Button.CANCEL) {
|
|
||||||
success = true;
|
|
||||||
NavigationManager.getInstance().reset();
|
|
||||||
// Reverts UI to its previous state on cancel.
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// When at the top of the menu and pressing UP, move to the bottommost item.
|
|
||||||
// First, set the cursor to the last visible element, preparing for the scroll to the end.
|
|
||||||
const successA = this.setCursor(this.rowsToDisplay - 1);
|
|
||||||
// Then, adjust the scroll to display the bottommost elements of the menu.
|
|
||||||
const successB = this.setScrollCursor(this.optionValueLabels.length - this.rowsToDisplay);
|
|
||||||
success = successA && successB; // success is just there to play the little validation sound effect
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Button.DOWN:
|
|
||||||
if (cursor < this.optionValueLabels.length - 1) {
|
|
||||||
if (this.cursor < this.rowsToDisplay - 1) {// if the visual cursor is in the frame of 0 to 8
|
|
||||||
success = this.setCursor(this.cursor + 1);
|
|
||||||
} else if (this.scrollCursor < this.optionValueLabels.length - this.rowsToDisplay) {
|
|
||||||
success = this.setScrollCursor(this.scrollCursor + 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// When at the bottom of the menu and pressing DOWN, move to the topmost item.
|
|
||||||
// First, set the cursor to the first visible element, resetting the scroll to the top.
|
|
||||||
const successA = this.setCursor(0);
|
|
||||||
// Then, reset the scroll to start from the first element of the menu.
|
|
||||||
const successB = this.setScrollCursor(0);
|
|
||||||
success = successA && successB; // Indicates a successful cursor and scroll adjustment.
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Button.LEFT:
|
|
||||||
if (this.optionCursors[cursor]) {// Moves the option cursor left, if possible.
|
|
||||||
success = this.setOptionCursor(cursor, this.optionCursors[cursor] - 1, true);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Button.RIGHT:
|
|
||||||
// Moves the option cursor right, if possible.
|
|
||||||
if (this.optionCursors[cursor] < this.optionValueLabels[cursor].length - 1) {
|
|
||||||
success = this.setOptionCursor(cursor, this.optionCursors[cursor] + 1, true);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Button.CYCLE_FORM:
|
|
||||||
case Button.CYCLE_SHINY:
|
|
||||||
success = this.navigationContainer.navigate(button);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Plays a select sound effect if an action was successfully processed.
|
|
||||||
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 = Setting[Object.keys(Setting)[settingIndex]];
|
|
||||||
|
|
||||||
if (setting === Setting.Touch_Controls && cursor && hasTouchscreen() && isMobile()) {
|
|
||||||
this.getUi().playError();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
if (reloadSettings.includes(setting)) {
|
|
||||||
this.reloadRequired = true;
|
|
||||||
if (setting === Setting.Language) {
|
|
||||||
this.reloadI18n = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 + this.rowsToDisplay;
|
|
||||||
this.settingLabels[s].setVisible(visible);
|
|
||||||
for (const 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
19
src/ui/ui.ts
@ -42,6 +42,7 @@ import {PlayerGender} from "#app/system/game-data";
|
|||||||
import GamepadBindingUiHandler from "./settings/gamepad-binding-ui-handler";
|
import GamepadBindingUiHandler from "./settings/gamepad-binding-ui-handler";
|
||||||
import SettingsKeyboardUiHandler from "#app/ui/settings/settings-keyboard-ui-handler";
|
import SettingsKeyboardUiHandler from "#app/ui/settings/settings-keyboard-ui-handler";
|
||||||
import KeyboardBindingUiHandler from "#app/ui/settings/keyboard-binding-ui-handler";
|
import KeyboardBindingUiHandler from "#app/ui/settings/keyboard-binding-ui-handler";
|
||||||
|
import SettingsAccessibilityUiHandler from "./settings/settings-accessiblity-ui-handler";
|
||||||
|
|
||||||
export enum Mode {
|
export enum Mode {
|
||||||
MESSAGE,
|
MESSAGE,
|
||||||
@ -62,6 +63,7 @@ export enum Mode {
|
|||||||
MENU,
|
MENU,
|
||||||
MENU_OPTION_SELECT,
|
MENU_OPTION_SELECT,
|
||||||
SETTINGS,
|
SETTINGS,
|
||||||
|
SETTINGS_ACCESSIBILITY,
|
||||||
SETTINGS_GAMEPAD,
|
SETTINGS_GAMEPAD,
|
||||||
GAMEPAD_BINDING,
|
GAMEPAD_BINDING,
|
||||||
SETTINGS_KEYBOARD,
|
SETTINGS_KEYBOARD,
|
||||||
@ -99,6 +101,7 @@ const noTransitionModes = [
|
|||||||
Mode.GAMEPAD_BINDING,
|
Mode.GAMEPAD_BINDING,
|
||||||
Mode.KEYBOARD_BINDING,
|
Mode.KEYBOARD_BINDING,
|
||||||
Mode.SETTINGS,
|
Mode.SETTINGS,
|
||||||
|
Mode.SETTINGS_ACCESSIBILITY,
|
||||||
Mode.SETTINGS_GAMEPAD,
|
Mode.SETTINGS_GAMEPAD,
|
||||||
Mode.SETTINGS_KEYBOARD,
|
Mode.SETTINGS_KEYBOARD,
|
||||||
Mode.ACHIEVEMENTS,
|
Mode.ACHIEVEMENTS,
|
||||||
@ -151,6 +154,7 @@ export default class UI extends Phaser.GameObjects.Container {
|
|||||||
new MenuUiHandler(scene),
|
new MenuUiHandler(scene),
|
||||||
new OptionSelectUiHandler(scene, Mode.MENU_OPTION_SELECT),
|
new OptionSelectUiHandler(scene, Mode.MENU_OPTION_SELECT),
|
||||||
new SettingsUiHandler(scene),
|
new SettingsUiHandler(scene),
|
||||||
|
new SettingsAccessibilityUiHandler(scene),
|
||||||
new SettingsGamepadUiHandler(scene),
|
new SettingsGamepadUiHandler(scene),
|
||||||
new GamepadBindingUiHandler(scene),
|
new GamepadBindingUiHandler(scene),
|
||||||
new SettingsKeyboardUiHandler(scene),
|
new SettingsKeyboardUiHandler(scene),
|
||||||
@ -218,6 +222,21 @@ export default class UI extends Phaser.GameObjects.Container {
|
|||||||
return this.handlers[Mode.MESSAGE] as BattleMessageUiHandler;
|
return this.handlers[Mode.MESSAGE] as BattleMessageUiHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processInfoButton(pressed: boolean) {
|
||||||
|
if (this.overlayActive) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const battleScene = this.scene as BattleScene;
|
||||||
|
if ([Mode.CONFIRM, Mode.COMMAND, Mode.FIGHT, Mode.MESSAGE].includes(this.mode)) {
|
||||||
|
battleScene?.processInfoButton(pressed);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
battleScene?.processInfoButton(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
processInput(button: Button): boolean {
|
processInput(button: Button): boolean {
|
||||||
if (this.overlayActive) {
|
if (this.overlayActive) {
|
||||||
return false;
|
return false;
|
||||||
|