[Enhancement] Skip egg hatching and show summary (#3726)

* cherry picked commits / manual copy

* better dex tracking for summary after regular egg hatching

* ui changes

* updated egg hatch bg, added candy tracker, icon anims for new shiny or new form unlock

* added i18 line, reset overrides

* touchup

* code cleanup, documentation and slight refactor

* sprite display fix

* load interrupts, simple sfx and no summary for small egg amounts

* Garbage Collection + Eslint/Docs approved.

* time logging and optimisation

* skip redundant load

* more time logs and fix pre-load issues

* more detailed loading logs

* changed loading to be on demand from cursor nav

* fix missing variant icon fallback

* removing redundant time logs and code touchup

* code cleanup

* Comments so developer doesn't get bugged about garbage collecton

* remove logs n stuff

* lang settings touchup and final touchup plus uploading blank egg summary bg

* fix nits, js imports, extra docs, magic numbers changed

* extra docs and spacing nits

* Update Github

---------

Co-authored-by: James Diefenbach <z5421232@ad.unsw.edu.au>
Co-authored-by: Frederico Santos <frederico.f.santos@tecnico.ulisboa.pt>
Co-authored-by: frutescens <info@laptop>
Co-authored-by: Mumble <171087428+frutescens@users.noreply.github.com>
This commit is contained in:
James Diefenbach 2024-09-05 02:59:25 +10:00 committed by GitHub
parent 3704680029
commit e822c91a16
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 913 additions and 76 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 B

View File

@ -2742,6 +2742,29 @@ export default class BattleScene extends SceneBase {
(window as any).gameInfo = gameInfo;
}
/**
* This function retrieves the sprite and audio keys for active Pokemon.
* Active Pokemon include both enemy and player Pokemon of the current wave.
* Note: Questions on garbage collection go to @frutescens
* @returns a string array of active sprite and audio keys that should not be deleted
*/
getActiveKeys(): string[] {
const keys: string[] = [];
const playerParty = this.getParty();
playerParty.forEach(p => {
keys.push("pkmn__" + p.species.getSpriteId(p.gender === Gender.FEMALE, p.species.formIndex, p.shiny, p.variant));
keys.push("pkmn__" + p.species.getSpriteId(p.gender === Gender.FEMALE, p.species.formIndex, p.shiny, p.variant, true));
keys.push("cry/" + p.species.getCryKey(p.species.formIndex));
});
// enemyParty has to be operated on separately from playerParty because playerPokemon =/= enemyPokemon
const enemyParty = this.getEnemyParty();
enemyParty.forEach(p => {
keys.push(p.species.getSpriteKey(p.gender === Gender.FEMALE, p.species.formIndex, p.shiny, p.variant));
keys.push("cry/" + p.species.getCryKey(p.species.formIndex));
});
return keys;
}
/**
* Initialized the 2nd phase of the final boss (e.g. form-change for Eternatus)
* @param pokemon The (enemy) pokemon

View File

@ -0,0 +1,98 @@
import BattleScene from "#app/battle-scene";
import { PlayerPokemon } from "#app/field/pokemon";
import { DexEntry, StarterDataEntry } from "#app/system/game-data";
/**
* Stores data associated with a specific egg and the hatched pokemon
* Allows hatch info to be stored at hatch then retrieved for display during egg summary
*/
export class EggHatchData {
/** the pokemon that hatched from the file (including shiny, IVs, ability) */
public pokemon: PlayerPokemon;
/** index of the egg move from the hatched pokemon (not stored in PlayerPokemon) */
public eggMoveIndex: number;
/** boolean indicating if the egg move for the hatch is new */
public eggMoveUnlocked: boolean;
/** stored copy of the hatched pokemon's dex entry before it was updated due to hatch */
public dexEntryBeforeUpdate: DexEntry;
/** stored copy of the hatched pokemon's starter entry before it was updated due to hatch */
public starterDataEntryBeforeUpdate: StarterDataEntry;
/** reference to the battle scene to get gamedata and update dex */
private scene: BattleScene;
constructor(scene: BattleScene, pokemon: PlayerPokemon, eggMoveIndex: number) {
this.scene = scene;
this.pokemon = pokemon;
this.eggMoveIndex = eggMoveIndex;
}
/**
* Sets the boolean for if the egg move for the hatch is a new unlock
* @param unlocked True if the EM is new
*/
setEggMoveUnlocked(unlocked: boolean) {
this.eggMoveUnlocked = unlocked;
}
/**
* Stores a copy of the current DexEntry of the pokemon and StarterDataEntry of its starter
* Used before updating the dex, so comparing the pokemon to these entries will show the new attributes
*/
setDex() {
const currDexEntry = this.scene.gameData.dexData[this.pokemon.species.speciesId];
const currStarterDataEntry = this.scene.gameData.starterData[this.pokemon.species.getRootSpeciesId()];
this.dexEntryBeforeUpdate = {
seenAttr: currDexEntry.seenAttr,
caughtAttr: currDexEntry.caughtAttr,
natureAttr: currDexEntry.natureAttr,
seenCount: currDexEntry.seenCount,
caughtCount: currDexEntry.caughtCount,
hatchedCount: currDexEntry.hatchedCount,
ivs: [...currDexEntry.ivs]
};
this.starterDataEntryBeforeUpdate = {
moveset: currStarterDataEntry.moveset,
eggMoves: currStarterDataEntry.eggMoves,
candyCount: currStarterDataEntry.candyCount,
friendship: currStarterDataEntry.friendship,
abilityAttr: currStarterDataEntry.abilityAttr,
passiveAttr: currStarterDataEntry.passiveAttr,
valueReduction: currStarterDataEntry.valueReduction,
classicWinCount: currStarterDataEntry.classicWinCount
};
}
/**
* Gets the dex entry before update
* @returns Dex Entry corresponding to this pokemon before the pokemon was added / updated to dex
*/
getDex(): DexEntry {
return this.dexEntryBeforeUpdate;
}
/**
* Gets the starter dex entry before update
* @returns Starter Dex Entry corresponding to this pokemon before the pokemon was added / updated to dex
*/
getStarterEntry(): StarterDataEntry {
return this.starterDataEntryBeforeUpdate;
}
/**
* Update the pokedex data corresponding with the new hatch's pokemon data
* Also sets whether the egg move is a new unlock or not
* @param showMessage boolean to show messages for the new catches and egg moves (false by default)
* @returns
*/
updatePokemon(showMessage : boolean = false) {
return new Promise<void>(resolve => {
this.scene.gameData.setPokemonCaught(this.pokemon, true, true, showMessage).then(() => {
this.scene.gameData.updateSpeciesDexIvs(this.pokemon.species.speciesId, this.pokemon.ivs);
this.scene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex, showMessage).then((value) => {
this.setEggMoveUnlocked(value);
resolve();
});
});
});
}
}

