mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-07-05 07:52:17 +02:00
Compare commits
8 Commits
cc161ecaf2
...
6030588351
Author | SHA1 | Date | |
---|---|---|---|
|
6030588351 | ||
|
a3f7d823e0 | ||
|
69da96d543 | ||
|
763d2bfeeb | ||
|
ff0e4fbdf0 | ||
|
145a79f8ef | ||
|
1abc591318 | ||
|
12bd22f2ca |
@ -115,6 +115,8 @@ export default class BattleScene extends SceneBase {
|
||||
public musicPreference: integer = 0;
|
||||
public moveAnimations: boolean = true;
|
||||
public expGainsSpeed: integer = 0;
|
||||
public skipSeenDialogues: boolean = false;
|
||||
|
||||
/**
|
||||
* Defines the experience gain display mode.
|
||||
*
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 = {
|
||||
padID: "default",
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {SettingGamepad} from "../../system/settings-gamepad";
|
||||
import {SettingGamepad} from "../../system/settings/settings-gamepad";
|
||||
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";
|
||||
|
||||
/**
|
||||
|
@ -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";
|
||||
|
||||
/**
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {SettingGamepad} from "../../system/settings-gamepad";
|
||||
import {SettingGamepad} from "../../system/settings/settings-gamepad";
|
||||
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";
|
||||
|
||||
/**
|
||||
|
@ -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.
|
||||
* 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 {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
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)
|
||||
.partial(),
|
||||
new AttackMove(Moves.POLLEN_PUFF, Type.BUG, MoveCategory.SPECIAL, 90, 100, 15, -1, 0, 7)
|
||||
.ballBombMove()
|
||||
.partial(),
|
||||
.attr(StatusCategoryOnAllyAttr)
|
||||
.attr(HealOnAllyAttr, 0.5, true, false)
|
||||
.ballBombMove(),
|
||||
new AttackMove(Moves.ANCHOR_SHOT, Type.STEEL, MoveCategory.PHYSICAL, 80, 100, 20, -1, 0, 7)
|
||||
.attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, false, 1),
|
||||
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 speciesPool = Object.keys(speciesStarters)
|
||||
let speciesPool = Object.keys(speciesStarters)
|
||||
.filter(s => speciesStarters[s] >= minStarterValue && speciesStarters[s] <= maxStarterValue)
|
||||
.map(s => parseInt(s) as Species)
|
||||
.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
|
||||
* 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);
|
||||
|
||||
ret = this.scene.addPlayerPokemon(pokemonSpecies, 1, undefined, undefined, undefined, false);
|
||||
|
@ -19,8 +19,8 @@ import {
|
||||
getIconForLatestInput, swap,
|
||||
} from "#app/configs/inputs/configHandler";
|
||||
import BattleScene from "./battle-scene";
|
||||
import {SettingGamepad} from "#app/system/settings-gamepad";
|
||||
import {SettingKeyboard} from "#app/system/settings-keyboard";
|
||||
import {SettingGamepad} from "#app/system/settings/settings-gamepad.js";
|
||||
import {SettingKeyboard} from "#app/system/settings/settings-keyboard";
|
||||
|
||||
export interface DeviceMapping {
|
||||
[key: string]: number;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {DialogueTranslationEntries, SimpleTranslationEntries} from "#app/plugins/i18n";
|
||||
import { DialogueTranslationEntries, SimpleTranslationEntries } from "#app/plugins/i18n";
|
||||
|
||||
// Dialogue of the NPCs in the game when the player character is male (or unset)
|
||||
export const PGMdialogue: DialogueTranslationEntries = {
|
||||
@ -2398,12 +2398,12 @@ export const PGMdoubleBattleDialogue: DialogueTranslationEntries = {
|
||||
},
|
||||
"steven_wallace_double": {
|
||||
"encounter": {
|
||||
1: `Steven: Do you have any rare pokémon?
|
||||
$Wallace: Steven... We are here for a battle, not to show off our pokémon.
|
||||
1: `Steven: Do you have any rare Pokémon?
|
||||
$Wallace: Steven... We are here for a battle, not to show off our Pokémon.
|
||||
$Steven: Oh... I see... Let's go then!`,
|
||||
},
|
||||
"victory": {
|
||||
1: `Steven: Now that we are done with the battle, let's show off our pokémon!
|
||||
1: `Steven: Now that we are done with the battle, let's show off our Pokémon!
|
||||
$Wallace: Steven...`,
|
||||
},
|
||||
},
|
||||
|
@ -29,8 +29,8 @@ export const tutorial: SimpleTranslationEntries = {
|
||||
$종류는 소모품, 포켓몬의 지닌 도구, 영구적 패시브 아이템에 이르기까지 다양합니다.
|
||||
$대부분의 소모되지 않는 도구는 효과가 누적됩니다.
|
||||
$진화용과 같은 일부분의 아이템은 사용할 수 있는 경우에만 등장합니다.
|
||||
$지닌 도구 건내주기 기능을 사용해 포켓몬끼리 도구를 옮겨 지닐 수도 있습니다.
|
||||
$지닌 도구가 있다면 아이템 선택 화면 오른쪽 하단에 건내주기 기능이 표시됩니다.
|
||||
$지닌 도구 건네주기 기능을 사용해 포켓몬끼리 도구를 옮겨 지닐 수도 있습니다.
|
||||
$지닌 도구가 있다면 아이템 선택 화면 오른쪽 하단에 건네주기 기능이 표시됩니다.
|
||||
$돈으로 소모품을 구입할 수도 있으며, 웨이브 진행에 따라 구입 가능한 종류가 늘어납니다.
|
||||
$아이템을 선택하면 다음 웨이브로 넘어가므로, 소모품 구입을 먼저 해 주세요.`,
|
||||
|
||||
|
@ -3,5 +3,5 @@ import { SimpleTranslationEntries } from "#app/plugins/i18n";
|
||||
export const abilityTriggers: SimpleTranslationEntries = {
|
||||
"blockRecoilDamage" : "{{abilityName}} de {{pokemonName}}\nprotegeu-o do dano de recuo!",
|
||||
"badDreams": "{{pokemonName}} está tendo pesadelos!",
|
||||
"windPowerCharged": "Being hit by {{moveName}} charged {{pokemonName}} with power!"
|
||||
"windPowerCharged": "Ser atingido por {{moveName}} carregou {{pokemonName}} com poder!"
|
||||
} as const;
|
||||
|
@ -48,7 +48,7 @@ export const battle: SimpleTranslationEntries = {
|
||||
"noEscapeForce": "Uma força misteriosa\nte impede de fugir.",
|
||||
"noEscapeTrainer": "Não se pode fugir de\nbatalhas contra treinadores!",
|
||||
"noEscapePokemon": "O movimento {{moveName}} de {{pokemonName}} te impede de fugir!",
|
||||
"runAwaySuccess": "Você fugiu com sucesso",
|
||||
"runAwaySuccess": "Você fugiu com sucesso.",
|
||||
"runAwayCannotEscape": "Você nao conseguiu fugir!",
|
||||
"escapeVerbSwitch": "trocar",
|
||||
"escapeVerbFlee": "fugir",
|
||||
@ -56,6 +56,6 @@ export const battle: SimpleTranslationEntries = {
|
||||
"skipItemQuestion": "Tem certeza de que não quer escolher um item?",
|
||||
"eggHatching": "Opa?",
|
||||
"ivScannerUseQuestion": "Quer usar o Scanner de IVs em {{pokemonName}}?",
|
||||
"drainMessage": "{{pokemonName}} had its\nenergy drained!",
|
||||
"regainHealth": "{{pokemonName}} regained\nhealth!"
|
||||
"drainMessage": "{{pokemonName}} teve sua\nenergia drenada!",
|
||||
"regainHealth": "{{pokemonName}} recuperou\npontos de saúde!"
|
||||
} as const;
|
||||
|
@ -4,8 +4,8 @@ export const biome: SimpleTranslationEntries = {
|
||||
"unknownLocation": "Em algum lugar do qual você não se lembra",
|
||||
"TOWN": "Cidade",
|
||||
"PLAINS": "Planície",
|
||||
"GRASS": "Campo de Grama",
|
||||
"TALL_GRASS": "Campo de Grama Alta",
|
||||
"GRASS": "Grama",
|
||||
"TALL_GRASS": "Grama Alta",
|
||||
"METROPOLIS": "Metrópole",
|
||||
"FOREST": "Floresta",
|
||||
"SEA": "Mar",
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,44 +1,44 @@
|
||||
import { SimpleTranslationEntries } from "#app/plugins/i18n";
|
||||
|
||||
export const gameStatsUiHandler: SimpleTranslationEntries = {
|
||||
"stats": "Stats",
|
||||
"playTime": "Play Time",
|
||||
"totalBattles": "Total Battles",
|
||||
"starters": "Starters",
|
||||
"shinyStarters": "Shiny Starters",
|
||||
"speciesSeen": "Species Seen",
|
||||
"speciesCaught": "Species Caught",
|
||||
"ribbonsOwned": "Ribbons Owned",
|
||||
"classicRuns": "Classic Runs",
|
||||
"classicWins": "Classic Wins",
|
||||
"dailyRunAttempts": "Daily Run Attempts",
|
||||
"dailyRunWins": "Daily Run Wins",
|
||||
"endlessRuns": "Endless Runs",
|
||||
"highestWaveEndless": "Highest Wave (Endless)",
|
||||
"highestMoney": "Highest Money",
|
||||
"highestDamage": "Highest Damage",
|
||||
"highestHPHealed": "Highest HP Healed",
|
||||
"pokemonEncountered": "Pokémon Encountered",
|
||||
"pokemonDefeated": "Pokémon Defeated",
|
||||
"pokemonCaught": "Pokémon Caught",
|
||||
"eggsHatched": "Eggs Hatched",
|
||||
"subLegendsSeen": "Sub-Legends Seen",
|
||||
"subLegendsCaught": "Sub-Legends Caught",
|
||||
"subLegendsHatched": "Sub-Legends Hatched",
|
||||
"legendsSeen": "Legends Seen",
|
||||
"legendsCaught": "Legends Caught",
|
||||
"legendsHatched": "Legends Hatched",
|
||||
"mythicalsSeen": "Mythicals Seen",
|
||||
"mythicalsCaught": "Mythicals Caught",
|
||||
"mythicalsHatched": "Mythicals Hatched",
|
||||
"shiniesSeen": "Shinies Seen",
|
||||
"shiniesCaught": "Shinies Caught",
|
||||
"shiniesHatched": "Shinies Hatched",
|
||||
"pokemonFused": "Pokémon Fused",
|
||||
"trainersDefeated": "Trainers Defeated",
|
||||
"eggsPulled": "Eggs Pulled",
|
||||
"rareEggsPulled": "Rare Eggs Pulled",
|
||||
"epicEggsPulled": "Epic Eggs Pulled",
|
||||
"legendaryEggsPulled": "Legendary Eggs Pulled",
|
||||
"manaphyEggsPulled": "Manaphy Eggs Pulled",
|
||||
"stats": "Estatísticas",
|
||||
"playTime": "Tempo de Jogo",
|
||||
"totalBattles": "Total de Batalhas",
|
||||
"starters": "Iniciais",
|
||||
"shinyStarters": "Iniciais Shiny",
|
||||
"speciesSeen": "Espécies Vistas",
|
||||
"speciesCaught": "Capturadas",
|
||||
"ribbonsOwned": "Fitas Obtidas",
|
||||
"classicRuns": "Jogos Clássicos",
|
||||
"classicWins": "Vitórias Clássicas",
|
||||
"dailyRunAttempts": "Jogos de Desafio Diário",
|
||||
"dailyRunWins": "Vitórias de Desafio Diário",
|
||||
"endlessRuns": "Jogos Infinitos",
|
||||
"highestWaveEndless": "Maior Onda (Infinito)",
|
||||
"highestMoney": "Maior Dinheiro",
|
||||
"highestDamage": "Maior Dano",
|
||||
"highestHPHealed": "Maior PS Curado",
|
||||
"pokemonEncountered": "Pokémon Encontrados",
|
||||
"pokemonDefeated": "Pokémon Derrotados",
|
||||
"pokemonCaught": "Pokémon Capturados",
|
||||
"eggsHatched": "Ovos Chocados",
|
||||
"subLegendsSeen": "Sub-Lendários Vistos",
|
||||
"subLegendsCaught": "Sub-Lend. Capturados",
|
||||
"subLegendsHatched": "Sub-Lendários Chocados",
|
||||
"legendsSeen": "Lendários Vistos",
|
||||
"legendsCaught": "Lendários Capturados",
|
||||
"legendsHatched": "Legendários Chocados",
|
||||
"mythicalsSeen": "Míticos Vistos",
|
||||
"mythicalsCaught": "Míticos Capturados",
|
||||
"mythicalsHatched": "Míticos Chocados",
|
||||
"shiniesSeen": "Shinies Vistos",
|
||||
"shiniesCaught": "Shinies Capturados",
|
||||
"shiniesHatched": "Shinies Chocados",
|
||||
"pokemonFused": "Pokémon Fundidos",
|
||||
"trainersDefeated": "Treinadores Derrotados",
|
||||
"eggsPulled": "Ovos Ganhos",
|
||||
"rareEggsPulled": "Ovos Raros Ganhos",
|
||||
"epicEggsPulled": "Ovos Épicos Ganhos",
|
||||
"legendaryEggsPulled": "Ovos Lendários Ganhos",
|
||||
"manaphyEggsPulled": "Ovos de Manaphy Ganhos",
|
||||
} as const;
|
||||
|
@ -209,8 +209,8 @@ export const modifierType: ModifierTypeTranslationEntries = {
|
||||
"LEFTOVERS": { name: "Sobras", description: "Cura 1/16 dos PS máximos de um Pokémon a cada turno" },
|
||||
"SHELL_BELL": { name: "Concha-Sino", description: "Cura 1/8 do dano causado por um Pokémon" },
|
||||
|
||||
"TOXIC_ORB": { name: "Toxic Orb", description: "It's a bizarre orb that exudes toxins when touched and will badly poison the holder during battle" },
|
||||
"FLAME_ORB": { name: "Flame Orb", description: "It's a bizarre orb that gives off heat when touched and will affect the holder with a burn during battle" },
|
||||
"TOXIC_ORB": { name: "Esfera Tóxica", description: "Uma esfera estranha que exala toxinas quando tocada e envenena seriamente quem a segurar" },
|
||||
"FLAME_ORB": { name: "Esfera da Chama", description: "Uma esfera estranha que aquece quando tocada e queima quem a segurar" },
|
||||
|
||||
"BATON": { name: "Bastão", description: "Permite passar mudanças de atributo ao trocar Pokémon, ignorando armadilhas" },
|
||||
|
||||
|
@ -1943,7 +1943,7 @@ export const move: MoveTranslationEntries = {
|
||||
},
|
||||
"electroBall": {
|
||||
name: "Electro Ball",
|
||||
effect: "O usuário arremessa uma orbe elétrica no alvo. Quanto mais rápido for o usuário comparado ao alvo, maior será o poder do movimento."
|
||||
effect: "O usuário arremessa uma esfera elétrica no alvo. Quanto mais rápido for o usuário comparado ao alvo, maior será o poder do movimento."
|
||||
},
|
||||
"soak": {
|
||||
name: "Soak",
|
||||
@ -2495,7 +2495,7 @@ export const move: MoveTranslationEntries = {
|
||||
},
|
||||
"allOutPummelingPhysical": {
|
||||
name: "All-Out Pummeling",
|
||||
effect: "Utilizando o Poder Z, o usuário cria e arremessa um orbe de energia no alvo com força total. Seu poder varia dependendo do movimento original."
|
||||
effect: "Utilizando o Poder Z, o usuário cria e arremessa uma esfera de energia no alvo com força total. Seu poder varia dependendo do movimento original."
|
||||
},
|
||||
"allOutPummelingSpecial": {
|
||||
name: "All-Out Pummeling",
|
||||
|
@ -48,7 +48,7 @@ export const trainerClasses: SimpleTranslationEntries = {
|
||||
"depot_agent": "Ferroviário",
|
||||
"doctor": "Doutor",
|
||||
"doctor_female": "Doutora",
|
||||
"firebreather": "Firebreather",
|
||||
"firebreather": "Cospe-Fogo",
|
||||
"fishermen": "Pescador",
|
||||
"fishermen_female": "Pescadora",
|
||||
"gentleman": "Cavalheiro",
|
||||
|
@ -48,7 +48,7 @@ import { addPokeballCaptureStars, addPokeballOpenParticles } from "./field/anims
|
||||
import { SpeciesFormChangeActiveTrigger, SpeciesFormChangeManualTrigger, SpeciesFormChangeMoveLearnedTrigger, SpeciesFormChangePostMoveTrigger, SpeciesFormChangePreMoveTrigger } from "./data/pokemon-forms";
|
||||
import { battleSpecDialogue, getCharVariantFromDialogue, miscDialogue } from "./data/dialogue";
|
||||
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 { TerrainType } from "./data/terrain";
|
||||
import { OptionSelectConfig, OptionSelectItem } from "./ui/abstact-option-select-ui-handler";
|
||||
@ -477,7 +477,7 @@ export class SelectGenderPhase extends Phase {
|
||||
label: i18next.t("menu:boy"),
|
||||
handler: () => {
|
||||
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());
|
||||
return true;
|
||||
}
|
||||
@ -486,7 +486,7 @@ export class SelectGenderPhase extends Phase {
|
||||
label: i18next.t("menu:girl"),
|
||||
handler: () => {
|
||||
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());
|
||||
return true;
|
||||
}
|
||||
@ -959,14 +959,15 @@ export class EncounterPhase extends BattlePhase {
|
||||
if (!encounterMessages?.length) {
|
||||
doSummon();
|
||||
} else {
|
||||
let message: string;
|
||||
this.scene.executeWithSeedOffset(() => message = Utils.randSeedItem(encounterMessages), this.scene.currentBattle.waveIndex);
|
||||
|
||||
const showDialogueAndSummon = () => {
|
||||
let message: string;
|
||||
this.scene.executeWithSeedOffset(() => message = Utils.randSeedItem(encounterMessages), this.scene.currentBattle.waveIndex);
|
||||
this.scene.ui.showDialogue(message, trainer.getName(TrainerSlot.NONE,true), null, () => {
|
||||
this.scene.charSprite.hide().then(() => this.scene.hideFieldOverlay(250).then(() => doSummon()));
|
||||
});
|
||||
};
|
||||
if (this.scene.currentBattle.trainer.config.hasCharSprite) {
|
||||
if (this.scene.currentBattle.trainer.config.hasCharSprite && !this.scene.ui.shouldSkipDialogue(message)) {
|
||||
this.scene.showFieldOverlay(500).then(() => this.scene.charSprite.showCharacter(trainer.getKey(), getCharVariantFromDialogue(encounterMessages[0])).then(() => showDialogueAndSummon()));
|
||||
} else {
|
||||
showDialogueAndSummon();
|
||||
@ -3789,21 +3790,18 @@ export class TrainerVictoryPhase extends BattlePhase {
|
||||
|
||||
this.scene.ui.showText(i18next.t("battle:trainerDefeated", { trainerName: this.scene.currentBattle.trainer.getName(TrainerSlot.NONE, true) }), null, () => {
|
||||
const victoryMessages = this.scene.currentBattle.trainer.getVictoryMessages();
|
||||
const showMessage = () => {
|
||||
let message: string;
|
||||
this.scene.executeWithSeedOffset(() => message = Utils.randSeedItem(victoryMessages), this.scene.currentBattle.waveIndex);
|
||||
const messagePages = message.split(/\$/g).map(m => m.trim());
|
||||
let message: string;
|
||||
this.scene.executeWithSeedOffset(() => message = Utils.randSeedItem(victoryMessages), this.scene.currentBattle.waveIndex);
|
||||
|
||||
for (let p = messagePages.length - 1; p >= 0; p--) {
|
||||
const originalFunc = showMessageOrEnd;
|
||||
showMessageOrEnd = () => this.scene.ui.showDialogue(messagePages[p], this.scene.currentBattle.trainer.getName(), null, originalFunc);
|
||||
}
|
||||
const showMessage = () => {
|
||||
const originalFunc = showMessageOrEnd;
|
||||
showMessageOrEnd = () => this.scene.ui.showDialogue(message, this.scene.currentBattle.trainer.getName(), null, originalFunc);
|
||||
|
||||
showMessageOrEnd();
|
||||
};
|
||||
let showMessageOrEnd = () => this.end();
|
||||
if (victoryMessages?.length) {
|
||||
if (this.scene.currentBattle.trainer.config.hasCharSprite) {
|
||||
if (this.scene.currentBattle.trainer.config.hasCharSprite && !this.scene.ui.shouldSkipDialogue(message)) {
|
||||
const originalFunc = showMessageOrEnd;
|
||||
showMessageOrEnd = () => this.scene.charSprite.hide().then(() => this.scene.hideFieldOverlay(250).then(() => originalFunc()));
|
||||
this.scene.showFieldOverlay(500).then(() => this.scene.charSprite.showCharacter(this.scene.currentBattle.trainer.getKey(), getCharVariantFromDialogue(victoryMessages[0])).then(() => showMessage()));
|
||||
@ -4014,19 +4012,27 @@ export class GameOverPhase extends BattlePhase {
|
||||
};
|
||||
|
||||
if (this.victory && this.scene.gameMode.isClassic) {
|
||||
this.scene.ui.fadeIn(500).then(() => {
|
||||
this.scene.charSprite.showCharacter(`rival_${this.scene.gameData.gender === PlayerGender.FEMALE ? "m" : "f"}`, getCharVariantFromDialogue(miscDialogue.ending[this.scene.gameData.gender === PlayerGender.FEMALE ? 0 : 1])).then(() => {
|
||||
this.scene.ui.showDialogue(miscDialogue.ending[this.scene.gameData.gender === PlayerGender.FEMALE ? 0 : 1], this.scene.gameData.gender === PlayerGender.FEMALE ? trainerConfigs[TrainerType.RIVAL].name : trainerConfigs[TrainerType.RIVAL].nameFemale, null, () => {
|
||||
this.scene.ui.fadeOut(500).then(() => {
|
||||
this.scene.charSprite.hide().then(() => {
|
||||
const endCardPhase = new EndCardPhase(this.scene);
|
||||
this.scene.unshiftPhase(endCardPhase);
|
||||
clear(endCardPhase);
|
||||
const message = miscDialogue.ending[this.scene.gameData.gender === PlayerGender.FEMALE ? 0 : 1];
|
||||
|
||||
if (!this.scene.ui.shouldSkipDialogue(message)) {
|
||||
this.scene.ui.fadeIn(500).then(() => {
|
||||
this.scene.charSprite.showCharacter(`rival_${this.scene.gameData.gender === PlayerGender.FEMALE ? "m" : "f"}`, getCharVariantFromDialogue(miscDialogue.ending[this.scene.gameData.gender === PlayerGender.FEMALE ? 0 : 1])).then(() => {
|
||||
this.scene.ui.showDialogue(message, this.scene.gameData.gender === PlayerGender.FEMALE ? trainerConfigs[TrainerType.RIVAL].name : trainerConfigs[TrainerType.RIVAL].nameFemale, null, () => {
|
||||
this.scene.ui.fadeOut(500).then(() => {
|
||||
this.scene.charSprite.hide().then(() => {
|
||||
const endCardPhase = new EndCardPhase(this.scene);
|
||||
this.scene.unshiftPhase(endCardPhase);
|
||||
clear(endCardPhase);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
const endCardPhase = new EndCardPhase(this.scene);
|
||||
this.scene.unshiftPhase(endCardPhase);
|
||||
clear(endCardPhase);
|
||||
}
|
||||
} else {
|
||||
clear();
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ import { GameModes, gameModes } from "../game-mode";
|
||||
import { BattleType } from "../battle";
|
||||
import TrainerData from "./trainer-data";
|
||||
import { trainerConfigs } from "../data/trainer-config";
|
||||
import { Setting, setSetting, settingDefaults } from "./settings";
|
||||
import { SettingKeys, resetSettings, setSetting } from "./settings/settings";
|
||||
import { achvs } from "./achv";
|
||||
import EggData from "./egg-data";
|
||||
import { Egg } from "../data/egg";
|
||||
@ -30,9 +30,10 @@ import { allMoves } from "../data/move";
|
||||
import { TrainerVariant } from "../field/trainer";
|
||||
import { OutdatedPhase, ReloadSessionPhase } from "#app/phases";
|
||||
import { Variant, variantData } from "#app/data/variant";
|
||||
import {setSettingGamepad, SettingGamepad, settingGamepadDefaults} from "./settings-gamepad";
|
||||
import {setSettingKeyboard, SettingKeyboard, settingKeyboardDefaults} from "#app/system/settings-keyboard";
|
||||
import {setSettingGamepad, SettingGamepad, settingGamepadDefaults} from "./settings/settings-gamepad";
|
||||
import {setSettingKeyboard, SettingKeyboard} from "#app/system/settings/settings-keyboard";
|
||||
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
|
||||
|
||||
@ -40,7 +41,8 @@ export enum GameDataType {
|
||||
SYSTEM,
|
||||
SESSION,
|
||||
SETTINGS,
|
||||
TUTORIALS
|
||||
TUTORIALS,
|
||||
SEEN_DIALOGUES
|
||||
}
|
||||
|
||||
export enum PlayerGender {
|
||||
@ -68,6 +70,8 @@ export function getDataTypeKey(dataType: GameDataType, slotId: integer = 0): str
|
||||
return "settings";
|
||||
case GameDataType.TUTORIALS:
|
||||
return "tutorials";
|
||||
case GameDataType.SEEN_DIALOGUES:
|
||||
return "seenDialogues";
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,6 +101,8 @@ interface SystemSaveData {
|
||||
eggs: EggData[];
|
||||
gameVersion: string;
|
||||
timestamp: integer;
|
||||
eggPity: integer[];
|
||||
unlockPity: integer[];
|
||||
}
|
||||
|
||||
export interface SessionSaveData {
|
||||
@ -201,6 +207,10 @@ export interface TutorialFlags {
|
||||
[key: string]: boolean
|
||||
}
|
||||
|
||||
export interface SeenDialogues {
|
||||
[key: string]: boolean;
|
||||
}
|
||||
|
||||
const systemShortKeys = {
|
||||
seenAttr: "$sa",
|
||||
caughtAttr: "$ca",
|
||||
@ -241,6 +251,8 @@ export class GameData {
|
||||
public voucherUnlocks: VoucherUnlocks;
|
||||
public voucherCounts: VoucherCounts;
|
||||
public eggs: Egg[];
|
||||
public eggPity: integer[];
|
||||
public unlockPity: integer[];
|
||||
|
||||
constructor(scene: BattleScene) {
|
||||
this.scene = scene;
|
||||
@ -265,6 +277,8 @@ export class GameData {
|
||||
[VoucherType.GOLDEN]: 0
|
||||
};
|
||||
this.eggs = [];
|
||||
this.eggPity = [0, 0, 0, 0];
|
||||
this.unlockPity = [0, 0, 0, 0];
|
||||
this.initDexData();
|
||||
this.initStarterData();
|
||||
}
|
||||
@ -283,7 +297,9 @@ export class GameData {
|
||||
voucherCounts: this.voucherCounts,
|
||||
eggs: this.eggs.map(e => new EggData(e)),
|
||||
gameVersion: this.scene.game.config.gameVersion,
|
||||
timestamp: new Date().getTime()
|
||||
timestamp: new Date().getTime(),
|
||||
eggPity: this.eggPity.slice(0),
|
||||
unlockPity: this.unlockPity.slice(0)
|
||||
};
|
||||
}
|
||||
|
||||
@ -387,7 +403,7 @@ export class GameData {
|
||||
|
||||
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;
|
||||
|
||||
@ -466,6 +482,9 @@ export class GameData {
|
||||
? 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.consolidateDexData(this.dexData);
|
||||
this.defaultDexData = null;
|
||||
@ -550,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 = {};
|
||||
if (localStorage.hasOwnProperty("settings")) {
|
||||
settings = JSON.parse(localStorage.getItem("settings"));
|
||||
}
|
||||
|
||||
setSetting(this.scene, setting as Setting, valueIndex);
|
||||
setSetting(this.scene, setting, valueIndex);
|
||||
|
||||
Object.keys(settingDefaults).forEach(s => {
|
||||
if (s === setting) {
|
||||
settings[s] = valueIndex;
|
||||
}
|
||||
});
|
||||
settings[setting] = valueIndex;
|
||||
|
||||
localStorage.setItem("settings", JSON.stringify(settings));
|
||||
|
||||
@ -635,61 +656,36 @@ export class GameData {
|
||||
* 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 saveGamepadSetting(setting: SettingGamepad, valueIndex: integer): boolean {
|
||||
let settingsGamepad: object = {}; // Initialize an empty object to hold the gamepad settings
|
||||
public saveControlSetting(device: Device, localStoragePropertyName: string, setting: SettingGamepad|SettingKeyboard, settingDefaults, valueIndex: integer): boolean {
|
||||
let settingsControls: object = {}; // Initialize an empty object to hold the gamepad settings
|
||||
|
||||
if (localStorage.hasOwnProperty("settingsGamepad")) { // Check if 'settingsGamepad' exists in localStorage
|
||||
settingsGamepad = JSON.parse(localStorage.getItem("settingsGamepad")); // Parse the existing 'settingsGamepad' from localStorage
|
||||
if (localStorage.hasOwnProperty(localStoragePropertyName)) { // Check if 'settingsControls' exists in 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
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a keyboard setting to localStorage.
|
||||
*
|
||||
* @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.
|
||||
* Loads Settings from local storage if available
|
||||
* @returns true if succesful, false if not
|
||||
*/
|
||||
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 {
|
||||
Object.values(Setting).map(setting => setting as Setting).forEach(setting => setSetting(this.scene, setting, settingDefaults[setting]));
|
||||
resetSettings(this.scene);
|
||||
|
||||
if (!localStorage.hasOwnProperty("settings")) {
|
||||
return false;
|
||||
@ -698,7 +694,7 @@ export class GameData {
|
||||
const settings = JSON.parse(localStorage.getItem("settings"));
|
||||
|
||||
for (const setting of Object.keys(settings)) {
|
||||
setSetting(this.scene, setting as Setting, settings[setting]);
|
||||
setSetting(this.scene, setting, settings[setting]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -716,9 +712,10 @@ export class GameData {
|
||||
}
|
||||
|
||||
public saveTutorialFlag(tutorial: Tutorial, flag: boolean): boolean {
|
||||
const key = getDataTypeKey(GameDataType.TUTORIALS);
|
||||
let tutorials: object = {};
|
||||
if (localStorage.hasOwnProperty("tutorials")) {
|
||||
tutorials = JSON.parse(localStorage.getItem("tutorials"));
|
||||
if (localStorage.hasOwnProperty(key)) {
|
||||
tutorials = JSON.parse(localStorage.getItem(key));
|
||||
}
|
||||
|
||||
Object.keys(Tutorial).map(t => t as Tutorial).forEach(t => {
|
||||
@ -730,20 +727,21 @@ export class GameData {
|
||||
}
|
||||
});
|
||||
|
||||
localStorage.setItem("tutorials", JSON.stringify(tutorials));
|
||||
localStorage.setItem(key, JSON.stringify(tutorials));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public getTutorialFlags(): TutorialFlags {
|
||||
const key = getDataTypeKey(GameDataType.TUTORIALS);
|
||||
const ret: TutorialFlags = {};
|
||||
Object.values(Tutorial).map(tutorial => tutorial as Tutorial).forEach(tutorial => ret[Tutorial[tutorial]] = false);
|
||||
|
||||
if (!localStorage.hasOwnProperty("tutorials")) {
|
||||
if (!localStorage.hasOwnProperty(key)) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
const tutorials = JSON.parse(localStorage.getItem("tutorials"));
|
||||
const tutorials = JSON.parse(localStorage.getItem(key));
|
||||
|
||||
for (const tutorial of Object.keys(tutorials)) {
|
||||
ret[tutorial] = tutorials[tutorial];
|
||||
@ -752,6 +750,34 @@ export class GameData {
|
||||
return ret;
|
||||
}
|
||||
|
||||
public saveSeenDialogue(dialogue: string): boolean {
|
||||
const key = getDataTypeKey(GameDataType.SEEN_DIALOGUES);
|
||||
const dialogues: object = this.getSeenDialogues();
|
||||
|
||||
dialogues[dialogue] = true;
|
||||
localStorage.setItem(key, JSON.stringify(dialogues));
|
||||
console.log("Dialogue saved as seen:", dialogue);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public getSeenDialogues(): SeenDialogues {
|
||||
const key = getDataTypeKey(GameDataType.SEEN_DIALOGUES);
|
||||
const ret: SeenDialogues = {};
|
||||
|
||||
if (!localStorage.hasOwnProperty(key)) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
const dialogues = JSON.parse(localStorage.getItem(key));
|
||||
|
||||
for (const dialogue of Object.keys(dialogues)) {
|
||||
ret[dialogue] = dialogues[dialogue];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private getSessionSaveData(scene: BattleScene): SessionSaveData {
|
||||
return {
|
||||
seed: scene.seed,
|
||||
|
@ -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",
|
||||
Candy_Upgrade_Notification = "CANDY_UPGRADE_NOTIFICATION",
|
||||
Candy_Upgrade_Display = "CANDY_UPGRADE_DISPLAY",
|
||||
Money_Format = "MONEY_FORMAT",
|
||||
Sprite_Set = "SPRITE_SET",
|
||||
Music_Preference = "MUSIC_PREFERENCE",
|
||||
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.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.Music_Preference]: ["Consistent", "Mixed"],
|
||||
[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.Candy_Upgrade_Notification]: 0,
|
||||
[Setting.Candy_Upgrade_Display]: 0,
|
||||
[Setting.Money_Format]: 0,
|
||||
[Setting.Sprite_Set]: 0,
|
||||
[Setting.Music_Preference]: 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.Music_Preference, 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.Music_Preference:
|
||||
scene.musicPreference = value;
|
||||
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.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 {SettingDefaults, SettingOptions} from "./settings";
|
||||
import SettingsGamepadUiHandler from "../ui/settings/settings-gamepad-ui-handler";
|
||||
import {Mode} from "../ui/ui";
|
||||
import {truncateString} from "../utils";
|
||||
import {Button} from "../enums/buttons";
|
||||
import {SettingKeyboard} from "#app/system/settings-keyboard";
|
||||
import BattleScene from "../../battle-scene";
|
||||
import SettingsGamepadUiHandler from "../../ui/settings/settings-gamepad-ui-handler";
|
||||
import {Mode} from "../../ui/ui";
|
||||
import {truncateString} from "../../utils";
|
||||
import {Button} from "../../enums/buttons";
|
||||
import {SettingKeyboard} from "#app/system/settings/settings-keyboard";
|
||||
|
||||
export enum SettingGamepad {
|
||||
Controller = "CONTROLLER",
|
||||
@ -28,29 +27,31 @@ export enum SettingGamepad {
|
||||
Button_Submit = "BUTTON_SUBMIT",
|
||||
}
|
||||
|
||||
export const settingGamepadOptions: SettingOptions = {
|
||||
const pressAction = "Press action to assign";
|
||||
|
||||
export const settingGamepadOptions = {
|
||||
[SettingGamepad.Controller]: ["Default", "Change"],
|
||||
[SettingGamepad.Gamepad_Support]: ["Auto", "Disabled"],
|
||||
[SettingGamepad.Button_Up]: [`KEY ${Button.UP.toString()}`, "Press action to assign"],
|
||||
[SettingGamepad.Button_Down]: [`KEY ${Button.DOWN.toString()}`, "Press action to assign"],
|
||||
[SettingGamepad.Button_Left]: [`KEY ${Button.LEFT.toString()}`, "Press action to assign"],
|
||||
[SettingGamepad.Button_Right]: [`KEY ${Button.RIGHT.toString()}`, "Press action to assign"],
|
||||
[SettingGamepad.Button_Action]: [`KEY ${Button.ACTION.toString()}`, "Press action to assign"],
|
||||
[SettingGamepad.Button_Cancel]: [`KEY ${Button.CANCEL.toString()}`, "Press action to assign"],
|
||||
[SettingGamepad.Button_Menu]: [`KEY ${Button.MENU.toString()}`, "Press action to assign"],
|
||||
[SettingGamepad.Button_Stats]: [`KEY ${Button.STATS.toString()}`, "Press action to assign"],
|
||||
[SettingGamepad.Button_Cycle_Form]: [`KEY ${Button.CYCLE_FORM.toString()}`, "Press action to assign"],
|
||||
[SettingGamepad.Button_Cycle_Shiny]: [`KEY ${Button.CYCLE_SHINY.toString()}`, "Press action to assign"],
|
||||
[SettingGamepad.Button_Cycle_Gender]: [`KEY ${Button.CYCLE_GENDER.toString()}`, "Press action to assign"],
|
||||
[SettingGamepad.Button_Cycle_Ability]: [`KEY ${Button.CYCLE_ABILITY.toString()}`, "Press action to assign"],
|
||||
[SettingGamepad.Button_Cycle_Nature]: [`KEY ${Button.CYCLE_NATURE.toString()}`, "Press action to assign"],
|
||||
[SettingGamepad.Button_Cycle_Variant]: [`KEY ${Button.V.toString()}`, "Press action to assign"],
|
||||
[SettingGamepad.Button_Speed_Up]: [`KEY ${Button.SPEED_UP.toString()}`, "Press action to assign"],
|
||||
[SettingGamepad.Button_Slow_Down]: [`KEY ${Button.SLOW_DOWN.toString()}`, "Press action to assign"],
|
||||
[SettingGamepad.Button_Submit]: [`KEY ${Button.SUBMIT.toString()}`, "Press action to assign"],
|
||||
[SettingGamepad.Button_Up]: [`KEY ${Button.UP.toString()}`, pressAction],
|
||||
[SettingGamepad.Button_Down]: [`KEY ${Button.DOWN.toString()}`, pressAction],
|
||||
[SettingGamepad.Button_Left]: [`KEY ${Button.LEFT.toString()}`, pressAction],
|
||||
[SettingGamepad.Button_Right]: [`KEY ${Button.RIGHT.toString()}`, pressAction],
|
||||
[SettingGamepad.Button_Action]: [`KEY ${Button.ACTION.toString()}`, pressAction],
|
||||
[SettingGamepad.Button_Cancel]: [`KEY ${Button.CANCEL.toString()}`, pressAction],
|
||||
[SettingGamepad.Button_Menu]: [`KEY ${Button.MENU.toString()}`, pressAction],
|
||||
[SettingGamepad.Button_Stats]: [`KEY ${Button.STATS.toString()}`, pressAction],
|
||||
[SettingGamepad.Button_Cycle_Form]: [`KEY ${Button.CYCLE_FORM.toString()}`, pressAction],
|
||||
[SettingGamepad.Button_Cycle_Shiny]: [`KEY ${Button.CYCLE_SHINY.toString()}`, pressAction],
|
||||
[SettingGamepad.Button_Cycle_Gender]: [`KEY ${Button.CYCLE_GENDER.toString()}`, pressAction],
|
||||
[SettingGamepad.Button_Cycle_Ability]: [`KEY ${Button.CYCLE_ABILITY.toString()}`, pressAction],
|
||||
[SettingGamepad.Button_Cycle_Nature]: [`KEY ${Button.CYCLE_NATURE.toString()}`, pressAction],
|
||||
[SettingGamepad.Button_Cycle_Variant]: [`KEY ${Button.V.toString()}`, pressAction],
|
||||
[SettingGamepad.Button_Speed_Up]: [`KEY ${Button.SPEED_UP.toString()}`, pressAction],
|
||||
[SettingGamepad.Button_Slow_Down]: [`KEY ${Button.SLOW_DOWN.toString()}`, pressAction],
|
||||
[SettingGamepad.Button_Submit]: [`KEY ${Button.SUBMIT.toString()}`, pressAction],
|
||||
};
|
||||
|
||||
export const settingGamepadDefaults: SettingDefaults = {
|
||||
export const settingGamepadDefaults = {
|
||||
[SettingGamepad.Controller]: 0,
|
||||
[SettingGamepad.Gamepad_Support]: 0,
|
||||
[SettingGamepad.Button_Up]: 0,
|
@ -1,4 +1,3 @@
|
||||
import {SettingDefaults, SettingOptions} from "#app/system/settings";
|
||||
import {Button} from "#app/enums/buttons";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import {Mode} from "#app/ui/ui";
|
||||
@ -42,46 +41,47 @@ export enum SettingKeyboard {
|
||||
Alt_Button_Submit = "ALT_BUTTON_SUBMIT",
|
||||
}
|
||||
|
||||
export const settingKeyboardOptions: SettingOptions = {
|
||||
// [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"],
|
||||
const pressAction = "Press action to assign";
|
||||
|
||||
[SettingKeyboard.Alt_Button_Down]: [`KEY ${Button.DOWN.toString()}`, "Press action to assign"],
|
||||
[SettingKeyboard.Alt_Button_Left]: [`KEY ${Button.LEFT.toString()}`, "Press action to assign"],
|
||||
[SettingKeyboard.Alt_Button_Right]: [`KEY ${Button.RIGHT.toString()}`, "Press action to assign"],
|
||||
[SettingKeyboard.Alt_Button_Action]: [`KEY ${Button.ACTION.toString()}`, "Press action to assign"],
|
||||
[SettingKeyboard.Button_Cancel]: [`KEY ${Button.CANCEL.toString()}`, "Press action to assign"],
|
||||
[SettingKeyboard.Alt_Button_Cancel]: [`KEY ${Button.CANCEL.toString()}`, "Press action to assign"],
|
||||
[SettingKeyboard.Alt_Button_Menu]: [`KEY ${Button.MENU.toString()}`, "Press action to assign"],
|
||||
[SettingKeyboard.Button_Stats]: [`KEY ${Button.STATS.toString()}`, "Press action to assign"],
|
||||
[SettingKeyboard.Alt_Button_Stats]: [`KEY ${Button.STATS.toString()}`, "Press action to assign"],
|
||||
[SettingKeyboard.Button_Cycle_Form]: [`KEY ${Button.CYCLE_FORM.toString()}`, "Press action to assign"],
|
||||
[SettingKeyboard.Alt_Button_Cycle_Form]: [`KEY ${Button.CYCLE_FORM.toString()}`, "Press action to assign"],
|
||||
[SettingKeyboard.Button_Cycle_Shiny]: [`KEY ${Button.CYCLE_SHINY.toString()}`, "Press action to assign"],
|
||||
[SettingKeyboard.Alt_Button_Cycle_Shiny]: [`KEY ${Button.CYCLE_SHINY.toString()}`, "Press action to assign"],
|
||||
[SettingKeyboard.Button_Cycle_Gender]: [`KEY ${Button.CYCLE_GENDER.toString()}`, "Press action to assign"],
|
||||
[SettingKeyboard.Alt_Button_Cycle_Gender]: [`KEY ${Button.CYCLE_GENDER.toString()}`, "Press action to assign"],
|
||||
[SettingKeyboard.Button_Cycle_Ability]: [`KEY ${Button.CYCLE_ABILITY.toString()}`, "Press action to assign"],
|
||||
[SettingKeyboard.Alt_Button_Cycle_Ability]: [`KEY ${Button.CYCLE_ABILITY.toString()}`, "Press action to assign"],
|
||||
[SettingKeyboard.Button_Cycle_Nature]: [`KEY ${Button.CYCLE_NATURE.toString()}`, "Press action to assign"],
|
||||
[SettingKeyboard.Alt_Button_Cycle_Nature]: [`KEY ${Button.CYCLE_NATURE.toString()}`, "Press action to assign"],
|
||||
[SettingKeyboard.Button_Cycle_Variant]: [`KEY ${Button.V.toString()}`, "Press action to assign"],
|
||||
[SettingKeyboard.Alt_Button_Cycle_Variant]: [`KEY ${Button.V.toString()}`, "Press action to assign"],
|
||||
[SettingKeyboard.Button_Speed_Up]: [`KEY ${Button.SPEED_UP.toString()}`, "Press action to assign"],
|
||||
[SettingKeyboard.Alt_Button_Speed_Up]: [`KEY ${Button.SPEED_UP.toString()}`, "Press action to assign"],
|
||||
[SettingKeyboard.Button_Slow_Down]: [`KEY ${Button.SLOW_DOWN.toString()}`, "Press action to assign"],
|
||||
[SettingKeyboard.Alt_Button_Slow_Down]: [`KEY ${Button.SLOW_DOWN.toString()}`, "Press action to assign"],
|
||||
[SettingKeyboard.Alt_Button_Submit]: [`KEY ${Button.SUBMIT.toString()}`, "Press action to assign"],
|
||||
export const settingKeyboardOptions = {
|
||||
// [SettingKeyboard.Default_Layout]: ['Default'],
|
||||
[SettingKeyboard.Button_Up]: [`KEY ${Button.UP.toString()}`, pressAction],
|
||||
[SettingKeyboard.Button_Down]: [`KEY ${Button.DOWN.toString()}`, pressAction],
|
||||
[SettingKeyboard.Alt_Button_Up]: [`KEY ${Button.UP.toString()}`, pressAction],
|
||||
[SettingKeyboard.Button_Left]: [`KEY ${Button.LEFT.toString()}`, pressAction],
|
||||
[SettingKeyboard.Button_Right]: [`KEY ${Button.RIGHT.toString()}`, pressAction],
|
||||
[SettingKeyboard.Button_Action]: [`KEY ${Button.ACTION.toString()}`, pressAction],
|
||||
[SettingKeyboard.Button_Menu]: [`KEY ${Button.MENU.toString()}`, pressAction],
|
||||
[SettingKeyboard.Button_Submit]: [`KEY ${Button.SUBMIT.toString()}`, pressAction],
|
||||
[SettingKeyboard.Alt_Button_Down]: [`KEY ${Button.DOWN.toString()}`, pressAction],
|
||||
[SettingKeyboard.Alt_Button_Left]: [`KEY ${Button.LEFT.toString()}`, pressAction],
|
||||
[SettingKeyboard.Alt_Button_Right]: [`KEY ${Button.RIGHT.toString()}`, pressAction],
|
||||
[SettingKeyboard.Alt_Button_Action]: [`KEY ${Button.ACTION.toString()}`, pressAction],
|
||||
[SettingKeyboard.Button_Cancel]: [`KEY ${Button.CANCEL.toString()}`, pressAction],
|
||||
[SettingKeyboard.Alt_Button_Cancel]: [`KEY ${Button.CANCEL.toString()}`, pressAction],
|
||||
[SettingKeyboard.Alt_Button_Menu]: [`KEY ${Button.MENU.toString()}`, pressAction],
|
||||
[SettingKeyboard.Button_Stats]: [`KEY ${Button.STATS.toString()}`, pressAction],
|
||||
[SettingKeyboard.Alt_Button_Stats]: [`KEY ${Button.STATS.toString()}`, pressAction],
|
||||
[SettingKeyboard.Button_Cycle_Form]: [`KEY ${Button.CYCLE_FORM.toString()}`, pressAction],
|
||||
[SettingKeyboard.Alt_Button_Cycle_Form]: [`KEY ${Button.CYCLE_FORM.toString()}`, pressAction],
|
||||
[SettingKeyboard.Button_Cycle_Shiny]: [`KEY ${Button.CYCLE_SHINY.toString()}`, pressAction],
|
||||
[SettingKeyboard.Alt_Button_Cycle_Shiny]: [`KEY ${Button.CYCLE_SHINY.toString()}`, pressAction],
|
||||
[SettingKeyboard.Button_Cycle_Gender]: [`KEY ${Button.CYCLE_GENDER.toString()}`, pressAction],
|
||||
[SettingKeyboard.Alt_Button_Cycle_Gender]: [`KEY ${Button.CYCLE_GENDER.toString()}`, pressAction],
|
||||
[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.Button_Up]: 0,
|
||||
[SettingKeyboard.Button_Down]: 0,
|
465
src/system/settings/settings.ts
Normal file
465
src/system/settings/settings.ts
Normal file
@ -0,0 +1,465 @@
|
||||
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",
|
||||
Music_Preference: "MUSIC_PREFERENCE",
|
||||
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"
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.Music_Preference,
|
||||
label: "Music Preference",
|
||||
options: ["Consistent", "Mixed"],
|
||||
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_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.Music_Preference:
|
||||
scene.musicPreference = value;
|
||||
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_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
|
||||
} from "#app/configs/inputs/configHandler";
|
||||
import {expect} from "vitest";
|
||||
import {SettingKeyboard} from "#app/system/settings-keyboard";
|
||||
import {SettingKeyboard} from "#app/system/settings/settings-keyboard";
|
||||
|
||||
export class InGameManip {
|
||||
private config;
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
assign,
|
||||
getSettingNameWithKeycode, canIAssignThisKey, canIDeleteThisKey, canIOverrideThisSetting
|
||||
} from "#app/configs/inputs/configHandler";
|
||||
import {SettingKeyboard} from "#app/system/settings-keyboard";
|
||||
import {SettingKeyboard} from "#app/system/settings/settings-keyboard";
|
||||
|
||||
export class MenuManip {
|
||||
private config;
|
||||
|
@ -10,7 +10,7 @@ import {InGameManip} from "#app/test/helpers/inGameManip";
|
||||
import {Device} from "#app/enums/devices";
|
||||
import {InterfaceConfig} from "#app/inputs-controller";
|
||||
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", () => {
|
||||
|
@ -3,12 +3,13 @@ import {Mode} from "./ui/ui";
|
||||
import {InputsController} from "./inputs-controller";
|
||||
import MessageUiHandler from "./ui/message-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 {Button} from "./enums/buttons";
|
||||
import SettingsGamepadUiHandler from "./ui/settings/settings-gamepad-ui-handler";
|
||||
import SettingsKeyboardUiHandler from "#app/ui/settings/settings-keyboard-ui-handler";
|
||||
import BattleScene from "./battle-scene";
|
||||
import SettingsAccessibilityUiHandler from "./ui/settings/settings-accessiblity-ui-handler";
|
||||
|
||||
type ActionKeys = Record<Button, () => void>;
|
||||
|
||||
@ -161,7 +162,7 @@ export class UiInputs {
|
||||
}
|
||||
|
||||
buttonCycleOption(button: Button): void {
|
||||
const whitelist = [StarterSelectUiHandler, SettingsUiHandler, SettingsGamepadUiHandler, SettingsKeyboardUiHandler];
|
||||
const whitelist = [StarterSelectUiHandler, SettingsUiHandler, SettingsAccessibilityUiHandler, SettingsGamepadUiHandler, SettingsKeyboardUiHandler];
|
||||
const uiHandler = this.scene.ui?.getHandler();
|
||||
if (whitelist.some(handler => uiHandler instanceof handler)) {
|
||||
this.scene.ui.processInput(button);
|
||||
@ -171,17 +172,14 @@ export class UiInputs {
|
||||
}
|
||||
|
||||
buttonSpeedChange(up = true): void {
|
||||
if (up) {
|
||||
if (this.scene.gameSpeed < 5) {
|
||||
this.scene.gameData.saveSetting(Setting.Game_Speed, settingOptions[Setting.Game_Speed].indexOf(`${this.scene.gameSpeed}x`) + 1);
|
||||
if (this.scene.ui?.getMode() === Mode.SETTINGS) {
|
||||
(this.scene.ui.getHandler() as SettingsUiHandler).show([]);
|
||||
}
|
||||
const settingGameSpeed = settingIndex(SettingKeys.Game_Speed);
|
||||
if (up && this.scene.gameSpeed < 5) {
|
||||
this.scene.gameData.saveSetting(SettingKeys.Game_Speed, Setting[settingGameSpeed].options.indexOf(`${this.scene.gameSpeed}x`) + 1);
|
||||
if (this.scene.ui?.getMode() === Mode.SETTINGS) {
|
||||
(this.scene.ui.getHandler() as SettingsUiHandler).show([]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
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));
|
||||
} 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.ui?.getMode() === Mode.SETTINGS) {
|
||||
(this.scene.ui.getHandler() as SettingsUiHandler).show([]);
|
||||
}
|
||||
|
@ -378,6 +378,20 @@ export default class EggGachaUiHandler extends MessageUiHandler {
|
||||
} else if (pullCount >= 10 && !tiers.filter(t => t >= EggTier.GREAT).length) {
|
||||
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();
|
||||
|
||||
|
660
src/ui/settings/abstract-control-settings-ui-handler.ts
Normal file
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 {Mode} from "../ui";
|
||||
import {InterfaceConfig} from "../../inputs-controller";
|
||||
import {addWindow} from "../ui-theme";
|
||||
import {addTextObject, TextStyle} from "../text";
|
||||
import { hasTouchscreen, isMobile } from "../../touch-controls";
|
||||
import { TextStyle, addTextObject } from "../text";
|
||||
import { Mode } from "../ui";
|
||||
import UiHandler from "../ui-handler";
|
||||
import { addWindow } from "../ui-theme";
|
||||
import {Button} from "../../enums/buttons";
|
||||
import {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 { 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.
|
||||
*/
|
||||
export default abstract class AbstractSettingsUiUiHandler extends UiHandler {
|
||||
protected settingsContainer: Phaser.GameObjects.Container;
|
||||
protected optionsContainer: Phaser.GameObjects.Container;
|
||||
protected navigationContainer: NavigationMenu;
|
||||
export default class AbstractSettingsUiHandler extends UiHandler {
|
||||
private settingsContainer: Phaser.GameObjects.Container;
|
||||
private optionsContainer: Phaser.GameObjects.Container;
|
||||
private navigationContainer: NavigationMenu;
|
||||
|
||||
protected scrollCursor: integer;
|
||||
protected optionCursors: integer[];
|
||||
protected cursorObj: Phaser.GameObjects.NineSlice;
|
||||
private scrollCursor: integer;
|
||||
|
||||
protected optionsBg: Phaser.GameObjects.NineSlice;
|
||||
protected actionsBg: Phaser.GameObjects.NineSlice;
|
||||
private optionsBg: Phaser.GameObjects.NineSlice;
|
||||
|
||||
protected settingLabels: Phaser.GameObjects.Text[];
|
||||
protected optionValueLabels: Phaser.GameObjects.Text[][];
|
||||
private optionCursors: integer[];
|
||||
|
||||
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;
|
||||
// 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>;
|
||||
private cursorObj: Phaser.GameObjects.NineSlice;
|
||||
|
||||
protected settingDevice;
|
||||
protected settingBlacklisted;
|
||||
protected settingDeviceDefaults;
|
||||
protected settingDeviceOptions;
|
||||
protected configs;
|
||||
protected commonSettingsCount;
|
||||
protected textureOverride;
|
||||
protected titleSelected;
|
||||
protected localStoragePropertyName;
|
||||
protected rowsToDisplay: number;
|
||||
private reloadSettings: Array<Setting>;
|
||||
private reloadRequired: boolean;
|
||||
private rowsToDisplay: number;
|
||||
|
||||
abstract getLocalStorageSetting(): object;
|
||||
abstract navigateMenuLeft(): boolean;
|
||||
abstract navigateMenuRight(): boolean;
|
||||
abstract saveSettingToLocalStorage(setting, cursor): void;
|
||||
abstract getActiveConfig(): InterfaceConfig;
|
||||
abstract setSetting(scene: BattleScene, setting, value: integer): boolean;
|
||||
protected title: string;
|
||||
protected settings: Array<Setting>;
|
||||
protected localStorageKey: string;
|
||||
|
||||
/**
|
||||
* Constructor for the AbstractSettingsUiUiHandler.
|
||||
*
|
||||
* @param scene - The BattleScene instance.
|
||||
* @param mode - The UI mode.
|
||||
*/
|
||||
constructor(scene: BattleScene, mode?: Mode) {
|
||||
super(scene, mode);
|
||||
|
||||
this.reloadRequired = false;
|
||||
this.rowsToDisplay = 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup UI elements.
|
||||
*/
|
||||
* 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.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);
|
||||
|
||||
|
||||
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 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(this.actionsBg, this.navigationContainer.width - 32, 4);
|
||||
iconAction.setPositionRelative(actionsBg, this.navigationContainer.width - 32, 4);
|
||||
this.navigationIcons["BUTTON_ACTION"] = iconAction;
|
||||
|
||||
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");
|
||||
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;
|
||||
|
||||
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;
|
||||
this.optionsContainer = this.scene.add.container(0, 0);
|
||||
|
||||
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.settingLabels = [];
|
||||
this.optionValueLabels = [];
|
||||
|
||||
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);
|
||||
this.reloadSettings = this.settings.filter(s => s?.requireReload);
|
||||
|
||||
/// 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.
|
||||
this.settings
|
||||
.forEach((setting, s) => {
|
||||
let settingName = setting.label;
|
||||
if (setting?.requireReload) {
|
||||
settingName += " (Requires Reload)";
|
||||
}
|
||||
|
||||
const optionsContainer = this.scene.add.container(0, 0);
|
||||
optionsContainer.setVisible(false);
|
||||
this.settingLabels[s] = addTextObject(this.scene, 8, 28 + s * 16, settingName, TextStyle.SETTINGS_LABEL);
|
||||
this.settingLabels[s].setOrigin(0, 0);
|
||||
|
||||
// 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.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);
|
||||
this.optionsContainer.add(this.settingLabels[s]);
|
||||
this.optionValueLabels.push(setting.options.map((option, o) => {
|
||||
const valueLabel = addTextObject(this.scene, 0, 0, option, setting.default === o ? TextStyle.SETTINGS_SELECTED : TextStyle.WINDOW);
|
||||
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
|
||||
valueLabels.push(valueLabel);
|
||||
}
|
||||
// Collect all option labels for this setting into the main array.
|
||||
optionValueLabels.push(valueLabels);
|
||||
return valueLabel;
|
||||
}));
|
||||
|
||||
// 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);
|
||||
const totalWidth = this.optionValueLabels[s].map(o => o.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);
|
||||
const labelWidth = Math.max(78, this.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));
|
||||
const optionSpacing = Math.floor(totalSpace / (this.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;
|
||||
for (const value of this.optionValueLabels[s]) {
|
||||
value.setPositionRelative(this.settingLabels[s], labelWidth + xOffset, 0);
|
||||
xOffset += value.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.
|
||||
this.optionCursors = this.settings.map(setting => setting.default);
|
||||
|
||||
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);
|
||||
|
||||
// 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.setCursor(0);
|
||||
this.setScrollCursor(0);
|
||||
|
||||
this.settingsContainer.setVisible(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the bindings for the current active device configuration.
|
||||
*/
|
||||
* 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)) {
|
||||
if (settingName === "BUTTON_HOME") {
|
||||
this.navigationIcons[settingName].setTexture("keyboard");
|
||||
this.navigationIcons[settingName].setFrame(specialIcons[settingName]);
|
||||
this.navigationIcons[settingName].setFrame("HOME.png");
|
||||
this.navigationIcons[settingName].alpha = 1;
|
||||
continue;
|
||||
}
|
||||
@ -324,112 +165,61 @@ export default abstract class AbstractSettingsUiUiHandler extends UiHandler {
|
||||
this.navigationIcons[settingName].alpha = 0;
|
||||
}
|
||||
}
|
||||
NavigationManager.getInstance().updateIcons();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the UI with the provided arguments.
|
||||
*
|
||||
* @param args - Arguments to be passed to the show method.
|
||||
* @returns `true` if successful.
|
||||
* 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();
|
||||
const settings: object = localStorage.hasOwnProperty(this.localStorageKey) ? JSON.parse(localStorage.getItem(this.localStorageKey)) : {};
|
||||
|
||||
this.settings.forEach((setting, s) => this.setOptionCursor(s, settings.hasOwnProperty(setting.key) ? settings[setting.key] : this.settings[s].default));
|
||||
|
||||
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);
|
||||
|
||||
// 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.
|
||||
*/
|
||||
* 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;
|
||||
this.updateNavigationDisplay();
|
||||
|
||||
// Handle the input based on the button pressed.
|
||||
let success = false;
|
||||
|
||||
if (button === Button.CANCEL) {
|
||||
// Handle cancel button press, reverting UI mode to previous state.
|
||||
success = true;
|
||||
NavigationManager.getInstance().reset();
|
||||
// Reverts UI to its previous state on cancel.
|
||||
this.scene.ui.revertMode();
|
||||
} else {
|
||||
const cursor = this.cursor + this.scrollCursor; // Calculate the absolute cursor position.
|
||||
const setting = this.settingDevice[Object.keys(this.settingDevice)[cursor]];
|
||||
const cursor = this.cursor + this.scrollCursor;
|
||||
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.
|
||||
case Button.UP:
|
||||
if (cursor) {
|
||||
if (this.cursor) {
|
||||
success = this.setCursor(this.cursor - 1);
|
||||
} else {// If at the top of the visible items, scroll up.
|
||||
} else {
|
||||
success = this.setScrollCursor(this.scrollCursor - 1);
|
||||
}
|
||||
} 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
|
||||
}
|
||||
break;
|
||||
case Button.DOWN: // Move down in the menu.
|
||||
if (!this.optionValueLabels) {
|
||||
return false;
|
||||
}
|
||||
case Button.DOWN:
|
||||
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);
|
||||
} else if (this.scrollCursor < this.optionValueLabels.length - this.rowsToDisplay) {
|
||||
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.
|
||||
}
|
||||
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]) {
|
||||
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: // 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) {
|
||||
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;
|
||||
@ -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) {
|
||||
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();
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the cursor to the specified position.
|
||||
*
|
||||
* @param cursor - The cursor position to set.
|
||||
* @returns `true` if the cursor was set successfully.
|
||||
*/
|
||||
* 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.
|
||||
this.cursorObj.setOrigin(0, 0);
|
||||
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);
|
||||
|
||||
return ret; // Return the result from the parent class's setCursor method.
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
* 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 {
|
||||
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 {
|
||||
// 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.
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.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.
|
||||
*/
|
||||
* 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);
|
||||
@ -619,29 +367,25 @@ export default abstract class AbstractSettingsUiUiHandler extends UiHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the UI elements and state.
|
||||
*/
|
||||
clear(): void {
|
||||
* Clear the UI elements and state.
|
||||
*/
|
||||
clear() {
|
||||
super.clear();
|
||||
|
||||
// Hide the settings container to remove it from the view.
|
||||
this.settingsContainer.setVisible(false);
|
||||
|
||||
// Remove the cursor from the UI.
|
||||
this.eraseCursor();
|
||||
if (this.reloadRequired) {
|
||||
this.reloadRequired = false;
|
||||
this.scene.reset(true, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Erase the cursor from the UI.
|
||||
*/
|
||||
eraseCursor(): void {
|
||||
// Check if a cursor object exists.
|
||||
* Erase the cursor from the UI.
|
||||
*/
|
||||
eraseCursor() {
|
||||
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,10 +1,13 @@
|
||||
import BattleScene from "#app/battle-scene";
|
||||
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 {addWindow} from "#app/ui/ui-theme";
|
||||
import {Button} from "#app/enums/buttons";
|
||||
|
||||
const LEFT = "LEFT";
|
||||
const RIGHT = "RIGHT";
|
||||
|
||||
/**
|
||||
* Manages navigation and menus tabs within the setting menu.
|
||||
*/
|
||||
@ -24,14 +27,16 @@ export class NavigationManager {
|
||||
constructor() {
|
||||
this.modes = [
|
||||
Mode.SETTINGS,
|
||||
Mode.SETTINGS_ACCESSIBILITY,
|
||||
Mode.SETTINGS_GAMEPAD,
|
||||
Mode.SETTINGS_KEYBOARD,
|
||||
];
|
||||
this.labels = ["General", "Gamepad", "Keyboard"];
|
||||
this.labels = ["General", "Accessibility", "Gamepad", "Keyboard"];
|
||||
}
|
||||
|
||||
public reset() {
|
||||
this.selectedMode = Mode.SETTINGS;
|
||||
this.updateNavigationMenus();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -46,32 +51,20 @@ export class NavigationManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the previous mode in the modes array.
|
||||
* @param scene The current BattleScene instance.
|
||||
* Navigates modes based on given direction
|
||||
* @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 maxPos = this.modes.length - 1;
|
||||
if (pos === 0) {
|
||||
const increment = direction === LEFT ? -1 : 1;
|
||||
if (pos === 0 && direction === LEFT) {
|
||||
this.selectedMode = this.modes[maxPos];
|
||||
} else {
|
||||
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) {
|
||||
} else if (pos === maxPos && direction === RIGHT) {
|
||||
this.selectedMode = this.modes[0];
|
||||
} else {
|
||||
this.selectedMode = this.modes[pos + 1];
|
||||
this.selectedMode = this.modes[pos + increment];
|
||||
}
|
||||
scene.ui.setMode(this.selectedMode);
|
||||
this.updateNavigationMenus();
|
||||
@ -204,13 +197,11 @@ export default class NavigationMenu extends Phaser.GameObjects.Container {
|
||||
const navigationManager = NavigationManager.getInstance();
|
||||
switch (button) {
|
||||
case Button.CYCLE_FORM:
|
||||
navigationManager.navigateLeft(this.scene);
|
||||
navigationManager.navigate(this.scene, LEFT);
|
||||
return true;
|
||||
break;
|
||||
case Button.CYCLE_SHINY:
|
||||
navigationManager.navigateRight(this.scene);
|
||||
navigationManager.navigate(this.scene, RIGHT);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
20
src/ui/settings/settings-accessiblity-ui-handler.ts
Normal file
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,
|
||||
settingGamepadDefaults,
|
||||
settingGamepadOptions
|
||||
} from "../../system/settings-gamepad";
|
||||
} from "../../system/settings/settings-gamepad";
|
||||
import pad_xbox360 from "#app/configs/inputs/pad_xbox360";
|
||||
import pad_dualshock from "#app/configs/inputs/pad_dualshock";
|
||||
import pad_unlicensedSNES from "#app/configs/inputs/pad_unlicensedSNES";
|
||||
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 {truncateString} from "#app/utils";
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@ -33,18 +33,17 @@ export default class SettingsGamepadUiHandler extends AbstractSettingsUiUiHandle
|
||||
constructor(scene: BattleScene, mode?: Mode) {
|
||||
super(scene, mode);
|
||||
this.titleSelected = "Gamepad";
|
||||
this.settingDevice = SettingGamepad;
|
||||
this.setting = SettingGamepad;
|
||||
this.settingDeviceDefaults = settingGamepadDefaults;
|
||||
this.settingDeviceOptions = settingGamepadOptions;
|
||||
this.configs = [pad_xbox360, pad_dualshock, pad_unlicensedSNES];
|
||||
this.commonSettingsCount = 2;
|
||||
this.localStoragePropertyName = "settingsGamepad";
|
||||
this.settingBlacklisted = settingGamepadBlackList;
|
||||
this.device = Device.GAMEPAD;
|
||||
}
|
||||
|
||||
setSetting(scene: BattleScene, setting, value: integer): boolean {
|
||||
return setSettingGamepad(scene, setting, value);
|
||||
}
|
||||
setSetting = setSettingGamepad;
|
||||
|
||||
/**
|
||||
* Setup UI elements.
|
||||
@ -65,26 +64,6 @@ export default class SettingsGamepadUiHandler extends AbstractSettingsUiUiHandle
|
||||
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.
|
||||
*
|
||||
@ -105,27 +84,6 @@ export default class SettingsGamepadUiHandler extends AbstractSettingsUiUiHandle
|
||||
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.
|
||||
*/
|
||||
@ -135,11 +93,11 @@ export default class SettingsGamepadUiHandler extends AbstractSettingsUiUiHandle
|
||||
this.resetScroll();
|
||||
|
||||
// Iterate over the keys in the settingDevice enumeration.
|
||||
for (const [index, key] of Object.keys(this.settingDevice).entries()) {
|
||||
const setting = this.settingDevice[key]; // Get the actual setting value using the key.
|
||||
for (const [index, key] of Object.keys(this.setting).entries()) {
|
||||
const setting = this.setting[key]; // Get the actual setting value using the key.
|
||||
|
||||
// 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.
|
||||
for (const _key of Object.keys(this.layout)) {
|
||||
if (_key === "noGamepads") {
|
||||
@ -157,12 +115,12 @@ export default class SettingsGamepadUiHandler extends AbstractSettingsUiUiHandle
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
saveSettingToLocalStorage(setting, cursor): void {
|
||||
if (this.settingDevice[setting] !== this.settingDevice.Controller) {
|
||||
this.scene.gameData.saveGamepadSetting(setting, cursor);
|
||||
saveSettingToLocalStorage(settingName, cursor): void {
|
||||
if (this.setting[settingName] !== this.setting.Controller) {
|
||||
this.scene.gameData.saveControlSetting(this.device, this.localStoragePropertyName, settingName, this.settingDeviceDefaults, cursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,9 +7,9 @@ import {
|
||||
settingKeyboardBlackList,
|
||||
settingKeyboardDefaults,
|
||||
settingKeyboardOptions
|
||||
} from "#app/system/settings-keyboard";
|
||||
} from "#app/system/settings/settings-keyboard";
|
||||
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 {addTextObject, TextStyle} from "#app/ui/text";
|
||||
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.
|
||||
*
|
||||
* @extends AbstractSettingsUiUiHandler
|
||||
* @extends AbstractControlSettingsUiHandler
|
||||
*/
|
||||
export default class SettingsKeyboardUiHandler extends AbstractSettingsUiUiHandler {
|
||||
export default class SettingsKeyboardUiHandler extends AbstractControlSettingsUiHandler {
|
||||
/**
|
||||
* Creates an instance of SettingsKeyboardUiHandler.
|
||||
*
|
||||
@ -31,7 +31,7 @@ export default class SettingsKeyboardUiHandler extends AbstractSettingsUiUiHandl
|
||||
constructor(scene: BattleScene, mode?: Mode) {
|
||||
super(scene, mode);
|
||||
this.titleSelected = "Keyboard";
|
||||
this.settingDevice = SettingKeyboard;
|
||||
this.setting = SettingKeyboard;
|
||||
this.settingDeviceDefaults = settingKeyboardDefaults;
|
||||
this.settingDeviceOptions = settingKeyboardOptions;
|
||||
this.configs = [cfg_keyboard_qwerty];
|
||||
@ -39,6 +39,7 @@ export default class SettingsKeyboardUiHandler extends AbstractSettingsUiUiHandl
|
||||
this.textureOverride = "keyboard";
|
||||
this.localStoragePropertyName = "settingsKeyboard";
|
||||
this.settingBlacklisted = settingKeyboardBlackList;
|
||||
this.device = Device.KEYBOARD;
|
||||
|
||||
const deleteEvent = scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.DELETE);
|
||||
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);
|
||||
}
|
||||
|
||||
setSetting(scene: BattleScene, setting, value: integer): boolean {
|
||||
return setSettingKeyboard(scene, setting, value);
|
||||
}
|
||||
setSetting = setSettingKeyboard;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@ -154,26 +133,6 @@ export default class SettingsKeyboardUiHandler extends AbstractSettingsUiUiHandl
|
||||
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.
|
||||
*/
|
||||
@ -182,11 +141,11 @@ export default class SettingsKeyboardUiHandler extends AbstractSettingsUiUiHandl
|
||||
this.updateBindings();
|
||||
|
||||
// Iterate over the keys in the settingDevice enumeration.
|
||||
for (const [index, key] of Object.keys(this.settingDevice).entries()) {
|
||||
const setting = this.settingDevice[key]; // Get the actual setting value using the key.
|
||||
for (const [index, key] of Object.keys(this.setting).entries()) {
|
||||
const setting = this.setting[key]; // Get the actual setting value using the key.
|
||||
|
||||
// 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.
|
||||
for (const _key of Object.keys(this.layout)) {
|
||||
if (_key === "noKeyboard") {
|
||||
@ -217,8 +176,8 @@ export default class SettingsKeyboardUiHandler extends AbstractSettingsUiUiHandl
|
||||
* @param cursor - The cursor position to save.
|
||||
*/
|
||||
saveSettingToLocalStorage(settingName, cursor): void {
|
||||
if (this.settingDevice[settingName] !== this.settingDevice.Default_Layout) {
|
||||
this.scene.gameData.saveKeyboardSetting(settingName, cursor);
|
||||
if (this.setting[settingName] !== this.setting.Default_Layout) {
|
||||
this.scene.gameData.saveControlSetting(this.device, this.localStoragePropertyName, settingName, this.settingDeviceDefaults, cursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,345 +1,19 @@
|
||||
import BattleScene from "../../battle-scene";
|
||||
import {Setting, reloadSettings, settingDefaults, settingOptions} from "../../system/settings";
|
||||
import { hasTouchscreen, isMobile } from "../../touch-controls";
|
||||
import { TextStyle, addTextObject } from "../text";
|
||||
import {Setting, SettingType} from "../../system/settings/settings";
|
||||
import { Mode } from "../ui";
|
||||
import UiHandler from "../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;
|
||||
import AbstractSettingsUiHandler from "./abstract-settings-ui-handler";
|
||||
|
||||
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) {
|
||||
super(scene, mode);
|
||||
|
||||
this.reloadRequired = false;
|
||||
this.reloadI18n = false;
|
||||
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;
|
||||
this.title = "General";
|
||||
this.settings = Setting.filter(s => s.type === SettingType.GENERAL);
|
||||
this.localStorageKey = "settings";
|
||||
}
|
||||
}
|
||||
|
41
src/ui/ui.ts
41
src/ui/ui.ts
@ -42,6 +42,7 @@ import {PlayerGender} from "#app/system/game-data";
|
||||
import GamepadBindingUiHandler from "./settings/gamepad-binding-ui-handler";
|
||||
import SettingsKeyboardUiHandler from "#app/ui/settings/settings-keyboard-ui-handler";
|
||||
import KeyboardBindingUiHandler from "#app/ui/settings/keyboard-binding-ui-handler";
|
||||
import SettingsAccessibilityUiHandler from "./settings/settings-accessiblity-ui-handler";
|
||||
|
||||
export enum Mode {
|
||||
MESSAGE,
|
||||
@ -62,6 +63,7 @@ export enum Mode {
|
||||
MENU,
|
||||
MENU_OPTION_SELECT,
|
||||
SETTINGS,
|
||||
SETTINGS_ACCESSIBILITY,
|
||||
SETTINGS_GAMEPAD,
|
||||
GAMEPAD_BINDING,
|
||||
SETTINGS_KEYBOARD,
|
||||
@ -99,6 +101,7 @@ const noTransitionModes = [
|
||||
Mode.GAMEPAD_BINDING,
|
||||
Mode.KEYBOARD_BINDING,
|
||||
Mode.SETTINGS,
|
||||
Mode.SETTINGS_ACCESSIBILITY,
|
||||
Mode.SETTINGS_GAMEPAD,
|
||||
Mode.SETTINGS_KEYBOARD,
|
||||
Mode.ACHIEVEMENTS,
|
||||
@ -151,6 +154,7 @@ export default class UI extends Phaser.GameObjects.Container {
|
||||
new MenuUiHandler(scene),
|
||||
new OptionSelectUiHandler(scene, Mode.MENU_OPTION_SELECT),
|
||||
new SettingsUiHandler(scene),
|
||||
new SettingsAccessibilityUiHandler(scene),
|
||||
new SettingsGamepadUiHandler(scene),
|
||||
new GamepadBindingUiHandler(scene),
|
||||
new SettingsKeyboardUiHandler(scene),
|
||||
@ -259,15 +263,26 @@ export default class UI extends Phaser.GameObjects.Container {
|
||||
}
|
||||
// Add the prefix to the text
|
||||
const localizationKey = playerGenderPrefix + text;
|
||||
|
||||
// Get localized dialogue (if available)
|
||||
let hasi18n = false;
|
||||
if (i18next.exists(localizationKey as ParseKeys) ) {
|
||||
|
||||
|
||||
text = i18next.t(localizationKey as ParseKeys);
|
||||
hasi18n = true;
|
||||
|
||||
// Skip dialogue if the player has enabled the option and the dialogue has been already seen
|
||||
if ((this.scene as BattleScene).skipSeenDialogues && (this.scene as BattleScene).gameData.getSeenDialogues()[localizationKey] === true) {
|
||||
console.log(`Dialogue ${localizationKey} skipped`);
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
}
|
||||
let showMessageAndCallback = () => {
|
||||
hasi18n && (this.scene as BattleScene).gameData.saveSeenDialogue(localizationKey);
|
||||
callback();
|
||||
};
|
||||
if (text.indexOf("$") > -1) {
|
||||
const messagePages = text.split(/\$/g).map(m => m.trim());
|
||||
let showMessageAndCallback = () => callback();
|
||||
for (let p = messagePages.length - 1; p >= 0; p--) {
|
||||
const originalFunc = showMessageAndCallback;
|
||||
showMessageAndCallback = () => this.showDialogue(messagePages[p], name, null, originalFunc);
|
||||
@ -276,13 +291,29 @@ export default class UI extends Phaser.GameObjects.Container {
|
||||
} else {
|
||||
const handler = this.getHandler();
|
||||
if (handler instanceof MessageUiHandler) {
|
||||
(handler as MessageUiHandler).showDialogue(text, name, delay, callback, callbackDelay, true, promptDelay);
|
||||
(handler as MessageUiHandler).showDialogue(text, name, delay, showMessageAndCallback, callbackDelay, true, promptDelay);
|
||||
} else {
|
||||
this.getMessageHandler().showDialogue(text, name, delay, callback, callbackDelay, true, promptDelay);
|
||||
this.getMessageHandler().showDialogue(text, name, delay, showMessageAndCallback, callbackDelay, true, promptDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shouldSkipDialogue(text): boolean {
|
||||
let playerGenderPrefix = "PGM";
|
||||
if ((this.scene as BattleScene).gameData.gender === PlayerGender.FEMALE) {
|
||||
playerGenderPrefix = "PGF";
|
||||
}
|
||||
|
||||
const key = playerGenderPrefix + text;
|
||||
|
||||
if (i18next.exists(key as ParseKeys) ) {
|
||||
if ((this.scene as BattleScene).skipSeenDialogues && (this.scene as BattleScene).gameData.getSeenDialogues()[key] === true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
showTooltip(title: string, content: string, overlap?: boolean): void {
|
||||
this.tooltipContainer.setVisible(true);
|
||||
this.tooltipTitle.setText(title || "");
|
||||
|
Loading…
Reference in New Issue
Block a user