mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-08-21 14:59:26 +02:00
Compare commits
1 Commits
b0c63456e7
...
9bcd25441b
Author | SHA1 | Date | |
---|---|---|---|
|
9bcd25441b |
@ -90,13 +90,9 @@ If this feature requires new text, the text should be integrated into the code w
|
|||||||
- For any feature pulled from the mainline Pokémon games (e.g. a Move or Ability implementation), it's best practice to include a source link for any added text.
|
- For any feature pulled from the mainline Pokémon games (e.g. a Move or Ability implementation), it's best practice to include a source link for any added text.
|
||||||
[Poké Corpus](https://abcboy101.github.io/poke-corpus/) is a great resource for finding text from the mainline games; otherwise, a video/picture showing the text being displayed should suffice.
|
[Poké Corpus](https://abcboy101.github.io/poke-corpus/) is a great resource for finding text from the mainline games; otherwise, a video/picture showing the text being displayed should suffice.
|
||||||
- You should also [notify the current Head of Translation](#notifying-translation) to ensure a fast response.
|
- You should also [notify the current Head of Translation](#notifying-translation) to ensure a fast response.
|
||||||
3. Your locales should use the following format:
|
3. At this point, you may begin [testing locales integration in your main PR](#documenting-locales-changes).
|
||||||
- File names should be in `kebab-case`. Example: `trainer-names.json`
|
4. The Translation Team will approve the locale PR (after corrections, if necessary), then merge it into `pokerogue-locales`.
|
||||||
- Key names should be in `camelCase`. Example: `aceTrainer`
|
5. The Dev Team will approve your main PR for your feature, then merge it into PokéRogue's beta environment.
|
||||||
- If you make use of i18next's inbuilt [context support](https://www.i18next.com/translation-function/context), you need to use `snake_case` for the context key. Example: `aceTrainer_male`
|
|
||||||
4. At this point, you may begin [testing locales integration in your main PR](#documenting-locales-changes).
|
|
||||||
5. The Translation Team will approve the locale PR (after corrections, if necessary), then merge it into `pokerogue-locales`.
|
|
||||||
6. The Dev Team will approve your main PR for your feature, then merge it into PokéRogue's beta environment.
|
|
||||||
|
|
||||||
[^2]: For those wondering, the reason for choosing English specifically is due to it being the master language set in Pontoon (the program used by the Translation Team to perform locale updates).
|
[^2]: For those wondering, the reason for choosing English specifically is due to it being the master language set in Pontoon (the program used by the Translation Team to perform locale updates).
|
||||||
If a key is present in any language _except_ the master language, it won't appear anywhere else in the translation tool, rendering missing English keys quite a hassle.
|
If a key is present in any language _except_ the master language, it won't appear anywhere else in the translation tool, rendering missing English keys quite a hassle.
|
||||||
|
@ -104,7 +104,6 @@ import {
|
|||||||
getLuckString,
|
getLuckString,
|
||||||
getLuckTextTint,
|
getLuckTextTint,
|
||||||
getPartyLuckValue,
|
getPartyLuckValue,
|
||||||
type ModifierType,
|
|
||||||
PokemonHeldItemModifierType,
|
PokemonHeldItemModifierType,
|
||||||
} from "#modifiers/modifier-type";
|
} from "#modifiers/modifier-type";
|
||||||
import { MysteryEncounter } from "#mystery-encounters/mystery-encounter";
|
import { MysteryEncounter } from "#mystery-encounters/mystery-encounter";
|
||||||
@ -1204,9 +1203,7 @@ export class BattleScene extends SceneBase {
|
|||||||
this.updateScoreText();
|
this.updateScoreText();
|
||||||
this.scoreText.setVisible(false);
|
this.scoreText.setVisible(false);
|
||||||
|
|
||||||
[this.luckLabelText, this.luckText].forEach(t => {
|
[this.luckLabelText, this.luckText].map(t => t.setVisible(false));
|
||||||
t.setVisible(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.newArena(Overrides.STARTING_BIOME_OVERRIDE || BiomeId.TOWN);
|
this.newArena(Overrides.STARTING_BIOME_OVERRIDE || BiomeId.TOWN);
|
||||||
|
|
||||||
@ -1240,7 +1237,8 @@ export class BattleScene extends SceneBase {
|
|||||||
Object.values(mp)
|
Object.values(mp)
|
||||||
.flat()
|
.flat()
|
||||||
.map(mt => mt.modifierType)
|
.map(mt => mt.modifierType)
|
||||||
.filter((mt): mt is ModifierType & Localizable => "localize" in mt && typeof mt.localize === "function"),
|
.filter(mt => "localize" in mt)
|
||||||
|
.map(lpb => lpb as unknown as Localizable),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
for (const item of localizable) {
|
for (const item of localizable) {
|
||||||
@ -1515,8 +1513,8 @@ export class BattleScene extends SceneBase {
|
|||||||
return this.currentBattle;
|
return this.currentBattle;
|
||||||
}
|
}
|
||||||
|
|
||||||
newArena(biome: BiomeId, playerFaints = 0): Arena {
|
newArena(biome: BiomeId, playerFaints?: number): Arena {
|
||||||
this.arena = new Arena(biome, playerFaints);
|
this.arena = new Arena(biome, BiomeId[biome].toLowerCase(), playerFaints);
|
||||||
this.eventTarget.dispatchEvent(new NewArenaEvent());
|
this.eventTarget.dispatchEvent(new NewArenaEvent());
|
||||||
|
|
||||||
this.arenaBg.pipelineData = {
|
this.arenaBg.pipelineData = {
|
||||||
@ -2713,9 +2711,7 @@ export class BattleScene extends SceneBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.party.forEach(p => {
|
this.party.map(p => p.updateInfo(instant));
|
||||||
p.updateInfo(instant);
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
const args = [this];
|
const args = [this];
|
||||||
if (modifier.shouldApply(...args)) {
|
if (modifier.shouldApply(...args)) {
|
||||||
|
@ -74,7 +74,6 @@ import {
|
|||||||
randSeedItem,
|
randSeedItem,
|
||||||
toDmgValue,
|
toDmgValue,
|
||||||
} from "#utils/common";
|
} from "#utils/common";
|
||||||
import { toCamelCase } from "#utils/strings";
|
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
|
||||||
export class Ability implements Localizable {
|
export class Ability implements Localizable {
|
||||||
@ -110,9 +109,13 @@ export class Ability implements Localizable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
localize(): void {
|
localize(): void {
|
||||||
const i18nKey = toCamelCase(AbilityId[this.id]);
|
const i18nKey = AbilityId[this.id]
|
||||||
|
.split("_")
|
||||||
|
.filter(f => f)
|
||||||
|
.map((f, i) => (i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()))
|
||||||
|
.join("") as string;
|
||||||
|
|
||||||
this.name = this.id ? `${i18next.t(`ability:${i18nKey}.name`)}${this.nameAppend}` : "";
|
this.name = this.id ? `${i18next.t(`ability:${i18nKey}.name`) as string}${this.nameAppend}` : "";
|
||||||
this.description = this.id ? (i18next.t(`ability:${i18nKey}.description`) as string) : "";
|
this.description = this.id ? (i18next.t(`ability:${i18nKey}.description`) as string) : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1866,16 +1866,17 @@ interface PokemonPrevolutions {
|
|||||||
export const pokemonPrevolutions: PokemonPrevolutions = {};
|
export const pokemonPrevolutions: PokemonPrevolutions = {};
|
||||||
|
|
||||||
export function initPokemonPrevolutions(): void {
|
export function initPokemonPrevolutions(): void {
|
||||||
// TODO: Why do we have empty strings in our array?
|
const megaFormKeys = [ SpeciesFormKey.MEGA, "", SpeciesFormKey.MEGA_X, "", SpeciesFormKey.MEGA_Y ].map(sfk => sfk as string);
|
||||||
const megaFormKeys = [ SpeciesFormKey.MEGA, "", SpeciesFormKey.MEGA_X, "", SpeciesFormKey.MEGA_Y ];
|
const prevolutionKeys = Object.keys(pokemonEvolutions);
|
||||||
for (const [pk, evolutions] of Object.entries(pokemonEvolutions)) {
|
prevolutionKeys.forEach(pk => {
|
||||||
|
const evolutions = pokemonEvolutions[pk];
|
||||||
for (const ev of evolutions) {
|
for (const ev of evolutions) {
|
||||||
if (ev.evoFormKey && megaFormKeys.indexOf(ev.evoFormKey) > -1) {
|
if (ev.evoFormKey && megaFormKeys.indexOf(ev.evoFormKey) > -1) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
pokemonPrevolutions[ev.speciesId] = Number.parseInt(pk) as SpeciesId;
|
pokemonPrevolutions[ev.speciesId] = Number.parseInt(pk) as SpeciesId;
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ import type { ChargingMove, MoveAttrMap, MoveAttrString, MoveClassMap, MoveKindS
|
|||||||
import type { TurnMove } from "#types/turn-move";
|
import type { TurnMove } from "#types/turn-move";
|
||||||
import { BooleanHolder, type Constructor, isNullOrUndefined, NumberHolder, randSeedFloat, randSeedInt, randSeedItem, toDmgValue } from "#utils/common";
|
import { BooleanHolder, type Constructor, isNullOrUndefined, NumberHolder, randSeedFloat, randSeedInt, randSeedItem, toDmgValue } from "#utils/common";
|
||||||
import { getEnumValues } from "#utils/enums";
|
import { getEnumValues } from "#utils/enums";
|
||||||
import { toCamelCase, toTitleCase } from "#utils/strings";
|
import { toTitleCase } from "#utils/strings";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { applyChallenges } from "#utils/challenge-utils";
|
import { applyChallenges } from "#utils/challenge-utils";
|
||||||
|
|
||||||
@ -162,16 +162,10 @@ export abstract class Move implements Localizable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
localize(): void {
|
localize(): void {
|
||||||
const i18nKey = toCamelCase(MoveId[this.id])
|
const i18nKey = MoveId[this.id].split("_").filter(f => f).map((f, i) => i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()).join("") as unknown as string;
|
||||||
|
|
||||||
if (this.id === MoveId.NONE) {
|
this.name = this.id ? `${i18next.t(`move:${i18nKey}.name`)}${this.nameAppend}` : "";
|
||||||
this.name = "";
|
this.effect = this.id ? `${i18next.t(`move:${i18nKey}.effect`)}${this.nameAppend}` : "";
|
||||||
this.effect = ""
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.name = `${i18next.t(`move:${i18nKey}.name`)}${this.nameAppend}`;
|
|
||||||
this.effect = `${i18next.t(`move:${i18nKey}.effect`)}${this.nameAppend}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -5932,8 +5926,8 @@ export class ProtectAttr extends AddBattlerTagAttr {
|
|||||||
for (const turnMove of user.getLastXMoves(-1).slice()) {
|
for (const turnMove of user.getLastXMoves(-1).slice()) {
|
||||||
if (
|
if (
|
||||||
// Quick & Wide guard increment the Protect counter without using it for fail chance
|
// Quick & Wide guard increment the Protect counter without using it for fail chance
|
||||||
!(allMoves[turnMove.move].hasAttr("ProtectAttr") ||
|
!(allMoves[turnMove.move].hasAttr("ProtectAttr") ||
|
||||||
[MoveId.QUICK_GUARD, MoveId.WIDE_GUARD].includes(turnMove.move)) ||
|
[MoveId.QUICK_GUARD, MoveId.WIDE_GUARD].includes(turnMove.move)) ||
|
||||||
turnMove.result !== MoveResult.SUCCESS
|
turnMove.result !== MoveResult.SUCCESS
|
||||||
) {
|
) {
|
||||||
break;
|
break;
|
||||||
|
@ -12,7 +12,6 @@ import { WeatherType } from "#enums/weather-type";
|
|||||||
import type { Pokemon } from "#field/pokemon";
|
import type { Pokemon } from "#field/pokemon";
|
||||||
import type { PokemonFormChangeItemModifier } from "#modifiers/modifier";
|
import type { PokemonFormChangeItemModifier } from "#modifiers/modifier";
|
||||||
import { type Constructor, coerceArray } from "#utils/common";
|
import { type Constructor, coerceArray } from "#utils/common";
|
||||||
import { toCamelCase } from "#utils/strings";
|
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
|
||||||
export abstract class SpeciesFormChangeTrigger {
|
export abstract class SpeciesFormChangeTrigger {
|
||||||
@ -144,7 +143,11 @@ export class SpeciesFormChangeMoveLearnedTrigger extends SpeciesFormChangeTrigge
|
|||||||
super();
|
super();
|
||||||
this.move = move;
|
this.move = move;
|
||||||
this.known = known;
|
this.known = known;
|
||||||
const moveKey = toCamelCase(MoveId[this.move]);
|
const moveKey = MoveId[this.move]
|
||||||
|
.split("_")
|
||||||
|
.filter(f => f)
|
||||||
|
.map((f, i) => (i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()))
|
||||||
|
.join("") as unknown as string;
|
||||||
this.description = known
|
this.description = known
|
||||||
? i18next.t("pokemonEvolutions:Forms.moveLearned", {
|
? i18next.t("pokemonEvolutions:Forms.moveLearned", {
|
||||||
move: i18next.t(`move:${moveKey}.name`),
|
move: i18next.t(`move:${moveKey}.name`),
|
||||||
|
@ -38,7 +38,6 @@ export enum UiMode {
|
|||||||
UNAVAILABLE,
|
UNAVAILABLE,
|
||||||
CHALLENGE_SELECT,
|
CHALLENGE_SELECT,
|
||||||
RENAME_POKEMON,
|
RENAME_POKEMON,
|
||||||
RENAME_RUN,
|
|
||||||
RUN_HISTORY,
|
RUN_HISTORY,
|
||||||
RUN_INFO,
|
RUN_INFO,
|
||||||
TEST_DIALOGUE,
|
TEST_DIALOGUE,
|
||||||
|
@ -54,7 +54,7 @@ export class Arena {
|
|||||||
public bgm: string;
|
public bgm: string;
|
||||||
public ignoreAbilities: boolean;
|
public ignoreAbilities: boolean;
|
||||||
public ignoringEffectSource: BattlerIndex | null;
|
public ignoringEffectSource: BattlerIndex | null;
|
||||||
public playerTerasUsed = 0;
|
public playerTerasUsed: number;
|
||||||
/**
|
/**
|
||||||
* Saves the number of times a party pokemon faints during a arena encounter.
|
* Saves the number of times a party pokemon faints during a arena encounter.
|
||||||
* {@linkcode globalScene.currentBattle.enemyFaints} is the corresponding faint counter for the enemy (this resets every wave).
|
* {@linkcode globalScene.currentBattle.enemyFaints} is the corresponding faint counter for the enemy (this resets every wave).
|
||||||
@ -68,11 +68,12 @@ export class Arena {
|
|||||||
|
|
||||||
public readonly eventTarget: EventTarget = new EventTarget();
|
public readonly eventTarget: EventTarget = new EventTarget();
|
||||||
|
|
||||||
constructor(biome: BiomeId, playerFaints = 0) {
|
constructor(biome: BiomeId, bgm: string, playerFaints = 0) {
|
||||||
this.biomeType = biome;
|
this.biomeType = biome;
|
||||||
this.bgm = BiomeId[biome].toLowerCase();
|
this.bgm = bgm;
|
||||||
this.trainerPool = biomeTrainerPools[biome];
|
this.trainerPool = biomeTrainerPools[biome];
|
||||||
this.updatePoolsForTimeOfDay();
|
this.updatePoolsForTimeOfDay();
|
||||||
|
this.playerTerasUsed = 0;
|
||||||
this.playerFaints = playerFaints;
|
this.playerFaints = playerFaints;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -447,9 +447,7 @@ export class LoadingScene extends SceneBase {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!mobile) {
|
if (!mobile) {
|
||||||
loadingGraphics.forEach(g => {
|
loadingGraphics.map(g => g.setVisible(false));
|
||||||
g.setVisible(false);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const intro = this.add.video(0, 0);
|
const intro = this.add.video(0, 0);
|
||||||
|
@ -121,8 +121,8 @@ export class ModifierBar extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateModifierOverflowVisibility(ignoreLimit: boolean) {
|
updateModifierOverflowVisibility(ignoreLimit: boolean) {
|
||||||
const modifierIcons = this.getAll().reverse() as Phaser.GameObjects.Container[];
|
const modifierIcons = this.getAll().reverse();
|
||||||
for (const modifier of modifierIcons.slice(iconOverflowIndex)) {
|
for (const modifier of modifierIcons.map(m => m as Phaser.GameObjects.Container).slice(iconOverflowIndex)) {
|
||||||
modifier.setVisible(ignoreLimit);
|
modifier.setVisible(ignoreLimit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,7 +127,6 @@ export interface SessionSaveData {
|
|||||||
battleType: BattleType;
|
battleType: BattleType;
|
||||||
trainer: TrainerData;
|
trainer: TrainerData;
|
||||||
gameVersion: string;
|
gameVersion: string;
|
||||||
runNameText: string;
|
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
challenges: ChallengeData[];
|
challenges: ChallengeData[];
|
||||||
mysteryEncounterType: MysteryEncounterType | -1; // Only defined when current wave is ME,
|
mysteryEncounterType: MysteryEncounterType | -1; // Only defined when current wave is ME,
|
||||||
@ -207,12 +206,10 @@ export interface StarterData {
|
|||||||
[key: number]: StarterDataEntry;
|
[key: number]: StarterDataEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Rework into a bitmask
|
export interface TutorialFlags {
|
||||||
export type TutorialFlags = {
|
[key: string]: boolean;
|
||||||
[key in Tutorial]: boolean;
|
}
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: Rework into a bitmask
|
|
||||||
export interface SeenDialogues {
|
export interface SeenDialogues {
|
||||||
[key: string]: boolean;
|
[key: string]: boolean;
|
||||||
}
|
}
|
||||||
@ -825,51 +822,52 @@ export class GameData {
|
|||||||
return true; // TODO: is `true` the correct return value?
|
return true; // TODO: is `true` the correct return value?
|
||||||
}
|
}
|
||||||
|
|
||||||
private loadGamepadSettings(): void {
|
private loadGamepadSettings(): boolean {
|
||||||
Object.values(SettingGamepad).forEach(setting => {
|
Object.values(SettingGamepad)
|
||||||
setSettingGamepad(setting, settingGamepadDefaults[setting]);
|
.map(setting => setting as SettingGamepad)
|
||||||
});
|
.forEach(setting => setSettingGamepad(setting, settingGamepadDefaults[setting]));
|
||||||
|
|
||||||
if (!localStorage.hasOwnProperty("settingsGamepad")) {
|
if (!localStorage.hasOwnProperty("settingsGamepad")) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
const settingsGamepad = JSON.parse(localStorage.getItem("settingsGamepad")!); // TODO: is this bang correct?
|
const settingsGamepad = JSON.parse(localStorage.getItem("settingsGamepad")!); // TODO: is this bang correct?
|
||||||
|
|
||||||
for (const setting of Object.keys(settingsGamepad)) {
|
for (const setting of Object.keys(settingsGamepad)) {
|
||||||
setSettingGamepad(setting as SettingGamepad, settingsGamepad[setting]);
|
setSettingGamepad(setting as SettingGamepad, settingsGamepad[setting]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true; // TODO: is `true` the correct return value?
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public saveTutorialFlag(tutorial: Tutorial, flag: boolean): boolean {
|
||||||
* Save the specified tutorial as having the specified completion status.
|
const key = getDataTypeKey(GameDataType.TUTORIALS);
|
||||||
* @param tutorial - The {@linkcode Tutorial} whose completion status is being saved
|
let tutorials: object = {};
|
||||||
* @param status - The completion status to set
|
if (localStorage.hasOwnProperty(key)) {
|
||||||
*/
|
tutorials = JSON.parse(localStorage.getItem(key)!); // TODO: is this bang correct?
|
||||||
public saveTutorialFlag(tutorial: Tutorial, status: boolean): void {
|
|
||||||
// Grab the prior save data tutorial
|
|
||||||
const saveDataKey = getDataTypeKey(GameDataType.TUTORIALS);
|
|
||||||
const tutorials: TutorialFlags = localStorage.hasOwnProperty(saveDataKey)
|
|
||||||
? JSON.parse(localStorage.getItem(saveDataKey)!)
|
|
||||||
: {};
|
|
||||||
|
|
||||||
// TODO: We shouldn't be storing this like that
|
|
||||||
for (const key of Object.values(Tutorial)) {
|
|
||||||
if (key === tutorial) {
|
|
||||||
tutorials[key] = status;
|
|
||||||
} else {
|
|
||||||
tutorials[key] ??= false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
localStorage.setItem(saveDataKey, JSON.stringify(tutorials));
|
Object.keys(Tutorial)
|
||||||
|
.map(t => t as Tutorial)
|
||||||
|
.forEach(t => {
|
||||||
|
const key = Tutorial[t];
|
||||||
|
if (key === tutorial) {
|
||||||
|
tutorials[key] = flag;
|
||||||
|
} else {
|
||||||
|
tutorials[key] ??= false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
localStorage.setItem(key, JSON.stringify(tutorials));
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTutorialFlags(): TutorialFlags {
|
public getTutorialFlags(): TutorialFlags {
|
||||||
const key = getDataTypeKey(GameDataType.TUTORIALS);
|
const key = getDataTypeKey(GameDataType.TUTORIALS);
|
||||||
const ret: TutorialFlags = Object.values(Tutorial).reduce((acc, tutorial) => {
|
const ret: TutorialFlags = {};
|
||||||
acc[Tutorial[tutorial]] = false;
|
Object.values(Tutorial)
|
||||||
return acc;
|
.map(tutorial => tutorial as Tutorial)
|
||||||
}, {} as TutorialFlags);
|
.forEach(tutorial => (ret[Tutorial[tutorial]] = false));
|
||||||
|
|
||||||
if (!localStorage.hasOwnProperty(key)) {
|
if (!localStorage.hasOwnProperty(key)) {
|
||||||
return ret;
|
return ret;
|
||||||
@ -981,54 +979,6 @@ export class GameData {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async renameSession(slotId: number, newName: string): Promise<boolean> {
|
|
||||||
return new Promise(async resolve => {
|
|
||||||
if (slotId < 0) {
|
|
||||||
return resolve(false);
|
|
||||||
}
|
|
||||||
const sessionData: SessionSaveData | null = await this.getSession(slotId);
|
|
||||||
|
|
||||||
if (!sessionData) {
|
|
||||||
return resolve(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newName === "") {
|
|
||||||
return resolve(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
sessionData.runNameText = newName;
|
|
||||||
const updatedDataStr = JSON.stringify(sessionData);
|
|
||||||
const encrypted = encrypt(updatedDataStr, bypassLogin);
|
|
||||||
const secretId = this.secretId;
|
|
||||||
const trainerId = this.trainerId;
|
|
||||||
|
|
||||||
if (bypassLogin) {
|
|
||||||
localStorage.setItem(
|
|
||||||
`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`,
|
|
||||||
encrypt(updatedDataStr, bypassLogin),
|
|
||||||
);
|
|
||||||
resolve(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pokerogueApi.savedata.session
|
|
||||||
.update({ slot: slotId, trainerId, secretId, clientSessionId }, encrypted)
|
|
||||||
.then(error => {
|
|
||||||
if (error) {
|
|
||||||
console.error("Failed to update session name:", error);
|
|
||||||
resolve(false);
|
|
||||||
} else {
|
|
||||||
localStorage.setItem(`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`, encrypted);
|
|
||||||
updateUserInfo().then(success => {
|
|
||||||
if (success !== null && !success) {
|
|
||||||
return resolve(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
resolve(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSession(slotId: number, sessionData?: SessionSaveData): Promise<boolean> {
|
loadSession(slotId: number, sessionData?: SessionSaveData): Promise<boolean> {
|
||||||
// biome-ignore lint/suspicious/noAsyncPromiseExecutor: TODO: fix this
|
// biome-ignore lint/suspicious/noAsyncPromiseExecutor: TODO: fix this
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
|
@ -1,54 +0,0 @@
|
|||||||
import i18next from "i18next";
|
|
||||||
import type { InputFieldConfig } from "./form-modal-ui-handler";
|
|
||||||
import { FormModalUiHandler } from "./form-modal-ui-handler";
|
|
||||||
import type { ModalConfig } from "./modal-ui-handler";
|
|
||||||
|
|
||||||
export class RenameRunFormUiHandler extends FormModalUiHandler {
|
|
||||||
getModalTitle(_config?: ModalConfig): string {
|
|
||||||
return i18next.t("menu:renamerun");
|
|
||||||
}
|
|
||||||
|
|
||||||
getWidth(_config?: ModalConfig): number {
|
|
||||||
return 160;
|
|
||||||
}
|
|
||||||
|
|
||||||
getMargin(_config?: ModalConfig): [number, number, number, number] {
|
|
||||||
return [0, 0, 48, 0];
|
|
||||||
}
|
|
||||||
|
|
||||||
getButtonLabels(_config?: ModalConfig): string[] {
|
|
||||||
return [i18next.t("menu:rename"), i18next.t("menu:cancel")];
|
|
||||||
}
|
|
||||||
|
|
||||||
getReadableErrorMessage(error: string): string {
|
|
||||||
const colonIndex = error?.indexOf(":");
|
|
||||||
if (colonIndex > 0) {
|
|
||||||
error = error.slice(0, colonIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.getReadableErrorMessage(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
override getInputFieldConfigs(): InputFieldConfig[] {
|
|
||||||
return [{ label: i18next.t("menu:runName") }];
|
|
||||||
}
|
|
||||||
|
|
||||||
show(args: any[]): boolean {
|
|
||||||
if (!super.show(args)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (this.inputs?.length) {
|
|
||||||
this.inputs.forEach(input => {
|
|
||||||
input.text = "";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const config = args[0] as ModalConfig;
|
|
||||||
this.submitAction = _ => {
|
|
||||||
this.sanitizeInputs();
|
|
||||||
const sanitizedName = btoa(encodeURIComponent(this.inputs[0].text));
|
|
||||||
config.buttonActions[0](sanitizedName);
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -26,7 +26,6 @@ import { addBBCodeTextObject, addTextObject, getTextColor } from "#ui/text";
|
|||||||
import { UiHandler } from "#ui/ui-handler";
|
import { UiHandler } from "#ui/ui-handler";
|
||||||
import { addWindow } from "#ui/ui-theme";
|
import { addWindow } from "#ui/ui-theme";
|
||||||
import { formatFancyLargeNumber, formatLargeNumber, formatMoney, getPlayTimeString } from "#utils/common";
|
import { formatFancyLargeNumber, formatLargeNumber, formatMoney, getPlayTimeString } from "#utils/common";
|
||||||
import { toCamelCase } from "#utils/strings";
|
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import RoundRectangle from "phaser3-rex-plugins/plugins/roundrectangle";
|
import RoundRectangle from "phaser3-rex-plugins/plugins/roundrectangle";
|
||||||
|
|
||||||
@ -208,10 +207,6 @@ export class RunInfoUiHandler extends UiHandler {
|
|||||||
headerText.setOrigin(0, 0);
|
headerText.setOrigin(0, 0);
|
||||||
headerText.setPositionRelative(headerBg, 8, 4);
|
headerText.setPositionRelative(headerBg, 8, 4);
|
||||||
this.runContainer.add(headerText);
|
this.runContainer.add(headerText);
|
||||||
const runName = addTextObject(0, 0, this.runInfo.runNameText, TextStyle.WINDOW);
|
|
||||||
runName.setOrigin(0, 0);
|
|
||||||
runName.setPositionRelative(headerBg, 60, 4);
|
|
||||||
this.runContainer.add(runName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -707,7 +702,10 @@ export class RunInfoUiHandler extends UiHandler {
|
|||||||
rules.push(i18next.t("challenges:inverseBattle.shortName"));
|
rules.push(i18next.t("challenges:inverseBattle.shortName"));
|
||||||
break;
|
break;
|
||||||
default: {
|
default: {
|
||||||
const localizationKey = toCamelCase(Challenges[this.runInfo.challenges[i].id]);
|
const localizationKey = Challenges[this.runInfo.challenges[i].id]
|
||||||
|
.split("_")
|
||||||
|
.map((f, i) => (i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()))
|
||||||
|
.join("");
|
||||||
rules.push(i18next.t(`challenges:${localizationKey}.name`));
|
rules.push(i18next.t(`challenges:${localizationKey}.name`));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
import { GameMode } from "#app/game-mode";
|
import { GameMode } from "#app/game-mode";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { Button } from "#enums/buttons";
|
import { Button } from "#enums/buttons";
|
||||||
import { GameModes } from "#enums/game-modes";
|
|
||||||
import { TextStyle } from "#enums/text-style";
|
import { TextStyle } from "#enums/text-style";
|
||||||
import { UiMode } from "#enums/ui-mode";
|
import { UiMode } from "#enums/ui-mode";
|
||||||
// biome-ignore lint/performance/noNamespaceImport: See `src/system/game-data.ts`
|
// biome-ignore lint/performance/noNamespaceImport: See `src/system/game-data.ts`
|
||||||
import * as Modifier from "#modifiers/modifier";
|
import * as Modifier from "#modifiers/modifier";
|
||||||
import type { SessionSaveData } from "#system/game-data";
|
import type { SessionSaveData } from "#system/game-data";
|
||||||
import type { PokemonData } from "#system/pokemon-data";
|
import type { PokemonData } from "#system/pokemon-data";
|
||||||
import type { OptionSelectConfig } from "#ui/abstract-option-select-ui-handler";
|
|
||||||
import { MessageUiHandler } from "#ui/message-ui-handler";
|
import { MessageUiHandler } from "#ui/message-ui-handler";
|
||||||
import { RunDisplayMode } from "#ui/run-info-ui-handler";
|
import { RunDisplayMode } from "#ui/run-info-ui-handler";
|
||||||
import { addTextObject } from "#ui/text";
|
import { addTextObject } from "#ui/text";
|
||||||
@ -17,7 +15,7 @@ import { fixedInt, formatLargeNumber, getPlayTimeString, isNullOrUndefined } fro
|
|||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
|
||||||
const SESSION_SLOTS_COUNT = 5;
|
const SESSION_SLOTS_COUNT = 5;
|
||||||
const SLOTS_ON_SCREEN = 2;
|
const SLOTS_ON_SCREEN = 3;
|
||||||
|
|
||||||
export enum SaveSlotUiMode {
|
export enum SaveSlotUiMode {
|
||||||
LOAD,
|
LOAD,
|
||||||
@ -35,7 +33,6 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler {
|
|||||||
|
|
||||||
private uiMode: SaveSlotUiMode;
|
private uiMode: SaveSlotUiMode;
|
||||||
private saveSlotSelectCallback: SaveSlotSelectCallback | null;
|
private saveSlotSelectCallback: SaveSlotSelectCallback | null;
|
||||||
protected manageDataConfig: OptionSelectConfig;
|
|
||||||
|
|
||||||
private scrollCursor = 0;
|
private scrollCursor = 0;
|
||||||
|
|
||||||
@ -104,7 +101,6 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler {
|
|||||||
|
|
||||||
processInput(button: Button): boolean {
|
processInput(button: Button): boolean {
|
||||||
const ui = this.getUi();
|
const ui = this.getUi();
|
||||||
const manageDataOptions: any[] = [];
|
|
||||||
|
|
||||||
let success = false;
|
let success = false;
|
||||||
let error = false;
|
let error = false;
|
||||||
@ -113,115 +109,14 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler {
|
|||||||
const originalCallback = this.saveSlotSelectCallback;
|
const originalCallback = this.saveSlotSelectCallback;
|
||||||
if (button === Button.ACTION) {
|
if (button === Button.ACTION) {
|
||||||
const cursor = this.cursor + this.scrollCursor;
|
const cursor = this.cursor + this.scrollCursor;
|
||||||
const sessionSlot = this.sessionSlots[cursor];
|
if (this.uiMode === SaveSlotUiMode.LOAD && !this.sessionSlots[cursor].hasData) {
|
||||||
if (this.uiMode === SaveSlotUiMode.LOAD && !sessionSlot.hasData) {
|
|
||||||
error = true;
|
error = true;
|
||||||
} else {
|
} else {
|
||||||
switch (this.uiMode) {
|
switch (this.uiMode) {
|
||||||
case SaveSlotUiMode.LOAD:
|
case SaveSlotUiMode.LOAD:
|
||||||
if (!sessionSlot.malformed) {
|
this.saveSlotSelectCallback = null;
|
||||||
manageDataOptions.push({
|
originalCallback?.(cursor);
|
||||||
label: i18next.t("menu:loadGame"),
|
|
||||||
handler: () => {
|
|
||||||
globalScene.ui.revertMode();
|
|
||||||
originalCallback?.(cursor);
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
keepOpen: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
manageDataOptions.push({
|
|
||||||
label: i18next.t("saveSlotSelectUiHandler:renameRun"),
|
|
||||||
handler: () => {
|
|
||||||
globalScene.ui.revertMode();
|
|
||||||
ui.setOverlayMode(
|
|
||||||
UiMode.RENAME_RUN,
|
|
||||||
{
|
|
||||||
buttonActions: [
|
|
||||||
(sanitizedName: string) => {
|
|
||||||
const name = decodeURIComponent(atob(sanitizedName));
|
|
||||||
globalScene.gameData.renameSession(cursor, name).then(response => {
|
|
||||||
if (response[0] === false) {
|
|
||||||
globalScene.reset(true);
|
|
||||||
} else {
|
|
||||||
this.clearSessionSlots();
|
|
||||||
this.cursorObj = null;
|
|
||||||
this.populateSessionSlots();
|
|
||||||
this.setScrollCursor(0);
|
|
||||||
this.setCursor(0);
|
|
||||||
ui.revertMode();
|
|
||||||
ui.showText("", 0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
ui.revertMode();
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
"",
|
|
||||||
);
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.manageDataConfig = {
|
|
||||||
xOffset: 0,
|
|
||||||
yOffset: 48,
|
|
||||||
options: manageDataOptions,
|
|
||||||
maxOptions: 4,
|
|
||||||
};
|
|
||||||
|
|
||||||
manageDataOptions.push({
|
|
||||||
label: i18next.t("saveSlotSelectUiHandler:deleteRun"),
|
|
||||||
handler: () => {
|
|
||||||
globalScene.ui.revertMode();
|
|
||||||
ui.showText(i18next.t("saveSlotSelectUiHandler:deleteData"), null, () => {
|
|
||||||
ui.setOverlayMode(
|
|
||||||
UiMode.CONFIRM,
|
|
||||||
() => {
|
|
||||||
globalScene.gameData.tryClearSession(cursor).then(response => {
|
|
||||||
if (response[0] === false) {
|
|
||||||
globalScene.reset(true);
|
|
||||||
} else {
|
|
||||||
this.clearSessionSlots();
|
|
||||||
this.cursorObj = null;
|
|
||||||
this.populateSessionSlots();
|
|
||||||
this.setScrollCursor(0);
|
|
||||||
this.setCursor(0);
|
|
||||||
ui.revertMode();
|
|
||||||
ui.showText("", 0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
ui.revertMode();
|
|
||||||
ui.showText("", 0);
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
0,
|
|
||||||
19,
|
|
||||||
import.meta.env.DEV ? 300 : 2000,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
keepOpen: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
manageDataOptions.push({
|
|
||||||
label: i18next.t("menuUiHandler:cancel"),
|
|
||||||
handler: () => {
|
|
||||||
globalScene.ui.revertMode();
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
keepOpen: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
ui.setOverlayMode(UiMode.MENU_OPTION_SELECT, this.manageDataConfig);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SaveSlotUiMode.SAVE: {
|
case SaveSlotUiMode.SAVE: {
|
||||||
const saveAndCallback = () => {
|
const saveAndCallback = () => {
|
||||||
const originalCallback = this.saveSlotSelectCallback;
|
const originalCallback = this.saveSlotSelectCallback;
|
||||||
@ -266,7 +161,6 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.saveSlotSelectCallback = null;
|
this.saveSlotSelectCallback = null;
|
||||||
ui.showText("", 0);
|
|
||||||
originalCallback?.(-1);
|
originalCallback?.(-1);
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
@ -373,34 +267,33 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler {
|
|||||||
this.cursorObj = globalScene.add.container(0, 0);
|
this.cursorObj = globalScene.add.container(0, 0);
|
||||||
const cursorBox = globalScene.add.nineslice(
|
const cursorBox = globalScene.add.nineslice(
|
||||||
0,
|
0,
|
||||||
15,
|
0,
|
||||||
"select_cursor_highlight_thick",
|
"select_cursor_highlight_thick",
|
||||||
undefined,
|
undefined,
|
||||||
294,
|
296,
|
||||||
this.sessionSlots[prevSlotIndex ?? 0]?.saveData?.runNameText ? 50 : 60,
|
44,
|
||||||
6,
|
6,
|
||||||
6,
|
6,
|
||||||
6,
|
6,
|
||||||
6,
|
6,
|
||||||
);
|
);
|
||||||
const rightArrow = globalScene.add.image(0, 0, "cursor");
|
const rightArrow = globalScene.add.image(0, 0, "cursor");
|
||||||
rightArrow.setPosition(160, 15);
|
rightArrow.setPosition(160, 0);
|
||||||
rightArrow.setName("rightArrow");
|
rightArrow.setName("rightArrow");
|
||||||
this.cursorObj.add([cursorBox, rightArrow]);
|
this.cursorObj.add([cursorBox, rightArrow]);
|
||||||
this.sessionSlotsContainer.add(this.cursorObj);
|
this.sessionSlotsContainer.add(this.cursorObj);
|
||||||
}
|
}
|
||||||
const cursorPosition = cursor + this.scrollCursor;
|
const cursorPosition = cursor + this.scrollCursor;
|
||||||
const cursorIncrement = cursorPosition * 76;
|
const cursorIncrement = cursorPosition * 56;
|
||||||
if (this.sessionSlots[cursorPosition] && this.cursorObj) {
|
if (this.sessionSlots[cursorPosition] && this.cursorObj) {
|
||||||
const session = this.sessionSlots[cursorPosition];
|
const hasData = this.sessionSlots[cursorPosition].hasData;
|
||||||
const hasData = session.hasData && !session.malformed;
|
|
||||||
// If the session slot lacks session data, it does not move from its default, central position.
|
// If the session slot lacks session data, it does not move from its default, central position.
|
||||||
// Only session slots with session data will move leftwards and have a visible arrow.
|
// Only session slots with session data will move leftwards and have a visible arrow.
|
||||||
if (!hasData) {
|
if (!hasData) {
|
||||||
this.cursorObj.setPosition(151, 20 + cursorIncrement);
|
this.cursorObj.setPosition(151, 26 + cursorIncrement);
|
||||||
this.sessionSlots[cursorPosition].setPosition(0, cursorIncrement);
|
this.sessionSlots[cursorPosition].setPosition(0, cursorIncrement);
|
||||||
} else {
|
} else {
|
||||||
this.cursorObj.setPosition(145, 20 + cursorIncrement);
|
this.cursorObj.setPosition(145, 26 + cursorIncrement);
|
||||||
this.sessionSlots[cursorPosition].setPosition(-6, cursorIncrement);
|
this.sessionSlots[cursorPosition].setPosition(-6, cursorIncrement);
|
||||||
}
|
}
|
||||||
this.setArrowVisibility(hasData);
|
this.setArrowVisibility(hasData);
|
||||||
@ -418,8 +311,7 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler {
|
|||||||
revertSessionSlot(slotIndex: number): void {
|
revertSessionSlot(slotIndex: number): void {
|
||||||
const sessionSlot = this.sessionSlots[slotIndex];
|
const sessionSlot = this.sessionSlots[slotIndex];
|
||||||
if (sessionSlot) {
|
if (sessionSlot) {
|
||||||
const valueHeight = 76;
|
sessionSlot.setPosition(0, slotIndex * 56);
|
||||||
sessionSlot.setPosition(0, slotIndex * valueHeight);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -448,7 +340,7 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler {
|
|||||||
this.setCursor(this.cursor, prevSlotIndex);
|
this.setCursor(this.cursor, prevSlotIndex);
|
||||||
globalScene.tweens.add({
|
globalScene.tweens.add({
|
||||||
targets: this.sessionSlotsContainer,
|
targets: this.sessionSlotsContainer,
|
||||||
y: this.sessionSlotsContainerInitialY - 76 * scrollCursor,
|
y: this.sessionSlotsContainerInitialY - 56 * scrollCursor,
|
||||||
duration: fixedInt(325),
|
duration: fixedInt(325),
|
||||||
ease: "Sine.easeInOut",
|
ease: "Sine.easeInOut",
|
||||||
});
|
});
|
||||||
@ -482,14 +374,12 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler {
|
|||||||
class SessionSlot extends Phaser.GameObjects.Container {
|
class SessionSlot extends Phaser.GameObjects.Container {
|
||||||
public slotId: number;
|
public slotId: number;
|
||||||
public hasData: boolean;
|
public hasData: boolean;
|
||||||
/** Indicates the save slot ran into an error while being loaded */
|
|
||||||
public malformed: boolean;
|
|
||||||
private slotWindow: Phaser.GameObjects.NineSlice;
|
|
||||||
private loadingLabel: Phaser.GameObjects.Text;
|
private loadingLabel: Phaser.GameObjects.Text;
|
||||||
|
|
||||||
public saveData: SessionSaveData;
|
public saveData: SessionSaveData;
|
||||||
|
|
||||||
constructor(slotId: number) {
|
constructor(slotId: number) {
|
||||||
super(globalScene, 0, slotId * 76);
|
super(globalScene, 0, slotId * 56);
|
||||||
|
|
||||||
this.slotId = slotId;
|
this.slotId = slotId;
|
||||||
|
|
||||||
@ -497,89 +387,32 @@ class SessionSlot extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
this.slotWindow = addWindow(0, 0, 304, 70);
|
const slotWindow = addWindow(0, 0, 304, 52);
|
||||||
this.add(this.slotWindow);
|
this.add(slotWindow);
|
||||||
|
|
||||||
this.loadingLabel = addTextObject(152, 33, i18next.t("saveSlotSelectUiHandler:loading"), TextStyle.WINDOW);
|
this.loadingLabel = addTextObject(152, 26, i18next.t("saveSlotSelectUiHandler:loading"), TextStyle.WINDOW);
|
||||||
this.loadingLabel.setOrigin(0.5, 0.5);
|
this.loadingLabel.setOrigin(0.5, 0.5);
|
||||||
this.add(this.loadingLabel);
|
this.add(this.loadingLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a name for sessions that don't have a name yet.
|
|
||||||
* @param data - The {@linkcode SessionSaveData} being checked
|
|
||||||
* @returns The default name for the given data.
|
|
||||||
*/
|
|
||||||
decideFallback(data: SessionSaveData): string {
|
|
||||||
let fallbackName = `${GameMode.getModeName(data.gameMode)}`;
|
|
||||||
switch (data.gameMode) {
|
|
||||||
case GameModes.CLASSIC:
|
|
||||||
fallbackName += ` (${globalScene.gameData.gameStats.classicSessionsPlayed + 1})`;
|
|
||||||
break;
|
|
||||||
case GameModes.ENDLESS:
|
|
||||||
case GameModes.SPLICED_ENDLESS:
|
|
||||||
fallbackName += ` (${globalScene.gameData.gameStats.endlessSessionsPlayed + 1})`;
|
|
||||||
break;
|
|
||||||
case GameModes.DAILY: {
|
|
||||||
const runDay = new Date(data.timestamp).toLocaleDateString();
|
|
||||||
fallbackName += ` (${runDay})`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case GameModes.CHALLENGE: {
|
|
||||||
const activeChallenges = data.challenges.filter(c => c.value !== 0);
|
|
||||||
if (activeChallenges.length === 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
fallbackName = "";
|
|
||||||
for (const challenge of activeChallenges.slice(0, 3)) {
|
|
||||||
if (fallbackName !== "") {
|
|
||||||
fallbackName += ", ";
|
|
||||||
}
|
|
||||||
fallbackName += challenge.toChallenge().getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activeChallenges.length > 3) {
|
|
||||||
fallbackName += ", ...";
|
|
||||||
} else if (fallbackName === "") {
|
|
||||||
// Something went wrong when retrieving the names of the active challenges,
|
|
||||||
// so fall back to just naming the run "Challenge"
|
|
||||||
fallbackName = `${GameMode.getModeName(data.gameMode)}`;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fallbackName;
|
|
||||||
}
|
|
||||||
|
|
||||||
async setupWithData(data: SessionSaveData) {
|
async setupWithData(data: SessionSaveData) {
|
||||||
const hasName = data?.runNameText;
|
|
||||||
this.remove(this.loadingLabel, true);
|
this.remove(this.loadingLabel, true);
|
||||||
if (hasName) {
|
|
||||||
const nameLabel = addTextObject(8, 5, data.runNameText, TextStyle.WINDOW);
|
|
||||||
this.add(nameLabel);
|
|
||||||
} else {
|
|
||||||
const fallbackName = this.decideFallback(data);
|
|
||||||
await globalScene.gameData.renameSession(this.slotId, fallbackName);
|
|
||||||
const nameLabel = addTextObject(8, 5, fallbackName, TextStyle.WINDOW);
|
|
||||||
this.add(nameLabel);
|
|
||||||
}
|
|
||||||
|
|
||||||
const gameModeLabel = addTextObject(
|
const gameModeLabel = addTextObject(
|
||||||
8,
|
8,
|
||||||
19,
|
5,
|
||||||
`${GameMode.getModeName(data.gameMode) || i18next.t("gameMode:unknown")} - ${i18next.t("saveSlotSelectUiHandler:wave")} ${data.waveIndex}`,
|
`${GameMode.getModeName(data.gameMode) || i18next.t("gameMode:unknown")} - ${i18next.t("saveSlotSelectUiHandler:wave")} ${data.waveIndex}`,
|
||||||
TextStyle.WINDOW,
|
TextStyle.WINDOW,
|
||||||
);
|
);
|
||||||
this.add(gameModeLabel);
|
this.add(gameModeLabel);
|
||||||
|
|
||||||
const timestampLabel = addTextObject(8, 33, new Date(data.timestamp).toLocaleString(), TextStyle.WINDOW);
|
const timestampLabel = addTextObject(8, 19, new Date(data.timestamp).toLocaleString(), TextStyle.WINDOW);
|
||||||
this.add(timestampLabel);
|
this.add(timestampLabel);
|
||||||
|
|
||||||
const playTimeLabel = addTextObject(8, 47, getPlayTimeString(data.playTime), TextStyle.WINDOW);
|
const playTimeLabel = addTextObject(8, 33, getPlayTimeString(data.playTime), TextStyle.WINDOW);
|
||||||
this.add(playTimeLabel);
|
this.add(playTimeLabel);
|
||||||
|
|
||||||
const pokemonIconsContainer = globalScene.add.container(144, 16);
|
const pokemonIconsContainer = globalScene.add.container(144, 4);
|
||||||
data.party.forEach((p: PokemonData, i: number) => {
|
data.party.forEach((p: PokemonData, i: number) => {
|
||||||
const iconContainer = globalScene.add.container(26 * i, 0);
|
const iconContainer = globalScene.add.container(26 * i, 0);
|
||||||
iconContainer.setScale(0.75);
|
iconContainer.setScale(0.75);
|
||||||
@ -594,9 +427,13 @@ class SessionSlot extends Phaser.GameObjects.Container {
|
|||||||
TextStyle.PARTY,
|
TextStyle.PARTY,
|
||||||
{ fontSize: "54px", color: "#f8f8f8" },
|
{ fontSize: "54px", color: "#f8f8f8" },
|
||||||
);
|
);
|
||||||
text.setShadow(0, 0, undefined).setStroke("#424242", 14).setOrigin(1, 0);
|
text.setShadow(0, 0, undefined);
|
||||||
|
text.setStroke("#424242", 14);
|
||||||
|
text.setOrigin(1, 0);
|
||||||
|
|
||||||
|
iconContainer.add(icon);
|
||||||
|
iconContainer.add(text);
|
||||||
|
|
||||||
iconContainer.add([icon, text]);
|
|
||||||
pokemonIconsContainer.add(iconContainer);
|
pokemonIconsContainer.add(iconContainer);
|
||||||
|
|
||||||
pokemon.destroy();
|
pokemon.destroy();
|
||||||
@ -604,7 +441,7 @@ class SessionSlot extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
this.add(pokemonIconsContainer);
|
this.add(pokemonIconsContainer);
|
||||||
|
|
||||||
const modifierIconsContainer = globalScene.add.container(148, 38);
|
const modifierIconsContainer = globalScene.add.container(148, 30);
|
||||||
modifierIconsContainer.setScale(0.5);
|
modifierIconsContainer.setScale(0.5);
|
||||||
let visibleModifierIndex = 0;
|
let visibleModifierIndex = 0;
|
||||||
for (const m of data.modifiers) {
|
for (const m of data.modifiers) {
|
||||||
@ -627,33 +464,22 @@ class SessionSlot extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
load(): Promise<boolean> {
|
load(): Promise<boolean> {
|
||||||
return new Promise<boolean>(resolve => {
|
return new Promise<boolean>(resolve => {
|
||||||
globalScene.gameData
|
globalScene.gameData.getSession(this.slotId).then(async sessionData => {
|
||||||
.getSession(this.slotId)
|
// Ignore the results if the view was exited
|
||||||
.then(async sessionData => {
|
if (!this.active) {
|
||||||
// Ignore the results if the view was exited
|
return;
|
||||||
if (!this.active) {
|
}
|
||||||
return;
|
if (!sessionData) {
|
||||||
}
|
this.hasData = false;
|
||||||
this.hasData = !!sessionData;
|
this.loadingLabel.setText(i18next.t("saveSlotSelectUiHandler:empty"));
|
||||||
if (!sessionData) {
|
resolve(false);
|
||||||
this.loadingLabel.setText(i18next.t("saveSlotSelectUiHandler:empty"));
|
return;
|
||||||
resolve(false);
|
}
|
||||||
return;
|
this.hasData = true;
|
||||||
}
|
this.saveData = sessionData;
|
||||||
this.saveData = sessionData;
|
await this.setupWithData(sessionData);
|
||||||
this.setupWithData(sessionData);
|
resolve(true);
|
||||||
resolve(true);
|
});
|
||||||
})
|
|
||||||
.catch(e => {
|
|
||||||
if (!this.active) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.warn(`Failed to load session slot #${this.slotId}:`, e);
|
|
||||||
this.loadingLabel.setText(i18next.t("menu:failedToLoadSession"));
|
|
||||||
this.hasData = true;
|
|
||||||
this.malformed = true;
|
|
||||||
resolve(true);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ export class TestDialogueUiHandler extends FormModalUiHandler {
|
|||||||
// we check for null or undefined here as per above - the typeof is still an object but the value is null so we need to exit out of this and pass the null key
|
// we check for null or undefined here as per above - the typeof is still an object but the value is null so we need to exit out of this and pass the null key
|
||||||
|
|
||||||
// Return in the format expected by i18next
|
// Return in the format expected by i18next
|
||||||
return middleKey ? `${topKey}:${middleKey.join(".")}.${t}` : `${topKey}:${t}`;
|
return middleKey ? `${topKey}:${middleKey.map(m => m).join(".")}.${t}` : `${topKey}:${t}`;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.filter(t => t);
|
.filter(t => t);
|
||||||
|
@ -60,7 +60,6 @@ import { addWindow } from "#ui/ui-theme";
|
|||||||
import { UnavailableModalUiHandler } from "#ui/unavailable-modal-ui-handler";
|
import { UnavailableModalUiHandler } from "#ui/unavailable-modal-ui-handler";
|
||||||
import { executeIf } from "#utils/common";
|
import { executeIf } from "#utils/common";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { RenameRunFormUiHandler } from "./rename-run-ui-handler";
|
|
||||||
|
|
||||||
const transitionModes = [
|
const transitionModes = [
|
||||||
UiMode.SAVE_SLOT,
|
UiMode.SAVE_SLOT,
|
||||||
@ -99,7 +98,6 @@ const noTransitionModes = [
|
|||||||
UiMode.SESSION_RELOAD,
|
UiMode.SESSION_RELOAD,
|
||||||
UiMode.UNAVAILABLE,
|
UiMode.UNAVAILABLE,
|
||||||
UiMode.RENAME_POKEMON,
|
UiMode.RENAME_POKEMON,
|
||||||
UiMode.RENAME_RUN,
|
|
||||||
UiMode.TEST_DIALOGUE,
|
UiMode.TEST_DIALOGUE,
|
||||||
UiMode.AUTO_COMPLETE,
|
UiMode.AUTO_COMPLETE,
|
||||||
UiMode.ADMIN,
|
UiMode.ADMIN,
|
||||||
@ -170,7 +168,6 @@ export class UI extends Phaser.GameObjects.Container {
|
|||||||
new UnavailableModalUiHandler(),
|
new UnavailableModalUiHandler(),
|
||||||
new GameChallengesUiHandler(),
|
new GameChallengesUiHandler(),
|
||||||
new RenameFormUiHandler(),
|
new RenameFormUiHandler(),
|
||||||
new RenameRunFormUiHandler(),
|
|
||||||
new RunHistoryUiHandler(),
|
new RunHistoryUiHandler(),
|
||||||
new RunInfoUiHandler(),
|
new RunInfoUiHandler(),
|
||||||
new TestDialogueUiHandler(UiMode.TEST_DIALOGUE),
|
new TestDialogueUiHandler(UiMode.TEST_DIALOGUE),
|
||||||
|
@ -112,7 +112,7 @@ describe("Weird Dream - Mystery Encounter", () => {
|
|||||||
it("should transform the new party into new species, 2 at +90/+110, the rest at +40/50 BST", async () => {
|
it("should transform the new party into new species, 2 at +90/+110, the rest at +40/50 BST", async () => {
|
||||||
await game.runToMysteryEncounter(MysteryEncounterType.WEIRD_DREAM, defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.WEIRD_DREAM, defaultParty);
|
||||||
|
|
||||||
const pokemonPrior = scene.getPlayerParty().slice();
|
const pokemonPrior = scene.getPlayerParty().map(pokemon => pokemon);
|
||||||
const bstsPrior = pokemonPrior.map(species => species.getSpeciesForm().getBaseStatTotal());
|
const bstsPrior = pokemonPrior.map(species => species.getSpeciesForm().getBaseStatTotal());
|
||||||
|
|
||||||
await runMysteryEncounterToEnd(game, 1);
|
await runMysteryEncounterToEnd(game, 1);
|
||||||
|
@ -1,82 +0,0 @@
|
|||||||
import * as account from "#app/account";
|
|
||||||
import * as bypassLoginModule from "#app/global-vars/bypass-login";
|
|
||||||
import { pokerogueApi } from "#app/plugins/api/pokerogue-api";
|
|
||||||
import type { SessionSaveData } from "#app/system/game-data";
|
|
||||||
import { AbilityId } from "#enums/ability-id";
|
|
||||||
import { MoveId } from "#enums/move-id";
|
|
||||||
import { GameManager } from "#test/test-utils/game-manager";
|
|
||||||
import Phaser from "phaser";
|
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
|
||||||
|
|
||||||
describe("System - Rename Run", () => {
|
|
||||||
let phaserGame: Phaser.Game;
|
|
||||||
let game: GameManager;
|
|
||||||
|
|
||||||
beforeAll(() => {
|
|
||||||
phaserGame = new Phaser.Game({
|
|
||||||
type: Phaser.HEADLESS,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
game = new GameManager(phaserGame);
|
|
||||||
game.override
|
|
||||||
.moveset([MoveId.SPLASH])
|
|
||||||
.battleStyle("single")
|
|
||||||
.enemyAbility(AbilityId.BALL_FETCH)
|
|
||||||
.enemyMoveset(MoveId.SPLASH);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
game.phaseInterceptor.restoreOg();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("renameSession", () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
vi.spyOn(bypassLoginModule, "bypassLogin", "get").mockReturnValue(false);
|
|
||||||
vi.spyOn(account, "updateUserInfo").mockImplementation(async () => [true, 1]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return false if slotId < 0", async () => {
|
|
||||||
const result = await game.scene.gameData.renameSession(-1, "Named Run");
|
|
||||||
|
|
||||||
expect(result).toEqual(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return false if getSession returns null", async () => {
|
|
||||||
vi.spyOn(game.scene.gameData, "getSession").mockResolvedValue(null as unknown as SessionSaveData);
|
|
||||||
|
|
||||||
const result = await game.scene.gameData.renameSession(-1, "Named Run");
|
|
||||||
|
|
||||||
expect(result).toEqual(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return true if bypassLogin is true", async () => {
|
|
||||||
vi.spyOn(bypassLoginModule, "bypassLogin", "get").mockReturnValue(true);
|
|
||||||
vi.spyOn(game.scene.gameData, "getSession").mockResolvedValue({} as SessionSaveData);
|
|
||||||
|
|
||||||
const result = await game.scene.gameData.renameSession(0, "Named Run");
|
|
||||||
|
|
||||||
expect(result).toEqual(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return false if api returns error", async () => {
|
|
||||||
vi.spyOn(game.scene.gameData, "getSession").mockResolvedValue({} as SessionSaveData);
|
|
||||||
vi.spyOn(pokerogueApi.savedata.session, "update").mockResolvedValue("Unknown Error!");
|
|
||||||
|
|
||||||
const result = await game.scene.gameData.renameSession(0, "Named Run");
|
|
||||||
|
|
||||||
expect(result).toEqual(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return true if api is succesfull", async () => {
|
|
||||||
vi.spyOn(game.scene.gameData, "getSession").mockResolvedValue({} as SessionSaveData);
|
|
||||||
vi.spyOn(pokerogueApi.savedata.session, "update").mockResolvedValue("");
|
|
||||||
|
|
||||||
const result = await game.scene.gameData.renameSession(0, "Named Run");
|
|
||||||
|
|
||||||
expect(result).toEqual(true);
|
|
||||||
expect(account.updateUserInfo).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
Reference in New Issue
Block a user