View File

@ -78,6 +78,7 @@ export class LoadingScene extends SceneBase {
this.loadAtlas("overlay_hp_boss", "ui");
this.loadImage("overlay_exp", "ui");
this.loadImage("icon_owned", "ui");
this.loadImage("icon_egg_move", "ui");
this.loadImage("ability_bar_left", "ui");
this.loadImage("bgm_bar", "ui");
this.loadImage("party_exp_bar", "ui");
@ -272,6 +273,7 @@ export class LoadingScene extends SceneBase {
this.loadImage("gacha_knob", "egg");
this.loadImage("egg_list_bg", "ui");
this.loadImage("egg_summary_bg", "ui");
this.loadImage("end_m", "cg");
this.loadImage("end_f", "cg");

View File

@ -61,6 +61,7 @@
"skipItemQuestion": "Are you sure you want to skip taking an item?",
"itemStackFull": "The stack for {{fullItemName}} is full.\nYou will receive {{itemName}} instead.",
"eggHatching": "Oh?",
"eggSkipPrompt": "Skip to egg summary?",
"ivScannerUseQuestion": "Use IV Scanner on {{pokemonName}}?",
"wildPokemonWithAffix": "Wild {{pokemonName}}",
"foePokemonWithAffix": "Foe {{pokemonName}}",

View File

@ -1,23 +1,29 @@
import BattleScene, { AnySound } from "#app/battle-scene.js";
import { Egg, EGG_SEED } from "#app/data/egg.js";
import { EggCountChangedEvent } from "#app/events/egg.js";
import { PlayerPokemon } from "#app/field/pokemon.js";
import { getPokemonNameWithAffix } from "#app/messages.js";
import { Phase } from "#app/phase.js";
import { achvs } from "#app/system/achv.js";
import EggCounterContainer from "#app/ui/egg-counter-container.js";
import EggHatchSceneHandler from "#app/ui/egg-hatch-scene-handler.js";
import PokemonInfoContainer from "#app/ui/pokemon-info-container.js";
import { Mode } from "#app/ui/ui.js";
import BattleScene, { AnySound } from "#app/battle-scene";
import { Egg } from "#app/data/egg";
import { EggCountChangedEvent } from "#app/events/egg";
import { PlayerPokemon } from "#app/field/pokemon";
import { getPokemonNameWithAffix } from "#app/messages";
import { Phase } from "#app/phase";
import { achvs } from "#app/system/achv";
import EggCounterContainer from "#app/ui/egg-counter-container";
import EggHatchSceneHandler from "#app/ui/egg-hatch-scene-handler";
import PokemonInfoContainer from "#app/ui/pokemon-info-container";
import { Mode } from "#app/ui/ui";
import i18next from "i18next";
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
import * as Utils from "#app/utils.js";
import * as Utils from "#app/utils";
import { EggLapsePhase } from "./egg-lapse-phase";
import { EggHatchData } from "#app/data/egg-hatch-data";
/**
* Class that represents egg hatching
*/
export class EggHatchPhase extends Phase {
/** The egg that is hatching */
private egg: Egg;
/** The new EggHatchData for the egg/pokemon that hatches */
private eggHatchData: EggHatchData;
/** The number of eggs that are hatching */
private eggsToHatchCount: integer;
@ -58,10 +64,11 @@ export class EggHatchPhase extends Phase {
private skipped: boolean;
/** The sound effect being played when the egg is hatched */
private evolutionBgm: AnySound;
private eggLapsePhase: EggLapsePhase;
constructor(scene: BattleScene, egg: Egg, eggsToHatchCount: integer) {
constructor(scene: BattleScene, hatchScene: EggLapsePhase, egg: Egg, eggsToHatchCount: integer) {
super(scene);
this.eggLapsePhase = hatchScene;
this.egg = egg;
this.eggsToHatchCount = eggsToHatchCount;
}
@ -307,6 +314,7 @@ export class EggHatchPhase extends Phase {
* Function to do the logic and animation of completing a hatch and revealing the Pokemon
*/
doReveal(): void {
// set the previous dex data so info container can show new unlocks in egg summary
const isShiny = this.pokemon.isShiny();
if (this.pokemon.species.subLegendary) {
this.scene.validateAchv(achvs.HATCH_SUB_LEGENDARY);
@ -345,13 +353,13 @@ export class EggHatchPhase extends Phase {
this.scene.ui.showText(i18next.t("egg:hatchFromTheEgg", { pokemonName: getPokemonNameWithAffix(this.pokemon) }), null, () => {
this.scene.gameData.updateSpeciesDexIvs(this.pokemon.species.speciesId, this.pokemon.ivs);
this.scene.gameData.setPokemonCaught(this.pokemon, true, true).then(() => {
this.scene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex).then(() => {
this.scene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex).then((value) => {
this.eggHatchData.setEggMoveUnlocked(value);
this.scene.ui.showText("", 0);
this.end();
});
});
}, null, true, 3000);
//this.scene.time.delayedCall(Utils.fixedInt(4250), () => this.scene.playBgm());
});
});
this.scene.tweens.add({
@ -435,17 +443,11 @@ export class EggHatchPhase extends Phase {
/**
* Generates a Pokemon to be hatched by the egg
* Also stores the generated pokemon in this.eggHatchData
* @returns the hatched PlayerPokemon
*/
generatePokemon(): PlayerPokemon {
let ret: PlayerPokemon;
this.scene.executeWithSeedOffset(() => {
ret = this.egg.generatePlayerPokemon(this.scene);
this.eggMoveIndex = this.egg.eggMoveIndex;
}, this.egg.id, EGG_SEED.toString());
return ret!;
this.eggHatchData = this.eggLapsePhase.generatePokemon(this.egg);
return this.eggHatchData.pokemon;
}
}

View File

@ -1,11 +1,23 @@
import BattleScene from "#app/battle-scene.js";
import { Egg } from "#app/data/egg.js";
import { Phase } from "#app/phase.js";
import BattleScene from "#app/battle-scene";
import { Egg, EGG_SEED } from "#app/data/egg";
import { Phase } from "#app/phase";
import i18next from "i18next";
import Overrides from "#app/overrides";
import { EggHatchPhase } from "./egg-hatch-phase";
import { Mode } from "#app/ui/ui";
import { achvs } from "#app/system/achv";
import { PlayerPokemon } from "#app/field/pokemon";
import { EggSummaryPhase } from "./egg-summary-phase";
import { EggHatchData } from "#app/data/egg-hatch-data";
/**
* Phase that handles updating eggs, and hatching any ready eggs
* Also handles prompts for skipping animation, and calling the egg summary phase
*/
export class EggLapsePhase extends Phase {
private eggHatchData: EggHatchData[] = [];
private readonly minEggsToPromptSkip: number = 5;
constructor(scene: BattleScene) {
super(scene);
}
@ -16,20 +28,111 @@ export class EggLapsePhase extends Phase {
const eggsToHatch: Egg[] = this.scene.gameData.eggs.filter((egg: Egg) => {
return Overrides.EGG_IMMEDIATE_HATCH_OVERRIDE ? true : --egg.hatchWaves < 1;
});
const eggsToHatchCount: number = eggsToHatch.length;
this.eggHatchData= [];
let eggCount: integer = eggsToHatch.length;
if (eggsToHatchCount > 0) {
if (eggCount) {
if (eggsToHatchCount >= this.minEggsToPromptSkip) {
this.scene.ui.showText(i18next.t("battle:eggHatching"), 0, () => {
// show prompt for skip
this.scene.ui.showText(i18next.t("battle:eggSkipPrompt"), 0);
this.scene.ui.setModeWithoutClear(Mode.CONFIRM, () => {
this.hatchEggsSkipped(eggsToHatch);
this.showSummary();
}, () => {
this.hatchEggsRegular(eggsToHatch);
this.showSummary();
}
);
}, 100, true);
} else {
// regular hatches, no summary
this.scene.queueMessage(i18next.t("battle:eggHatching"));
for (const egg of eggsToHatch) {
this.scene.unshiftPhase(new EggHatchPhase(this.scene, egg, eggCount));
if (eggCount > 0) {
eggCount--;
}
}
this.hatchEggsRegular(eggsToHatch);
this.end();
}
} else {
this.end();
}
}
/**
* Hatches eggs normally one by one, showing animations
* @param eggsToHatch list of eggs to hatch
*/
hatchEggsRegular(eggsToHatch: Egg[]) {
let eggsToHatchCount: number = eggsToHatch.length;
for (const egg of eggsToHatch) {
this.scene.unshiftPhase(new EggHatchPhase(this.scene, this, egg, eggsToHatchCount));
eggsToHatchCount--;
}
}
/**
* Hatches eggs with no animations
* @param eggsToHatch list of eggs to hatch
*/
hatchEggsSkipped(eggsToHatch: Egg[]) {
for (const egg of eggsToHatch) {
this.hatchEggSilently(egg);
}
}
showSummary() {
this.scene.unshiftPhase(new EggSummaryPhase(this.scene, this.eggHatchData));
this.end();
}
/**
* Hatches an egg and stores it in the local EggHatchData array without animations
* Also validates the achievements for the hatched pokemon and removes the egg
* @param egg egg to hatch
*/
hatchEggSilently(egg: Egg) {
const eggIndex = this.scene.gameData.eggs.findIndex(e => e.id === egg.id);
if (eggIndex === -1) {
return this.end();
}
this.scene.gameData.eggs.splice(eggIndex, 1);
const data = this.generatePokemon(egg);
const pokemon = data.pokemon;
if (pokemon.fusionSpecies) {
pokemon.clearFusionSpecies();
}
if (pokemon.species.subLegendary) {
this.scene.validateAchv(achvs.HATCH_SUB_LEGENDARY);
}
if (pokemon.species.legendary) {
this.scene.validateAchv(achvs.HATCH_LEGENDARY);
}
if (pokemon.species.mythical) {
this.scene.validateAchv(achvs.HATCH_MYTHICAL);
}
if (pokemon.isShiny()) {
this.scene.validateAchv(achvs.HATCH_SHINY);
}
}
/**
* Generates a Pokemon and creates a new EggHatchData instance for the given egg
* @param egg the egg to hatch
* @returns the hatched PlayerPokemon
*/
generatePokemon(egg: Egg): EggHatchData {
let ret: PlayerPokemon;
let newHatchData: EggHatchData;
this.scene.executeWithSeedOffset(() => {
ret = egg.generatePlayerPokemon(this.scene);
newHatchData = new EggHatchData(this.scene, ret, egg.eggMoveIndex);
newHatchData.setDex();
this.eggHatchData.push(newHatchData);
}, egg.id, EGG_SEED.toString());
return newHatchData!;
}
}

View File

@ -0,0 +1,50 @@
import BattleScene from "#app/battle-scene";
import { Phase } from "#app/phase";
import { Mode } from "#app/ui/ui";
import EggHatchSceneHandler from "#app/ui/egg-hatch-scene-handler";
import { EggHatchData } from "#app/data/egg-hatch-data";
/**
* Class that represents the egg summary phase
* It does some of the function for updating egg data
* Phase is handled mostly by the egg-hatch-scene-handler UI
*/
export class EggSummaryPhase extends Phase {
private eggHatchData: EggHatchData[];
private eggHatchHandler: EggHatchSceneHandler;
constructor(scene: BattleScene, eggHatchData: EggHatchData[]) {
super(scene);
this.eggHatchData = eggHatchData;
}
start() {
super.start();
// updates next pokemon once the current update has been completed
const updateNextPokemon = (i: number) => {
if (i >= this.eggHatchData.length) {
this.scene.ui.setModeForceTransition(Mode.EGG_HATCH_SUMMARY, this.eggHatchData).then(() => {
this.scene.fadeOutBgm(undefined, false);
this.eggHatchHandler = this.scene.ui.getHandler() as EggHatchSceneHandler;
});
} else {
this.eggHatchData[i].setDex();
this.eggHatchData[i].updatePokemon().then(() => {
if (i < this.eggHatchData.length) {
updateNextPokemon(i + 1);
}
});
}
};
updateNextPokemon(0);
}
end() {
this.eggHatchHandler.clear();
this.scene.ui.setModeForceTransition(Mode.MESSAGE).then(() => {});
super.end();
}
}

View File

@ -1553,11 +1553,11 @@ export class GameData {
}
}
setPokemonCaught(pokemon: Pokemon, incrementCount: boolean = true, fromEgg: boolean = false): Promise<void> {
return this.setPokemonSpeciesCaught(pokemon, pokemon.species, incrementCount, fromEgg);
setPokemonCaught(pokemon: Pokemon, incrementCount: boolean = true, fromEgg: boolean = false, showMessage: boolean = true): Promise<void> {
return this.setPokemonSpeciesCaught(pokemon, pokemon.species, incrementCount, fromEgg, showMessage);
}
setPokemonSpeciesCaught(pokemon: Pokemon, species: PokemonSpecies, incrementCount: boolean = true, fromEgg: boolean = false): Promise<void> {
setPokemonSpeciesCaught(pokemon: Pokemon, species: PokemonSpecies, incrementCount: boolean = true, fromEgg: boolean = false, showMessage: boolean = true): Promise<void> {
return new Promise<void>(resolve => {
const dexEntry = this.dexData[species.speciesId];
const caughtAttr = dexEntry.caughtAttr;
@ -1616,13 +1616,17 @@ export class GameData {
const checkPrevolution = () => {
if (hasPrevolution) {
const prevolutionSpecies = pokemonPrevolutions[species.speciesId];
return this.setPokemonSpeciesCaught(pokemon, getPokemonSpecies(prevolutionSpecies), incrementCount, fromEgg).then(() => resolve());
this.setPokemonSpeciesCaught(pokemon, getPokemonSpecies(prevolutionSpecies), incrementCount, fromEgg, showMessage).then(() => resolve());
} else {
resolve();
}
};
if (newCatch && speciesStarters.hasOwnProperty(species.speciesId)) {
if (!showMessage) {
resolve();
return;
}
this.scene.playSound("level_up_fanfare");
this.scene.ui.showText(i18next.t("battle:addedAsAStarter", { pokemonName: species.name }), null, () => checkPrevolution(), null, true);
} else {
@ -1668,7 +1672,7 @@ export class GameData {
this.starterData[species.speciesId].candyCount += count;
}
setEggMoveUnlocked(species: PokemonSpecies, eggMoveIndex: integer): Promise<boolean> {
setEggMoveUnlocked(species: PokemonSpecies, eggMoveIndex: integer, showMessage: boolean = true): Promise<boolean> {
return new Promise<boolean>(resolve => {
const speciesId = species.speciesId;
if (!speciesEggMoves.hasOwnProperty(speciesId) || !speciesEggMoves[speciesId][eggMoveIndex]) {
@ -1688,11 +1692,15 @@ export class GameData {
}
this.starterData[speciesId].eggMoves |= value;
if (!showMessage) {
resolve(true);
return;
}
this.scene.playSound("level_up_fanfare");
const moveName = allMoves[speciesEggMoves[speciesId][eggMoveIndex]].name;
this.scene.ui.showText(eggMoveIndex === 3 ? i18next.t("egg:rareEggMoveUnlock", { moveName: moveName }) : i18next.t("egg:eggMoveUnlock", { moveName: moveName }), null, () => resolve(true), null, true);
this.scene.ui.showText(eggMoveIndex === 3 ? i18next.t("egg:rareEggMoveUnlock", { moveName: moveName }) : i18next.t("egg:eggMoveUnlock", { moveName: moveName }), null, (() => {
resolve(true);
}), null, true);
});
}

View File

@ -0,0 +1,320 @@
import BattleScene from "../battle-scene";
import { Mode } from "./ui";
import PokemonIconAnimHandler, { PokemonIconAnimMode } from "./pokemon-icon-anim-handler";
import MessageUiHandler from "./message-ui-handler";
import { getEggTierForSpecies } from "../data/egg";
import {Button} from "#enums/buttons";
import { Gender } from "#app/data/gender";
import { getVariantTint } from "#app/data/variant";
import { EggTier } from "#app/enums/egg-type";
import PokemonHatchInfoContainer from "./pokemon-hatch-info-container";
import { EggSummaryPhase } from "#app/phases/egg-summary-phase";
import { DexAttr } from "#app/system/game-data";
import { EggHatchData } from "#app/data/egg-hatch-data";
const iconContainerX = 115;
const iconContainerY = 9;
const numCols = 11;
const iconSize = 18;
/**
* UI Handler for the egg summary.
* Handles navigation and display of each pokemon as a list
* Also handles display of the pokemon-hatch-info-container
*/
export default class EggSummaryUiHandler extends MessageUiHandler {
/** holds all elements in the scene */
private eggHatchContainer: Phaser.GameObjects.Container;
/** holds the icon containers and info container */
private summaryContainer: Phaser.GameObjects.Container;
/** container for the mini pokemon sprites */
private pokemonIconSpritesContainer: Phaser.GameObjects.Container;
/** container for the icons displayed alongside the mini icons (e.g. shiny, HA capsule) */
private pokemonIconsContainer: Phaser.GameObjects.Container;
/** hatch info container that displays the current pokemon / hatch (main element on left hand side) */
private infoContainer: PokemonHatchInfoContainer;
/** handles jumping animations for the pokemon sprite icons */
private iconAnimHandler: PokemonIconAnimHandler;
private eggHatchBg: Phaser.GameObjects.Image;
private cursorObj: Phaser.GameObjects.Image;
private eggHatchData: EggHatchData[];
/**
* Allows subscribers to listen for events
*
* Current Events:
* - {@linkcode EggEventType.EGG_COUNT_CHANGED} {@linkcode EggCountChangedEvent}
*/
public readonly eventTarget: EventTarget = new EventTarget();
constructor(scene: BattleScene) {
super(scene, Mode.EGG_HATCH_SUMMARY);
}
setup() {
const ui = this.getUi();
this.summaryContainer = this.scene.add.container(0, -this.scene.game.canvas.height / 6);
this.summaryContainer.setVisible(false);
ui.add(this.summaryContainer);
this.eggHatchContainer = this.scene.add.container(0, -this.scene.game.canvas.height / 6);
this.eggHatchContainer.setVisible(false);
ui.add(this.eggHatchContainer);
this.iconAnimHandler = new PokemonIconAnimHandler();
this.iconAnimHandler.setup(this.scene);
this.eggHatchBg = this.scene.add.image(0, 0, "egg_summary_bg");
this.eggHatchBg.setOrigin(0, 0);
this.eggHatchContainer.add(this.eggHatchBg);
this.pokemonIconsContainer = this.scene.add.container(iconContainerX, iconContainerY);
this.pokemonIconSpritesContainer = this.scene.add.container(iconContainerX, iconContainerY);
this.summaryContainer.add(this.pokemonIconsContainer);
this.summaryContainer.add(this.pokemonIconSpritesContainer);
this.cursorObj = this.scene.add.image(0, 0, "select_cursor");
this.cursorObj.setOrigin(0, 0);
this.summaryContainer.add(this.cursorObj);
this.infoContainer = new PokemonHatchInfoContainer(this.scene, this.summaryContainer);
this.infoContainer.setup();
this.infoContainer.changeToEggSummaryLayout();
this.infoContainer.setVisible(true);
this.summaryContainer.add(this.infoContainer);
this.cursor = -1;
}
clear() {
super.clear();
this.cursor = -1;
this.summaryContainer.setVisible(false);
this.pokemonIconSpritesContainer.removeAll(true);
this.pokemonIconsContainer.removeAll(true);
this.eggHatchBg.setVisible(false);
this.getUi().hideTooltip();
// Note: Questions on garbage collection go to @frutescens
const activeKeys = this.scene.getActiveKeys();
// Removing unnecessary sprites from animation manager
const animKeys = Object.keys(this.scene.anims["anims"]["entries"]);
animKeys.forEach(key => {
if (key.startsWith("pkmn__") && !activeKeys.includes(key)) {
this.scene.anims.remove(key);
}
});
// Removing unnecessary cries from audio cache
const audioKeys = Object.keys(this.scene.cache.audio.entries.entries);
audioKeys.forEach(key => {
if (key.startsWith("cry/") && !activeKeys.includes(key)) {
delete this.scene.cache.audio.entries.entries[key];
}
});
// Clears eggHatchData in EggSummaryUiHandler
this.eggHatchData.length = 0;
// Removes Pokemon icons in EggSummaryUiHandler
this.iconAnimHandler.removeAll();
console.log("Egg Summary Handler cleared");
}
/**
* @param args EggHatchData[][]
* args[0]: list of EggHatchData for each egg/pokemon hatched
*/
show(args: EggHatchData[][]): boolean {
super.show(args);
if (args.length >= 1) {
// sort the egg hatch data by egg tier then by species number (then by order hatched)
this.eggHatchData = args[0].sort(function sortHatchData(a: EggHatchData, b: EggHatchData) {
const speciesA = a.pokemon.species;
const speciesB = b.pokemon.species;
if (getEggTierForSpecies(speciesA) < getEggTierForSpecies(speciesB)) {
return -1;
} else if (getEggTierForSpecies(speciesA) > getEggTierForSpecies(speciesB)) {
return 1;
} else {
if (speciesA.speciesId < speciesB.speciesId) {
return -1;
} else if (speciesA.speciesId > speciesB.speciesId) {
return 1;
} else {
return 0;
}
}
}
);
}
this.getUi().bringToTop(this.summaryContainer);
this.summaryContainer.setVisible(true);
this.eggHatchContainer.setVisible(true);
this.pokemonIconsContainer.setVisible(true);
this.eggHatchBg.setVisible(true);
this.infoContainer.hideDisplayPokemon();
this.eggHatchData.forEach( (value: EggHatchData, i: number) => {
const x = (i % numCols) * iconSize;
const y = Math.floor(i / numCols) * iconSize;
const displayPokemon = value.pokemon;
const offset = 2;
const rightSideX = 12;
const bg = this.scene.add.image(x+2, y+5, "passive_bg");
bg.setOrigin(0, 0);
bg.setScale(0.75);
bg.setVisible(true);
this.pokemonIconsContainer.add(bg);
// set tint for passive bg
switch (getEggTierForSpecies(displayPokemon.species)) {
case EggTier.COMMON:
bg.setVisible(false);
break;
case EggTier.GREAT:
bg.setTint(0xabafff);
break;
case EggTier.ULTRA:
bg.setTint(0xffffaa);
break;
case EggTier.MASTER:
bg.setTint(0xdfffaf);
break;
}
const species = displayPokemon.species;
const female = displayPokemon.gender === Gender.FEMALE;
const formIndex = displayPokemon.formIndex;
const variant = displayPokemon.variant;
const isShiny = displayPokemon.shiny;
// set pokemon icon (and replace with base sprite if there is a mismatch)
const icon = this.scene.add.sprite(x - offset, y + offset, species.getIconAtlasKey(formIndex, isShiny, variant));
icon.setScale(0.5);
icon.setOrigin(0, 0);
icon.setFrame(species.getIconId(female, formIndex, isShiny, variant));
if (icon.frame.name !== species.getIconId(female, formIndex, isShiny, variant)) {
console.log(`${species.name}'s variant icon does not exist. Replacing with default.`);
icon.setTexture(species.getIconAtlasKey(formIndex, false, variant));
icon.setFrame(species.getIconId(female, formIndex, false, variant));
}
this.pokemonIconSpritesContainer.add(icon);
this.iconAnimHandler.addOrUpdate(icon, PokemonIconAnimMode.NONE);
const shiny = this.scene.add.image(x + rightSideX, y + offset * 2, "shiny_star_small");
shiny.setScale(0.5);
shiny.setVisible(displayPokemon.shiny);
shiny.setTint(getVariantTint(displayPokemon.variant));
this.pokemonIconsContainer.add(shiny);
const ha = this.scene.add.image(x + rightSideX, y + 7, "ha_capsule");
ha.setScale(0.5);
ha.setVisible((displayPokemon.hasAbility(displayPokemon.species.abilityHidden)));
this.pokemonIconsContainer.add(ha);
const pb = this.scene.add.image(x + rightSideX, y + offset * 7, "icon_owned");
pb.setOrigin(0, 0);
pb.setScale(0.5);
// add animation for new unlocks (new catch or new shiny or new form)
const dexEntry = value.dexEntryBeforeUpdate;
const caughtAttr = dexEntry.caughtAttr;
const newShiny = BigInt(1 << (displayPokemon.shiny ? 1 : 0));
const newVariant = BigInt(1 << (displayPokemon.variant + 4));
const newShinyOrVariant = ((newShiny & caughtAttr) === BigInt(0)) || ((newVariant & caughtAttr) === BigInt(0));
const newForm = (BigInt(1 << displayPokemon.formIndex) * DexAttr.DEFAULT_FORM & caughtAttr) === BigInt(0);
pb.setVisible(!caughtAttr || newForm);
if (!caughtAttr || newShinyOrVariant || newForm) {
this.iconAnimHandler.addOrUpdate(icon, PokemonIconAnimMode.PASSIVE);
}
this.pokemonIconsContainer.add(pb);
const em = this.scene.add.image(x, y + offset, "icon_egg_move");
em.setOrigin(0, 0);
em.setScale(0.5);
em.setVisible(value.eggMoveUnlocked);
this.pokemonIconsContainer.add(em);
});
this.setCursor(0);
this.scene.playSoundWithoutBgm("evolution_fanfare");
return true;
}
processInput(button: Button): boolean {
const ui = this.getUi();
let success = false;
const error = false;
if (button === Button.CANCEL) {
const phase = this.scene.getCurrentPhase();
if (phase instanceof EggSummaryPhase) {
phase.end();
}
ui.revertMode();
success = true;
} else {
const count = this.eggHatchData.length;
const rows = Math.ceil(count / numCols);
const row = Math.floor(this.cursor / numCols);
switch (button) {
case Button.UP:
if (row) {
success = this.setCursor(this.cursor - numCols);
}
break;
case Button.DOWN:
if (row < rows - 2 || (row < rows - 1 && this.cursor % numCols <= (count - 1) % numCols)) {
success = this.setCursor(this.cursor + numCols);
}
break;
case Button.LEFT:
if (this.cursor % numCols) {
success = this.setCursor(this.cursor - 1);
}
break;
case Button.RIGHT:
if (this.cursor % numCols < (row < rows - 1 ? 10 : (count - 1) % numCols)) {
success = this.setCursor(this.cursor + 1);
}
break;
}
}
if (success) {
ui.playSelect();
} else if (error) {
ui.playError();
}
return success || error;
}
setCursor(cursor: number): boolean {
let changed = false;
const lastCursor = this.cursor;
changed = super.setCursor(cursor);
if (changed) {
this.cursorObj.setPosition(iconContainerX - 1 + iconSize * (cursor % numCols), iconContainerY + 1 + iconSize * Math.floor(cursor / numCols));
if (lastCursor > -1) {
this.iconAnimHandler.addOrUpdate(this.pokemonIconSpritesContainer.getAt(lastCursor) as Phaser.GameObjects.Sprite, PokemonIconAnimMode.NONE);
}
this.iconAnimHandler.addOrUpdate(this.pokemonIconSpritesContainer.getAt(cursor) as Phaser.GameObjects.Sprite, PokemonIconAnimMode.ACTIVE);
this.infoContainer.showHatchInfo(this.eggHatchData[cursor]);
}
return changed;
}
}

View File

@ -0,0 +1,189 @@
import PokemonInfoContainer from "./pokemon-info-container";
import BattleScene from "../battle-scene";
import { Gender } from "../data/gender";
import { Type } from "../data/type";
import * as Utils from "../utils";
import { TextStyle, addTextObject } from "./text";
import { speciesEggMoves } from "#app/data/egg-moves";
import { allMoves } from "#app/data/move";
import { Species } from "#app/enums/species";
import { getEggTierForSpecies } from "#app/data/egg";
import { starterColors } from "../battle-scene";
import { argbFromRgba } from "@material/material-color-utilities";
import { EggHatchData } from "#app/data/egg-hatch-data";
import { PlayerPokemon } from "#app/field/pokemon";
import { getPokemonSpeciesForm } from "#app/data/pokemon-species";
/**
* Class for the hatch info summary of each pokemon
* Holds an info container as well as an additional egg sprite, name, egg moves and main sprite
*/
export default class PokemonHatchInfoContainer extends PokemonInfoContainer {
private currentPokemonSprite: Phaser.GameObjects.Sprite;
private pokemonNumberText: Phaser.GameObjects.Text;
private pokemonNameText: Phaser.GameObjects.Text;
private pokemonEggMovesContainer: Phaser.GameObjects.Container;
private pokemonEggMoveContainers: Phaser.GameObjects.Container[];
private pokemonEggMoveBgs: Phaser.GameObjects.NineSlice[];
private pokemonEggMoveLabels: Phaser.GameObjects.Text[];
private pokemonHatchedIcon : Phaser.GameObjects.Sprite;
private pokemonListContainer: Phaser.GameObjects.Container;
private pokemonCandyIcon: Phaser.GameObjects.Sprite;
private pokemonCandyOverlayIcon: Phaser.GameObjects.Sprite;
private pokemonCandyCountText: Phaser.GameObjects.Text;
constructor(scene: BattleScene, listContainer : Phaser.GameObjects.Container, x: number = 115, y: number = 9,) {
super(scene, x, y);
this.pokemonListContainer = listContainer;
}
setup(): void {
super.setup();
super.changeToEggSummaryLayout();
this.currentPokemonSprite = this.scene.add.sprite(54, 80, "pkmn__sub");
this.currentPokemonSprite.setScale(0.8);
this.currentPokemonSprite.setPipeline(this.scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], ignoreTimeTint: true });
this.pokemonListContainer.add(this.currentPokemonSprite);
// setup name and number
this.pokemonNumberText = addTextObject(this.scene, 80, 107.5, "0000", TextStyle.SUMMARY, {fontSize: 74});
this.pokemonNumberText.setOrigin(0, 0);
this.pokemonListContainer.add(this.pokemonNumberText);
this.pokemonNameText = addTextObject(this.scene, 7, 107.5, "", TextStyle.SUMMARY, {fontSize: 74});
this.pokemonNameText.setOrigin(0, 0);
this.pokemonListContainer.add(this.pokemonNameText);
// setup egg icon and candy count
this.pokemonHatchedIcon = this.scene.add.sprite(-5, 90, "egg_icons");
this.pokemonHatchedIcon.setOrigin(0, 0.2);
this.pokemonHatchedIcon.setScale(0.8);
this.pokemonListContainer.add(this.pokemonHatchedIcon);
this.pokemonCandyIcon = this.scene.add.sprite(4.5, 40, "candy");
this.pokemonCandyIcon.setScale(0.5);
this.pokemonCandyIcon.setOrigin(0, 0);
this.pokemonListContainer.add(this.pokemonCandyIcon);
this.pokemonCandyOverlayIcon = this.scene.add.sprite(4.5, 40, "candy_overlay");
this.pokemonCandyOverlayIcon.setScale(0.5);
this.pokemonCandyOverlayIcon.setOrigin(0, 0);
this.pokemonListContainer.add(this.pokemonCandyOverlayIcon);
this.pokemonCandyCountText = addTextObject(this.scene, 14, 40, "x0", TextStyle.SUMMARY, { fontSize: "56px" });
this.pokemonCandyCountText.setOrigin(0, 0);
this.pokemonListContainer.add(this.pokemonCandyCountText);
// setup egg moves
this.pokemonEggMoveContainers = [];
this.pokemonEggMoveBgs = [];
this.pokemonEggMoveLabels = [];
this.pokemonEggMovesContainer = this.scene.add.container(0, 200);
this.pokemonEggMovesContainer.setVisible(false);
this.pokemonEggMovesContainer.setScale(0.5);
for (let m = 0; m < 4; m++) {
const eggMoveContainer = this.scene.add.container(0, 0 + 6 * m);
const eggMoveBg = this.scene.add.nineslice(70, 0, "type_bgs", "unknown", 92, 14, 2, 2, 2, 2);
eggMoveBg.setOrigin(1, 0);
const eggMoveLabel = addTextObject(this.scene, 70 -eggMoveBg.width / 2, 0, "???", TextStyle.PARTY);
eggMoveLabel.setOrigin(0.5, 0);
this.pokemonEggMoveBgs.push(eggMoveBg);
this.pokemonEggMoveLabels.push(eggMoveLabel);
eggMoveContainer.add(eggMoveBg);
eggMoveContainer.add(eggMoveLabel);
eggMoveContainer.setScale(0.44);
this.pokemonEggMoveContainers.push(eggMoveContainer);
this.pokemonEggMovesContainer.add(eggMoveContainer);
}
super.add(this.pokemonEggMoveContainers);
}
/**
* Disable the sprite (and replace with substitute)
*/
hideDisplayPokemon() {
this.currentPokemonSprite.setVisible(false);
}
/**
* Display a given pokemon sprite with animations
* assumes the specific pokemon sprite has already been loaded
*/
displayPokemon(pokemon: PlayerPokemon) {
const species = pokemon.species;
const female = pokemon.gender === Gender.FEMALE;
const formIndex = pokemon.formIndex;
const shiny = pokemon.shiny;
const variant = pokemon.variant;
this.currentPokemonSprite.setVisible(false);
species.loadAssets(this.scene, female, formIndex, shiny, variant, true).then(() => {
getPokemonSpeciesForm(species.speciesId, pokemon.formIndex).cry(this.scene);
this.currentPokemonSprite.play(species.getSpriteKey(female, formIndex, shiny, variant));
this.currentPokemonSprite.setPipelineData("shiny", shiny);
this.currentPokemonSprite.setPipelineData("variant", variant);
this.currentPokemonSprite.setPipelineData("spriteKey", species.getSpriteKey(female, formIndex, shiny, variant));
this.currentPokemonSprite.setVisible(true);
});
}
/**
* Updates the info container with the appropriate dex data and starter entry from the hatchInfo
* Also updates the displayed name, number, egg moves and main animated sprite for the pokemon
* @param hatchInfo The EggHatchData of the pokemon / new hatch to show
*/
showHatchInfo(hatchInfo: EggHatchData) {
this.pokemonEggMovesContainer.setVisible(true);
const pokemon = hatchInfo.pokemon;
const species = pokemon.species;
this.displayPokemon(pokemon);
super.show(pokemon, false, 1, hatchInfo.getDex(), hatchInfo.getStarterEntry(), true);
const colorScheme = starterColors[species.speciesId];
this.pokemonCandyIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[0])));
this.pokemonCandyIcon.setVisible(true);
this.pokemonCandyOverlayIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[1])));
this.pokemonCandyOverlayIcon.setVisible(true);
this.pokemonCandyCountText.setText(`x${this.scene.gameData.starterData[species.speciesId].candyCount}`);
this.pokemonCandyCountText.setVisible(true);
this.pokemonNumberText.setText(Utils.padInt(species.speciesId, 4));
this.pokemonNameText.setText(species.name);
const hasEggMoves = species && speciesEggMoves.hasOwnProperty(species.speciesId);
for (let em = 0; em < 4; em++) {
const eggMove = hasEggMoves ? allMoves[speciesEggMoves[species.speciesId][em]] : null;
const eggMoveUnlocked = eggMove && this.scene.gameData.starterData[species.speciesId].eggMoves & Math.pow(2, em);
this.pokemonEggMoveBgs[em].setFrame(Type[eggMove ? eggMove.type : Type.UNKNOWN].toString().toLowerCase());
this.pokemonEggMoveLabels[em].setText(eggMove && eggMoveUnlocked ? eggMove.name : "???");
if (!(eggMove && hatchInfo.starterDataEntryBeforeUpdate.eggMoves & Math.pow(2, em)) && eggMoveUnlocked) {
this.pokemonEggMoveLabels[em].setText("(+) " + eggMove.name);
}
}
// will always have at least one egg move
this.pokemonEggMovesContainer.setVisible(true);
if (species.speciesId === Species.MANAPHY || species.speciesId === Species.PHIONE) {
this.pokemonHatchedIcon.setFrame("manaphy");
} else {
this.pokemonHatchedIcon.setFrame(getEggTierForSpecies(species));
}
}
}

View File

@ -6,7 +6,7 @@ import { getNatureName } from "../data/nature";
import { Type } from "../data/type";
import Pokemon from "../field/pokemon";
import i18next from "i18next";
import { DexAttr } from "../system/game-data";
import { DexAttr, DexEntry, StarterDataEntry } from "../system/game-data";
import * as Utils from "../utils";
import ConfirmUiHandler from "./confirm-ui-handler";
import { StatsContainer } from "./stats-container";
@ -24,7 +24,7 @@ const languageSettings: { [key: string]: LanguageSetting } = {
infoContainerTextSize: "64px"
},
"de": {
infoContainerTextSize: "64px"
infoContainerTextSize: "64px",
},
"es": {
infoContainerTextSize: "64px"
@ -63,6 +63,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
private pokemonMovesContainers: Phaser.GameObjects.Container[];
private pokemonMoveBgs: Phaser.GameObjects.NineSlice[];
private pokemonMoveLabels: Phaser.GameObjects.Text[];
private infoBg;
private numCharsBeforeCutoff = 16;
@ -83,9 +84,9 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
const currentLanguage = i18next.resolvedLanguage!; // TODO: is this bang correct?
const langSettingKey = Object.keys(languageSettings).find(lang => currentLanguage?.includes(lang))!; // TODO: is this bang correct?
const textSettings = languageSettings[langSettingKey];
const infoBg = addWindow(this.scene, 0, 0, this.infoWindowWidth, 132);
infoBg.setOrigin(0.5, 0.5);
infoBg.setName("window-info-bg");
this.infoBg = addWindow(this.scene, 0, 0, this.infoWindowWidth, 132);
this.infoBg.setOrigin(0.5, 0.5);
this.infoBg.setName("window-info-bg");
this.pokemonMovesContainer = this.scene.add.container(6, 14);
this.pokemonMovesContainer.setName("pkmn-moves");
@ -133,7 +134,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
this.statsContainer = new StatsContainer(this.scene, -48, -64, true);
this.add(infoBg);
this.add(this.infoBg);
this.add(this.statsContainer);
// The position should be set per language
@ -207,9 +208,16 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
this.setVisible(false);
}
show(pokemon: Pokemon, showMoves: boolean = false, speedMultiplier: number = 1): Promise<void> {
show(pokemon: Pokemon, showMoves: boolean = false, speedMultiplier: number = 1, dexEntry?: DexEntry, starterEntry?: StarterDataEntry, eggInfo = false): Promise<void> {
return new Promise<void>(resolve => {
const caughtAttr = BigInt(pokemon.scene.gameData.dexData[pokemon.species.speciesId].caughtAttr);
if (!dexEntry) {
dexEntry = pokemon.scene.gameData.dexData[pokemon.species.speciesId];
}
if (!starterEntry) {
starterEntry = pokemon.scene.gameData.starterData[pokemon.species.getRootSpeciesId()];
}
const caughtAttr = BigInt(dexEntry.caughtAttr);
if (pokemon.gender > Gender.GENDERLESS) {
this.pokemonGenderText.setText(getGenderSymbol(pokemon.gender));
this.pokemonGenderText.setColor(getGenderColor(pokemon.gender));
@ -268,7 +276,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
const opponentPokemonAbilityIndex = (opponentPokemonOneNormalAbility && pokemon.abilityIndex === 1) ? 2 : pokemon.abilityIndex;
const opponentPokemonAbilityAttr = 1 << opponentPokemonAbilityIndex;
const rootFormHasHiddenAbility = pokemon.scene.gameData.starterData[pokemon.species.getRootSpeciesId()].abilityAttr & opponentPokemonAbilityAttr;
const rootFormHasHiddenAbility = starterEntry.abilityAttr & opponentPokemonAbilityAttr;
if (!rootFormHasHiddenAbility) {
this.pokemonAbilityLabelText.setColor(getTextColor(TextStyle.SUMMARY_BLUE, false, this.scene.uiTheme));
@ -280,7 +288,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
this.pokemonNatureText.setText(getNatureName(pokemon.getNature(), true, false, false, this.scene.uiTheme));
const dexNatures = pokemon.scene.gameData.dexData[pokemon.species.speciesId].natureAttr;
const dexNatures = dexEntry.natureAttr;
const newNature = 1 << (pokemon.nature + 1);
if (!(dexNatures & newNature)) {
@ -324,12 +332,11 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
}
const starterSpeciesId = pokemon.species.getRootSpeciesId();
const originalIvs: integer[] | null = this.scene.gameData.dexData[starterSpeciesId].caughtAttr
? this.scene.gameData.dexData[starterSpeciesId].ivs
: null;
const originalIvs: integer[] | null = eggInfo ? (dexEntry.caughtAttr ? dexEntry.ivs : null) : (this.scene.gameData.dexData[starterSpeciesId].caughtAttr
? this.scene.gameData.dexData[starterSpeciesId].ivs : null);
this.statsContainer.updateIvs(pokemon.ivs, originalIvs!); // TODO: is this bang correct?
if (!eggInfo) {
this.scene.tweens.add({
targets: this,
duration: Utils.fixedInt(Math.floor(750 / speedMultiplier)),
@ -350,6 +357,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
onComplete: () => resolve()
});
}
}
for (let m = 0; m < 4; m++) {
const move = m < pokemon.moveset.length && pokemon.moveset[m] ? pokemon.moveset[m]!.getMove() : null;
@ -364,6 +372,36 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
});
}
changeToEggSummaryLayout() {
// The position should be set per language (and shifted for new layout)
const currentLanguage = i18next.resolvedLanguage!; // TODO: is this bang correct?
const langSettingKey = Object.keys(languageSettings).find(lang => currentLanguage?.includes(lang))!; // TODO: is this bang correct?
const textSettings = languageSettings[langSettingKey];
const eggLabelTextOffset = 43;
const infoContainerLabelXPos = (textSettings?.infoContainerLabelXPos || -18) + eggLabelTextOffset;
const infoContainerTextXPos = (textSettings?.infoContainerTextXPos || -14) + eggLabelTextOffset;
this.x = this.initialX - this.infoWindowWidth;
this.pokemonGenderText.setPosition(89, -2);
this.pokemonGenderNewText.setPosition(79, -2);
this.pokemonShinyIcon.setPosition(82, 87);
this.pokemonShinyNewIcon.setPosition(72, 87);
this.pokemonFormLabelText.setPosition(infoContainerLabelXPos, 152);
this.pokemonFormText.setPosition(infoContainerTextXPos, 152);
this.pokemonAbilityLabelText.setPosition(infoContainerLabelXPos, 110);
this.pokemonAbilityText.setPosition(infoContainerTextXPos, 110);
this.pokemonNatureLabelText.setPosition(infoContainerLabelXPos, 125);
this.pokemonNatureText.setPosition(infoContainerTextXPos, 125);
this.statsContainer.setScale(0.7);
this.statsContainer.setPosition(30, -3);
this.infoBg.setVisible(false);
this.pokemonMovesContainer.setVisible(false);
}
makeRoomForConfirmUi(speedMultiplier: number = 1, fromCatch: boolean = false): Promise<void> {
const xPosition = fromCatch ? this.initialX - this.infoWindowWidth - 65 : this.initialX - this.infoWindowWidth - ConfirmUiHandler.windowWidth;
return new Promise<void>(resolve => {

View File

@ -49,6 +49,7 @@ import RenameFormUiHandler from "./rename-form-ui-handler";
import AdminUiHandler from "./admin-ui-handler";
import RunHistoryUiHandler from "./run-history-ui-handler";
import RunInfoUiHandler from "./run-info-ui-handler";
import EggSummaryUiHandler from "./egg-summary-ui-handler";
import TestDialogueUiHandler from "#app/ui/test-dialogue-ui-handler";
import AutoCompleteUiHandler from "./autocomplete-ui-handler";
@ -66,6 +67,7 @@ export enum Mode {
STARTER_SELECT,
EVOLUTION_SCENE,
EGG_HATCH_SCENE,
EGG_HATCH_SUMMARY,
CONFIRM,
OPTION_SELECT,
MENU,
@ -171,6 +173,7 @@ export default class UI extends Phaser.GameObjects.Container {
new StarterSelectUiHandler(scene),
new EvolutionSceneHandler(scene),
new EggHatchSceneHandler(scene),
new EggSummaryUiHandler(scene),
new ConfirmUiHandler(scene),
new OptionSelectUiHandler(scene),
new MenuUiHandler(scene),