[Refactor][UI/UX] Cleanup battle-info ui code (#5696)

* Create battle-info directory and move battle-info.ts to it

* Move player and enemy battle info to their own files

* Move subclass specific parts of constructor to subclass constructor

* Fixup mock gameobject methods to match phaser gameobject returns

* Make statOrder specific to subclass

* Create getShinyDescriptor function in utils

* Move icon construction to its own function

* Cleanup enemybattleinfo constructor to use chaining

* Make flyout exclusive to EnemyBattleInfo

* Move EnemyPokemon specific init Logic to its class

* Break up initInfo into different methods

* Remove hp bar segment dividers from base battle info

* Move setMini to pokemoninfo

* Breakup updateInfo into smaller parts

* Remove hp info handling from base updateInfo

* Use phaser object chaining methods

* Add some docs

* Add missing chain usage

* Use getShinyDescriptor in pokemon-info-container

* Minor cleanup of updatePokemonExp

* Fixup setSizeToFrame mock

* Ensure pokemon hp numbers are not visible during stat display

* Update src/utils/common.ts

Co-authored-by: Wlowscha <54003515+Wlowscha@users.noreply.github.com>

* Make summary-ui-handler use new shinyDescriptor method

* Remove `undefined` parameter pass

Co-authored-by: Amani H. <109637146+xsn34kzx@users.noreply.github.com>

* Address kev's review comments

Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>

* Ensure hp number display fades in/out

* Ensure ribbon and caught indicator fade with stat display

* Update src/ui/battle-info/battle-info.ts

Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>

* Move construction of stats and type icons to their own methods

* Make setPositionRelative return this

* Improve doc comment on paddingX param

* Fix mock sprite's setPositionRelative

---------

Co-authored-by: Wlowscha <54003515+Wlowscha@users.noreply.github.com>
Co-authored-by: Amani H. <109637146+xsn34kzx@users.noreply.github.com>
Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
This commit is contained in:
Sirz Benjie 2025-05-28 12:29:03 -05:00 committed by GitHub
parent eb0937a09b
commit d5789105f3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 1602 additions and 1219 deletions

View File

@ -5,7 +5,9 @@ import { globalScene } from "#app/global-scene";
import type { Variant } from "#app/sprites/variant"; import type { Variant } from "#app/sprites/variant";
import { populateVariantColors, variantColorCache } from "#app/sprites/variant"; import { populateVariantColors, variantColorCache } from "#app/sprites/variant";
import { variantData } from "#app/sprites/variant"; import { variantData } from "#app/sprites/variant";
import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from "#app/ui/battle-info"; import BattleInfo from "#app/ui/battle-info/battle-info";
import { EnemyBattleInfo } from "#app/ui/battle-info/enemy-battle-info";
import { PlayerBattleInfo } from "#app/ui/battle-info/player-battle-info";
import type Move from "#app/data/moves/move"; import type Move from "#app/data/moves/move";
import { import {
HighCritAttr, HighCritAttr,
@ -3347,22 +3349,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return this.battleInfo.updateInfo(this, instant); return this.battleInfo.updateInfo(this, instant);
} }
/**
* Show or hide the type effectiveness multiplier window
* Passing undefined will hide the window
*/
updateEffectiveness(effectiveness?: string) {
this.battleInfo.updateEffectiveness(effectiveness);
}
toggleStats(visible: boolean): void { toggleStats(visible: boolean): void {
this.battleInfo.toggleStats(visible); this.battleInfo.toggleStats(visible);
} }
toggleFlyout(visible: boolean): void {
this.battleInfo.toggleFlyout(visible);
}
/** /**
* Adds experience to this PlayerPokemon, subject to wave based level caps. * Adds experience to this PlayerPokemon, subject to wave based level caps.
* @param exp The amount of experience to add * @param exp The amount of experience to add
@ -5518,6 +5508,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
export class PlayerPokemon extends Pokemon { export class PlayerPokemon extends Pokemon {
protected battleInfo: PlayerBattleInfo;
public compatibleTms: Moves[]; public compatibleTms: Moves[];
constructor( constructor(
@ -6038,6 +6029,7 @@ export class PlayerPokemon extends Pokemon {
} }
export class EnemyPokemon extends Pokemon { export class EnemyPokemon extends Pokemon {
protected battleInfo: EnemyBattleInfo;
public trainerSlot: TrainerSlot; public trainerSlot: TrainerSlot;
public aiType: AiType; public aiType: AiType;
public bossSegments: number; public bossSegments: number;
@ -6709,6 +6701,19 @@ export class EnemyPokemon extends Pokemon {
return ret; return ret;
} }
/**
* Show or hide the type effectiveness multiplier window
* Passing undefined will hide the window
*/
updateEffectiveness(effectiveness?: string) {
this.battleInfo.updateEffectiveness(effectiveness);
}
toggleFlyout(visible: boolean): void {
this.battleInfo.toggleFlyout(visible);
}
} }
/** /**

View File

@ -29,7 +29,7 @@ window.addEventListener("unhandledrejection", event => {
const setPositionRelative = function (guideObject: Phaser.GameObjects.GameObject, x: number, y: number) { const setPositionRelative = function (guideObject: Phaser.GameObjects.GameObject, x: number, y: number) {
const offsetX = guideObject.width * (-0.5 + (0.5 - guideObject.originX)); const offsetX = guideObject.width * (-0.5 + (0.5 - guideObject.originX));
const offsetY = guideObject.height * (-0.5 + (0.5 - guideObject.originY)); const offsetY = guideObject.height * (-0.5 + (0.5 - guideObject.originY));
this.setPosition(guideObject.x + offsetX + x, guideObject.y + offsetY + y); return this.setPosition(guideObject.x + offsetX + x, guideObject.y + offsetY + y);
}; };
Phaser.GameObjects.Container.prototype.setPositionRelative = setPositionRelative; Phaser.GameObjects.Container.prototype.setPositionRelative = setPositionRelative;

View File

@ -20,37 +20,37 @@ declare module "phaser" {
/** /**
* Sets this object's position relative to another object with a given offset * Sets this object's position relative to another object with a given offset
*/ */
setPositionRelative(guideObject: any, x: number, y: number): void; setPositionRelative(guideObject: any, x: number, y: number): this;
} }
interface Sprite { interface Sprite {
/** /**
* Sets this object's position relative to another object with a given offset * Sets this object's position relative to another object with a given offset
*/ */
setPositionRelative(guideObject: any, x: number, y: number): void; setPositionRelative(guideObject: any, x: number, y: number): this;
} }
interface Image { interface Image {
/** /**
* Sets this object's position relative to another object with a given offset * Sets this object's position relative to another object with a given offset
*/ */
setPositionRelative(guideObject: any, x: number, y: number): void; setPositionRelative(guideObject: any, x: number, y: number): this;
} }
interface NineSlice { interface NineSlice {
/** /**
* Sets this object's position relative to another object with a given offset * Sets this object's position relative to another object with a given offset
*/ */
setPositionRelative(guideObject: any, x: number, y: number): void; setPositionRelative(guideObject: any, x: number, y: number): this;
} }
interface Text { interface Text {
/** /**
* Sets this object's position relative to another object with a given offset * Sets this object's position relative to another object with a given offset
*/ */
setPositionRelative(guideObject: any, x: number, y: number): void; setPositionRelative(guideObject: any, x: number, y: number): this;
} }
interface Rectangle { interface Rectangle {
/** /**
* Sets this object's position relative to another object with a given offset * Sets this object's position relative to another object with a given offset
*/ */
setPositionRelative(guideObject: any, x: number, y: number): void; setPositionRelative(guideObject: any, x: number, y: number): this;
} }
} }

View File

@ -161,7 +161,7 @@ export class UiInputs {
buttonInfo(pressed = true): void { buttonInfo(pressed = true): void {
if (globalScene.showMovesetFlyout) { if (globalScene.showMovesetFlyout) {
for (const p of globalScene.getField().filter(p => p?.isActive(true))) { for (const p of globalScene.getEnemyField().filter(p => p?.isActive(true))) {
p.toggleFlyout(pressed); p.toggleFlyout(pressed);
} }
} }

View File

@ -1,4 +1,4 @@
import type { default as Pokemon } from "../field/pokemon"; import type { EnemyPokemon, default as Pokemon } from "../field/pokemon";
import { addTextObject, TextStyle } from "./text"; import { addTextObject, TextStyle } from "./text";
import { fixedInt } from "#app/utils/common"; import { fixedInt } from "#app/utils/common";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
@ -126,7 +126,7 @@ export default class BattleFlyout extends Phaser.GameObjects.Container {
* Links the given {@linkcode Pokemon} and subscribes to the {@linkcode BattleSceneEventType.MOVE_USED} event * Links the given {@linkcode Pokemon} and subscribes to the {@linkcode BattleSceneEventType.MOVE_USED} event
* @param pokemon {@linkcode Pokemon} to link to this flyout * @param pokemon {@linkcode Pokemon} to link to this flyout
*/ */
initInfo(pokemon: Pokemon) { initInfo(pokemon: EnemyPokemon) {
this.pokemon = pokemon; this.pokemon = pokemon;
this.name = `Flyout ${getPokemonNameWithAffix(this.pokemon)}`; this.name = `Flyout ${getPokemonNameWithAffix(this.pokemon)}`;

View File

@ -1,986 +0,0 @@
import type { EnemyPokemon, default as Pokemon } from "../field/pokemon";
import { getLevelTotalExp, getLevelRelExp } from "../data/exp";
import { getLocalizedSpriteKey, fixedInt } from "#app/utils/common";
import { addTextObject, TextStyle } from "./text";
import { getGenderSymbol, getGenderColor, Gender } from "../data/gender";
import { StatusEffect } from "#enums/status-effect";
import { globalScene } from "#app/global-scene";
import { getTypeRgb } from "#app/data/type";
import { PokemonType } from "#enums/pokemon-type";
import { getVariantTint } from "#app/sprites/variant";
import { Stat } from "#enums/stat";
import BattleFlyout from "./battle-flyout";
import { WindowVariant, addWindow } from "./ui-theme";
import i18next from "i18next";
import { ExpGainsSpeed } from "#app/enums/exp-gains-speed";
export default class BattleInfo extends Phaser.GameObjects.Container {
public static readonly EXP_GAINS_DURATION_BASE = 1650;
private baseY: number;
private player: boolean;
private mini: boolean;
private boss: boolean;
private bossSegments: number;
private offset: boolean;
private lastName: string | null;
private lastTeraType: PokemonType;
private lastStatus: StatusEffect;
private lastHp: number;
private lastMaxHp: number;
private lastHpFrame: string | null;
private lastExp: number;
private lastLevelExp: number;
private lastLevel: number;
private lastLevelCapped: boolean;
private lastStats: string;
private box: Phaser.GameObjects.Sprite;
private nameText: Phaser.GameObjects.Text;
private genderText: Phaser.GameObjects.Text;
private ownedIcon: Phaser.GameObjects.Sprite;
private championRibbon: Phaser.GameObjects.Sprite;
private teraIcon: Phaser.GameObjects.Sprite;
private shinyIcon: Phaser.GameObjects.Sprite;
private fusionShinyIcon: Phaser.GameObjects.Sprite;
private splicedIcon: Phaser.GameObjects.Sprite;
private statusIndicator: Phaser.GameObjects.Sprite;
private levelContainer: Phaser.GameObjects.Container;
private hpBar: Phaser.GameObjects.Image;
private hpBarSegmentDividers: Phaser.GameObjects.Rectangle[];
private levelNumbersContainer: Phaser.GameObjects.Container;
private hpNumbersContainer: Phaser.GameObjects.Container;
private type1Icon: Phaser.GameObjects.Sprite;
private type2Icon: Phaser.GameObjects.Sprite;
private type3Icon: Phaser.GameObjects.Sprite;
private expBar: Phaser.GameObjects.Image;
// #region Type effectiveness hint objects
private effectivenessContainer: Phaser.GameObjects.Container;
private effectivenessWindow: Phaser.GameObjects.NineSlice;
private effectivenessText: Phaser.GameObjects.Text;
private currentEffectiveness?: string;
// #endregion
public expMaskRect: Phaser.GameObjects.Graphics;
private statsContainer: Phaser.GameObjects.Container;
private statsBox: Phaser.GameObjects.Sprite;
private statValuesContainer: Phaser.GameObjects.Container;
private statNumbers: Phaser.GameObjects.Sprite[];
public flyoutMenu?: BattleFlyout;
private statOrder: Stat[];
private readonly statOrderPlayer = [Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.ACC, Stat.EVA, Stat.SPD];
private readonly statOrderEnemy = [Stat.HP, Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.ACC, Stat.EVA, Stat.SPD];
constructor(x: number, y: number, player: boolean) {
super(globalScene, x, y);
this.baseY = y;
this.player = player;
this.mini = !player;
this.boss = false;
this.offset = false;
this.lastName = null;
this.lastTeraType = PokemonType.UNKNOWN;
this.lastStatus = StatusEffect.NONE;
this.lastHp = -1;
this.lastMaxHp = -1;
this.lastHpFrame = null;
this.lastExp = -1;
this.lastLevelExp = -1;
this.lastLevel = -1;
// Initially invisible and shown via Pokemon.showInfo
this.setVisible(false);
this.box = globalScene.add.sprite(0, 0, this.getTextureName());
this.box.setName("box");
this.box.setOrigin(1, 0.5);
this.add(this.box);
this.nameText = addTextObject(player ? -115 : -124, player ? -15.2 : -11.2, "", TextStyle.BATTLE_INFO);
this.nameText.setName("text_name");
this.nameText.setOrigin(0, 0);
this.add(this.nameText);
this.genderText = addTextObject(0, 0, "", TextStyle.BATTLE_INFO);
this.genderText.setName("text_gender");
this.genderText.setOrigin(0, 0);
this.genderText.setPositionRelative(this.nameText, 0, 2);
this.add(this.genderText);
if (!this.player) {
this.ownedIcon = globalScene.add.sprite(0, 0, "icon_owned");
this.ownedIcon.setName("icon_owned");
this.ownedIcon.setVisible(false);
this.ownedIcon.setOrigin(0, 0);
this.ownedIcon.setPositionRelative(this.nameText, 0, 11.75);
this.add(this.ownedIcon);
this.championRibbon = globalScene.add.sprite(0, 0, "champion_ribbon");
this.championRibbon.setName("icon_champion_ribbon");
this.championRibbon.setVisible(false);
this.championRibbon.setOrigin(0, 0);
this.championRibbon.setPositionRelative(this.nameText, 8, 11.75);
this.add(this.championRibbon);
}
this.teraIcon = globalScene.add.sprite(0, 0, "icon_tera");
this.teraIcon.setName("icon_tera");
this.teraIcon.setVisible(false);
this.teraIcon.setOrigin(0, 0);
this.teraIcon.setScale(0.5);
this.teraIcon.setPositionRelative(this.nameText, 0, 2);
this.teraIcon.setInteractive(new Phaser.Geom.Rectangle(0, 0, 12, 15), Phaser.Geom.Rectangle.Contains);
this.add(this.teraIcon);
this.shinyIcon = globalScene.add.sprite(0, 0, "shiny_star");
this.shinyIcon.setName("icon_shiny");
this.shinyIcon.setVisible(false);
this.shinyIcon.setOrigin(0, 0);
this.shinyIcon.setScale(0.5);
this.shinyIcon.setPositionRelative(this.nameText, 0, 2);
this.shinyIcon.setInteractive(new Phaser.Geom.Rectangle(0, 0, 12, 15), Phaser.Geom.Rectangle.Contains);
this.add(this.shinyIcon);
this.fusionShinyIcon = globalScene.add.sprite(0, 0, "shiny_star_2");
this.fusionShinyIcon.setName("icon_fusion_shiny");
this.fusionShinyIcon.setVisible(false);
this.fusionShinyIcon.setOrigin(0, 0);
this.fusionShinyIcon.setScale(0.5);
this.fusionShinyIcon.setPosition(this.shinyIcon.x, this.shinyIcon.y);
this.add(this.fusionShinyIcon);
this.splicedIcon = globalScene.add.sprite(0, 0, "icon_spliced");
this.splicedIcon.setName("icon_spliced");
this.splicedIcon.setVisible(false);
this.splicedIcon.setOrigin(0, 0);
this.splicedIcon.setScale(0.5);
this.splicedIcon.setPositionRelative(this.nameText, 0, 2);
this.splicedIcon.setInteractive(new Phaser.Geom.Rectangle(0, 0, 12, 15), Phaser.Geom.Rectangle.Contains);
this.add(this.splicedIcon);
this.statusIndicator = globalScene.add.sprite(0, 0, getLocalizedSpriteKey("statuses"));
this.statusIndicator.setName("icon_status");
this.statusIndicator.setVisible(false);
this.statusIndicator.setOrigin(0, 0);
this.statusIndicator.setPositionRelative(this.nameText, 0, 11.5);
this.add(this.statusIndicator);
this.levelContainer = globalScene.add.container(player ? -41 : -50, player ? -10 : -5);
this.levelContainer.setName("container_level");
this.add(this.levelContainer);
const levelOverlay = globalScene.add.image(0, 0, "overlay_lv");
this.levelContainer.add(levelOverlay);
this.hpBar = globalScene.add.image(player ? -61 : -71, player ? -1 : 4.5, "overlay_hp");
this.hpBar.setName("hp_bar");
this.hpBar.setOrigin(0);
this.add(this.hpBar);
this.hpBarSegmentDividers = [];
this.levelNumbersContainer = globalScene.add.container(9.5, globalScene.uiTheme ? 0 : -0.5);
this.levelNumbersContainer.setName("container_level");
this.levelContainer.add(this.levelNumbersContainer);
if (this.player) {
this.hpNumbersContainer = globalScene.add.container(-15, 10);
this.hpNumbersContainer.setName("container_hp");
this.add(this.hpNumbersContainer);
const expBar = globalScene.add.image(-98, 18, "overlay_exp");
expBar.setName("overlay_exp");
expBar.setOrigin(0);
this.add(expBar);
const expMaskRect = globalScene.make.graphics({});
expMaskRect.setScale(6);
expMaskRect.fillStyle(0xffffff);
expMaskRect.beginPath();
expMaskRect.fillRect(127, 126, 85, 2);
const expMask = expMaskRect.createGeometryMask();
expBar.setMask(expMask);
this.expBar = expBar;
this.expMaskRect = expMaskRect;
}
this.statsContainer = globalScene.add.container(0, 0);
this.statsContainer.setName("container_stats");
this.statsContainer.setAlpha(0);
this.add(this.statsContainer);
this.statsBox = globalScene.add.sprite(0, 0, `${this.getTextureName()}_stats`);
this.statsBox.setName("box_stats");
this.statsBox.setOrigin(1, 0.5);
this.statsContainer.add(this.statsBox);
const statLabels: Phaser.GameObjects.Sprite[] = [];
this.statNumbers = [];
this.statValuesContainer = globalScene.add.container(0, 0);
this.statsContainer.add(this.statValuesContainer);
// this gives us a different starting location from the left of the label and padding between stats for a player vs enemy
// since the player won't have HP to show, it doesn't need to change from the current version
const startingX = this.player ? -this.statsBox.width + 8 : -this.statsBox.width + 5;
const paddingX = this.player ? 4 : 2;
const statOverflow = this.player ? 1 : 0;
this.statOrder = this.player ? this.statOrderPlayer : this.statOrderEnemy; // this tells us whether or not to use the player or enemy battle stat order
this.statOrder.map((s, i) => {
// we do a check for i > statOverflow to see when the stat labels go onto the next column
// For enemies, we have HP (i=0) by itself then a new column, so we check for i > 0
// For players, we don't have HP, so we start with i = 0 and i = 1 for our first column, and so need to check for i > 1
const statX =
i > statOverflow
? this.statNumbers[Math.max(i - 2, 0)].x + this.statNumbers[Math.max(i - 2, 0)].width + paddingX
: startingX; // we have the Math.max(i - 2, 0) in there so for i===1 to not return a negative number; since this is now based on anything >0 instead of >1, we need to allow for i-2 < 0
const baseY = -this.statsBox.height / 2 + 4; // this is the baseline for the y-axis
let statY: number; // this will be the y-axis placement for the labels
if (this.statOrder[i] === Stat.SPD || this.statOrder[i] === Stat.HP) {
statY = baseY + 5;
} else {
statY = baseY + (!!(i % 2) === this.player ? 10 : 0); // we compare i % 2 against this.player to tell us where to place the label; because this.battleStatOrder for enemies has HP, this.battleStatOrder[1]=ATK, but for players this.battleStatOrder[0]=ATK, so this comparing i % 2 to this.player fixes this issue for us
}
const statLabel = globalScene.add.sprite(statX, statY, "pbinfo_stat", Stat[s]);
statLabel.setName("icon_stat_label_" + i.toString());
statLabel.setOrigin(0, 0);
statLabels.push(statLabel);
this.statValuesContainer.add(statLabel);
const statNumber = globalScene.add.sprite(
statX + statLabel.width,
statY,
"pbinfo_stat_numbers",
this.statOrder[i] !== Stat.HP ? "3" : "empty",
);
statNumber.setName("icon_stat_number_" + i.toString());
statNumber.setOrigin(0, 0);
this.statNumbers.push(statNumber);
this.statValuesContainer.add(statNumber);
if (this.statOrder[i] === Stat.HP) {
statLabel.setVisible(false);
statNumber.setVisible(false);
}
});
if (!this.player) {
this.flyoutMenu = new BattleFlyout(this.player);
this.add(this.flyoutMenu);
this.moveBelow<Phaser.GameObjects.GameObject>(this.flyoutMenu, this.box);
}
this.type1Icon = globalScene.add.sprite(
player ? -139 : -15,
player ? -17 : -15.5,
`pbinfo_${player ? "player" : "enemy"}_type1`,
);
this.type1Icon.setName("icon_type_1");
this.type1Icon.setOrigin(0, 0);
this.add(this.type1Icon);
this.type2Icon = globalScene.add.sprite(
player ? -139 : -15,
player ? -1 : -2.5,
`pbinfo_${player ? "player" : "enemy"}_type2`,
);
this.type2Icon.setName("icon_type_2");
this.type2Icon.setOrigin(0, 0);
this.add(this.type2Icon);
this.type3Icon = globalScene.add.sprite(
player ? -154 : 0,
player ? -17 : -15.5,
`pbinfo_${player ? "player" : "enemy"}_type`,
);
this.type3Icon.setName("icon_type_3");
this.type3Icon.setOrigin(0, 0);
this.add(this.type3Icon);
if (!this.player) {
this.effectivenessContainer = globalScene.add.container(0, 0);
this.effectivenessContainer.setPositionRelative(this.type1Icon, 22, 4);
this.effectivenessContainer.setVisible(false);
this.add(this.effectivenessContainer);
this.effectivenessText = addTextObject(5, 4.5, "", TextStyle.BATTLE_INFO);
this.effectivenessWindow = addWindow(0, 0, 0, 20, undefined, false, undefined, undefined, WindowVariant.XTHIN);
this.effectivenessContainer.add(this.effectivenessWindow);
this.effectivenessContainer.add(this.effectivenessText);
}
}
getStatsValueContainer(): Phaser.GameObjects.Container {
return this.statValuesContainer;
}
initInfo(pokemon: Pokemon) {
this.updateNameText(pokemon);
const nameTextWidth = this.nameText.displayWidth;
this.name = pokemon.getNameToRender();
this.box.name = pokemon.getNameToRender();
this.flyoutMenu?.initInfo(pokemon);
this.genderText.setText(getGenderSymbol(pokemon.gender));
this.genderText.setColor(getGenderColor(pokemon.gender));
this.genderText.setPositionRelative(this.nameText, nameTextWidth, 0);
this.lastTeraType = pokemon.getTeraType();
this.teraIcon.setPositionRelative(this.nameText, nameTextWidth + this.genderText.displayWidth + 1, 2);
this.teraIcon.setVisible(pokemon.isTerastallized);
this.teraIcon.on("pointerover", () => {
if (pokemon.isTerastallized) {
globalScene.ui.showTooltip(
"",
i18next.t("fightUiHandler:teraHover", {
type: i18next.t(`pokemonInfo:Type.${PokemonType[this.lastTeraType]}`),
}),
);
}
});
this.teraIcon.on("pointerout", () => globalScene.ui.hideTooltip());
const isFusion = pokemon.isFusion(true);
this.splicedIcon.setPositionRelative(
this.nameText,
nameTextWidth + this.genderText.displayWidth + 1 + (this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0),
2.5,
);
this.splicedIcon.setVisible(isFusion);
if (this.splicedIcon.visible) {
this.splicedIcon.on("pointerover", () =>
globalScene.ui.showTooltip(
"",
`${pokemon.species.getName(pokemon.formIndex)}/${pokemon.fusionSpecies?.getName(pokemon.fusionFormIndex)}`,
),
);
this.splicedIcon.on("pointerout", () => globalScene.ui.hideTooltip());
}
const doubleShiny = isFusion && pokemon.shiny && pokemon.fusionShiny;
const baseVariant = !doubleShiny ? pokemon.getVariant(true) : pokemon.variant;
this.shinyIcon.setPositionRelative(
this.nameText,
nameTextWidth +
this.genderText.displayWidth +
1 +
(this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0) +
(this.splicedIcon.visible ? this.splicedIcon.displayWidth + 1 : 0),
2.5,
);
this.shinyIcon.setTexture(`shiny_star${doubleShiny ? "_1" : ""}`);
this.shinyIcon.setVisible(pokemon.isShiny());
this.shinyIcon.setTint(getVariantTint(baseVariant));
if (this.shinyIcon.visible) {
const shinyDescriptor =
doubleShiny || baseVariant
? `${baseVariant === 2 ? i18next.t("common:epicShiny") : baseVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}${doubleShiny ? `/${pokemon.fusionVariant === 2 ? i18next.t("common:epicShiny") : pokemon.fusionVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}` : ""}`
: "";
this.shinyIcon.on("pointerover", () =>
globalScene.ui.showTooltip(
"",
`${i18next.t("common:shinyOnHover")}${shinyDescriptor ? ` (${shinyDescriptor})` : ""}`,
),
);
this.shinyIcon.on("pointerout", () => globalScene.ui.hideTooltip());
}
this.fusionShinyIcon.setPosition(this.shinyIcon.x, this.shinyIcon.y);
this.fusionShinyIcon.setVisible(doubleShiny);
if (isFusion) {
this.fusionShinyIcon.setTint(getVariantTint(pokemon.fusionVariant));
}
if (!this.player) {
if (this.nameText.visible) {
this.nameText.on("pointerover", () =>
globalScene.ui.showTooltip(
"",
i18next.t("battleInfo:generation", {
generation: i18next.t(`starterSelectUiHandler:gen${pokemon.species.generation}`),
}),
),
);
this.nameText.on("pointerout", () => globalScene.ui.hideTooltip());
}
const dexEntry = globalScene.gameData.dexData[pokemon.species.speciesId];
this.ownedIcon.setVisible(!!dexEntry.caughtAttr);
const opponentPokemonDexAttr = pokemon.getDexAttr();
if (globalScene.gameMode.isClassic) {
if (
globalScene.gameData.starterData[pokemon.species.getRootSpeciesId()].classicWinCount > 0 &&
globalScene.gameData.starterData[pokemon.species.getRootSpeciesId(true)].classicWinCount > 0
) {
this.championRibbon.setVisible(true);
}
}
// Check if Player owns all genders and forms of the Pokemon
const missingDexAttrs = (dexEntry.caughtAttr & opponentPokemonDexAttr) < opponentPokemonDexAttr;
const ownedAbilityAttrs = globalScene.gameData.starterData[pokemon.species.getRootSpeciesId()].abilityAttr;
// Check if the player owns ability for the root form
const playerOwnsThisAbility = pokemon.checkIfPlayerHasAbilityOfStarter(ownedAbilityAttrs);
if (missingDexAttrs || !playerOwnsThisAbility) {
this.ownedIcon.setTint(0x808080);
}
if (this.boss) {
this.updateBossSegmentDividers(pokemon as EnemyPokemon);
}
}
this.hpBar.setScale(pokemon.getHpRatio(true), 1);
this.lastHpFrame = this.hpBar.scaleX > 0.5 ? "high" : this.hpBar.scaleX > 0.25 ? "medium" : "low";
this.hpBar.setFrame(this.lastHpFrame);
if (this.player) {
this.setHpNumbers(pokemon.hp, pokemon.getMaxHp());
}
this.lastHp = pokemon.hp;
this.lastMaxHp = pokemon.getMaxHp();
this.setLevel(pokemon.level);
this.lastLevel = pokemon.level;
this.shinyIcon.setVisible(pokemon.isShiny());
const types = pokemon.getTypes(true, false, undefined, true);
this.type1Icon.setTexture(`pbinfo_${this.player ? "player" : "enemy"}_type${types.length > 1 ? "1" : ""}`);
this.type1Icon.setFrame(PokemonType[types[0]].toLowerCase());
this.type2Icon.setVisible(types.length > 1);
this.type3Icon.setVisible(types.length > 2);
if (types.length > 1) {
this.type2Icon.setFrame(PokemonType[types[1]].toLowerCase());
}
if (types.length > 2) {
this.type3Icon.setFrame(PokemonType[types[2]].toLowerCase());
}
if (this.player) {
this.expMaskRect.x = (pokemon.levelExp / getLevelTotalExp(pokemon.level, pokemon.species.growthRate)) * 510;
this.lastExp = pokemon.exp;
this.lastLevelExp = pokemon.levelExp;
this.statValuesContainer.setPosition(8, 7);
}
const stats = this.statOrder.map(() => 0);
this.lastStats = stats.join("");
this.updateStats(stats);
}
getTextureName(): string {
return `pbinfo_${this.player ? "player" : "enemy"}${!this.player && this.boss ? "_boss" : this.mini ? "_mini" : ""}`;
}
setMini(mini: boolean): void {
if (this.mini === mini) {
return;
}
this.mini = mini;
this.box.setTexture(this.getTextureName());
this.statsBox.setTexture(`${this.getTextureName()}_stats`);
if (this.player) {
this.y -= 12 * (mini ? 1 : -1);
this.baseY = this.y;
}
const offsetElements = [
this.nameText,
this.genderText,
this.teraIcon,
this.splicedIcon,
this.shinyIcon,
this.statusIndicator,
this.levelContainer,
];
offsetElements.forEach(el => (el.y += 1.5 * (mini ? -1 : 1)));
[this.type1Icon, this.type2Icon, this.type3Icon].forEach(el => {
el.x += 4 * (mini ? 1 : -1);
el.y += -8 * (mini ? 1 : -1);
});
this.statValuesContainer.x += 2 * (mini ? 1 : -1);
this.statValuesContainer.y += -7 * (mini ? 1 : -1);
const toggledElements = [this.hpNumbersContainer, this.expBar];
toggledElements.forEach(el => el.setVisible(!mini));
}
toggleStats(visible: boolean): void {
globalScene.tweens.add({
targets: this.statsContainer,
duration: fixedInt(125),
ease: "Sine.easeInOut",
alpha: visible ? 1 : 0,
});
}
updateBossSegments(pokemon: EnemyPokemon): void {
const boss = !!pokemon.bossSegments;
if (boss !== this.boss) {
this.boss = boss;
[
this.nameText,
this.genderText,
this.teraIcon,
this.splicedIcon,
this.shinyIcon,
this.ownedIcon,
this.championRibbon,
this.statusIndicator,
this.statValuesContainer,
].map(e => (e.x += 48 * (boss ? -1 : 1)));
this.hpBar.x += 38 * (boss ? -1 : 1);
this.hpBar.y += 2 * (this.boss ? -1 : 1);
this.levelContainer.x += 2 * (boss ? -1 : 1);
this.hpBar.setTexture(`overlay_hp${boss ? "_boss" : ""}`);
this.box.setTexture(this.getTextureName());
this.statsBox.setTexture(`${this.getTextureName()}_stats`);
}
this.bossSegments = boss ? pokemon.bossSegments : 0;
this.updateBossSegmentDividers(pokemon);
}
updateBossSegmentDividers(pokemon: EnemyPokemon): void {
while (this.hpBarSegmentDividers.length) {
this.hpBarSegmentDividers.pop()?.destroy();
}
if (this.boss && this.bossSegments > 1) {
const uiTheme = globalScene.uiTheme;
const maxHp = pokemon.getMaxHp();
for (let s = 1; s < this.bossSegments; s++) {
const dividerX = (Math.round((maxHp / this.bossSegments) * s) / maxHp) * this.hpBar.width;
const divider = globalScene.add.rectangle(
0,
0,
1,
this.hpBar.height - (uiTheme ? 0 : 1),
pokemon.bossSegmentIndex >= s ? 0xffffff : 0x404040,
);
divider.setOrigin(0.5, 0);
divider.setName("hpBar_divider_" + s.toString());
this.add(divider);
this.moveBelow(divider as Phaser.GameObjects.GameObject, this.statsContainer);
divider.setPositionRelative(this.hpBar, dividerX, uiTheme ? 0 : 1);
this.hpBarSegmentDividers.push(divider);
}
}
}
setOffset(offset: boolean): void {
if (this.offset === offset) {
return;
}
this.offset = offset;
this.x += 10 * (this.offset === this.player ? 1 : -1);
this.y += 27 * (this.offset ? 1 : -1);
this.baseY = this.y;
}
updateInfo(pokemon: Pokemon, instant?: boolean): Promise<void> {
return new Promise(resolve => {
if (!globalScene) {
return resolve();
}
const gender = pokemon.summonData.illusion?.gender ?? pokemon.gender;
this.genderText.setText(getGenderSymbol(gender));
this.genderText.setColor(getGenderColor(gender));
const nameUpdated = this.lastName !== pokemon.getNameToRender();
if (nameUpdated) {
this.updateNameText(pokemon);
this.genderText.setPositionRelative(this.nameText, this.nameText.displayWidth, 0);
}
const teraType = pokemon.isTerastallized ? pokemon.getTeraType() : PokemonType.UNKNOWN;
const teraTypeUpdated = this.lastTeraType !== teraType;
if (teraTypeUpdated) {
this.teraIcon.setVisible(teraType !== PokemonType.UNKNOWN);
this.teraIcon.setPositionRelative(
this.nameText,
this.nameText.displayWidth + this.genderText.displayWidth + 1,
2,
);
this.teraIcon.setTintFill(Phaser.Display.Color.GetColor(...getTypeRgb(teraType)));
this.lastTeraType = teraType;
}
const isFusion = pokemon.isFusion(true);
if (nameUpdated || teraTypeUpdated) {
this.splicedIcon.setVisible(isFusion);
this.teraIcon.setPositionRelative(
this.nameText,
this.nameText.displayWidth + this.genderText.displayWidth + 1,
2,
);
this.splicedIcon.setPositionRelative(
this.nameText,
this.nameText.displayWidth +
this.genderText.displayWidth +
1 +
(this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0),
1.5,
);
this.shinyIcon.setPositionRelative(
this.nameText,
this.nameText.displayWidth +
this.genderText.displayWidth +
1 +
(this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0) +
(this.splicedIcon.visible ? this.splicedIcon.displayWidth + 1 : 0),
2.5,
);
}
if (this.lastStatus !== (pokemon.status?.effect || StatusEffect.NONE)) {
this.lastStatus = pokemon.status?.effect || StatusEffect.NONE;
if (this.lastStatus !== StatusEffect.NONE) {
this.statusIndicator.setFrame(StatusEffect[this.lastStatus].toLowerCase());
}
const offsetX = !this.player ? (this.ownedIcon.visible ? 8 : 0) + (this.championRibbon.visible ? 8 : 0) : 0;
this.statusIndicator.setPositionRelative(this.nameText, offsetX, 11.5);
this.statusIndicator.setVisible(!!this.lastStatus);
}
const types = pokemon.getTypes(true, false, undefined, true);
this.type1Icon.setTexture(`pbinfo_${this.player ? "player" : "enemy"}_type${types.length > 1 ? "1" : ""}`);
this.type1Icon.setFrame(PokemonType[types[0]].toLowerCase());
this.type2Icon.setVisible(types.length > 1);
this.type3Icon.setVisible(types.length > 2);
if (types.length > 1) {
this.type2Icon.setFrame(PokemonType[types[1]].toLowerCase());
}
if (types.length > 2) {
this.type3Icon.setFrame(PokemonType[types[2]].toLowerCase());
}
const updateHpFrame = () => {
const hpFrame = this.hpBar.scaleX > 0.5 ? "high" : this.hpBar.scaleX > 0.25 ? "medium" : "low";
if (hpFrame !== this.lastHpFrame) {
this.hpBar.setFrame(hpFrame);
this.lastHpFrame = hpFrame;
}
};
const updatePokemonHp = () => {
let duration = !instant ? Phaser.Math.Clamp(Math.abs(this.lastHp - pokemon.hp) * 5, 250, 5000) : 0;
const speed = globalScene.hpBarSpeed;
if (speed) {
duration = speed >= 3 ? 0 : duration / Math.pow(2, speed);
}
globalScene.tweens.add({
targets: this.hpBar,
ease: "Sine.easeOut",
scaleX: pokemon.getHpRatio(true),
duration: duration,
onUpdate: () => {
if (this.player && this.lastHp !== pokemon.hp) {
const tweenHp = Math.ceil(this.hpBar.scaleX * pokemon.getMaxHp());
this.setHpNumbers(tweenHp, pokemon.getMaxHp());
this.lastHp = tweenHp;
}
updateHpFrame();
},
onComplete: () => {
updateHpFrame();
// If, after tweening, the hp is different from the original (due to rounding), force the hp number display
// to update to the correct value.
if (this.player && this.lastHp !== pokemon.hp) {
this.setHpNumbers(pokemon.hp, pokemon.getMaxHp());
this.lastHp = pokemon.hp;
}
resolve();
},
});
if (!this.player) {
this.lastHp = pokemon.hp;
}
this.lastMaxHp = pokemon.getMaxHp();
};
if (this.player) {
const isLevelCapped = pokemon.level >= globalScene.getMaxExpLevel();
if (this.lastExp !== pokemon.exp || this.lastLevel !== pokemon.level) {
const originalResolve = resolve;
const durationMultipler = Math.max(
Phaser.Tweens.Builders.GetEaseFunction("Cubic.easeIn")(
1 - Math.min(pokemon.level - this.lastLevel, 10) / 10,
),
0.1,
);
resolve = () => this.updatePokemonExp(pokemon, false, durationMultipler).then(() => originalResolve());
} else if (isLevelCapped !== this.lastLevelCapped) {
this.setLevel(pokemon.level);
}
this.lastLevelCapped = isLevelCapped;
}
if (this.lastHp !== pokemon.hp || this.lastMaxHp !== pokemon.getMaxHp()) {
return updatePokemonHp();
}
if (!this.player && this.lastLevel !== pokemon.level) {
this.setLevel(pokemon.level);
this.lastLevel = pokemon.level;
}
const stats = pokemon.getStatStages();
const statsStr = stats.join("");
if (this.lastStats !== statsStr) {
this.updateStats(stats);
this.lastStats = statsStr;
}
this.shinyIcon.setVisible(pokemon.isShiny(true));
const doubleShiny = isFusion && pokemon.shiny && pokemon.fusionShiny;
const baseVariant = !doubleShiny ? pokemon.getVariant(true) : pokemon.variant;
this.shinyIcon.setTint(getVariantTint(baseVariant));
this.fusionShinyIcon.setVisible(doubleShiny);
if (isFusion) {
this.fusionShinyIcon.setTint(getVariantTint(pokemon.fusionVariant));
}
this.fusionShinyIcon.setPosition(this.shinyIcon.x, this.shinyIcon.y);
resolve();
});
}
updateNameText(pokemon: Pokemon): void {
let displayName = pokemon.getNameToRender().replace(/[♂♀]/g, "");
let nameTextWidth: number;
const nameSizeTest = addTextObject(0, 0, displayName, TextStyle.BATTLE_INFO);
nameTextWidth = nameSizeTest.displayWidth;
const gender = pokemon.summonData.illusion?.gender ?? pokemon.gender;
while (
nameTextWidth >
(this.player || !this.boss ? 60 : 98) -
((gender !== Gender.GENDERLESS ? 6 : 0) +
(pokemon.fusionSpecies ? 8 : 0) +
(pokemon.isShiny() ? 8 : 0) +
(Math.min(pokemon.level.toString().length, 3) - 3) * 8)
) {
displayName = `${displayName.slice(0, displayName.endsWith(".") ? -2 : -1).trimEnd()}.`;
nameSizeTest.setText(displayName);
nameTextWidth = nameSizeTest.displayWidth;
}
nameSizeTest.destroy();
this.nameText.setText(displayName);
this.lastName = pokemon.getNameToRender();
if (this.nameText.visible) {
this.nameText.setInteractive(
new Phaser.Geom.Rectangle(0, 0, this.nameText.width, this.nameText.height),
Phaser.Geom.Rectangle.Contains,
);
}
}
updatePokemonExp(pokemon: Pokemon, instant?: boolean, levelDurationMultiplier = 1): Promise<void> {
return new Promise(resolve => {
const levelUp = this.lastLevel < pokemon.level;
const relLevelExp = getLevelRelExp(this.lastLevel + 1, pokemon.species.growthRate);
const levelExp = levelUp ? relLevelExp : pokemon.levelExp;
let ratio = relLevelExp ? levelExp / relLevelExp : 0;
if (this.lastLevel >= globalScene.getMaxExpLevel(true)) {
if (levelUp) {
ratio = 1;
} else {
ratio = 0;
}
instant = true;
}
const durationMultiplier = Phaser.Tweens.Builders.GetEaseFunction("Sine.easeIn")(
1 - Math.max(this.lastLevel - 100, 0) / 150,
);
let duration =
this.visible && !instant
? ((levelExp - this.lastLevelExp) / relLevelExp) *
BattleInfo.EXP_GAINS_DURATION_BASE *
durationMultiplier *
levelDurationMultiplier
: 0;
const speed = globalScene.expGainsSpeed;
if (speed && speed >= ExpGainsSpeed.DEFAULT) {
duration = speed >= ExpGainsSpeed.SKIP ? ExpGainsSpeed.DEFAULT : duration / Math.pow(2, speed);
}
if (ratio === 1) {
this.lastLevelExp = 0;
this.lastLevel++;
} else {
this.lastExp = pokemon.exp;
this.lastLevelExp = pokemon.levelExp;
}
if (duration) {
globalScene.playSound("se/exp");
}
globalScene.tweens.add({
targets: this.expMaskRect,
ease: "Sine.easeIn",
x: ratio * 510,
duration: duration,
onComplete: () => {
if (!globalScene) {
return resolve();
}
if (duration) {
globalScene.sound.stopByKey("se/exp");
}
if (ratio === 1) {
globalScene.playSound("se/level_up");
this.setLevel(this.lastLevel);
globalScene.time.delayedCall(500 * levelDurationMultiplier, () => {
this.expMaskRect.x = 0;
this.updateInfo(pokemon, instant).then(() => resolve());
});
return;
}
resolve();
},
});
});
}
setLevel(level: number): void {
const isCapped = level >= globalScene.getMaxExpLevel();
this.levelNumbersContainer.removeAll(true);
const levelStr = level.toString();
for (let i = 0; i < levelStr.length; i++) {
this.levelNumbersContainer.add(
globalScene.add.image(i * 8, 0, `numbers${isCapped && this.player ? "_red" : ""}`, levelStr[i]),
);
}
this.levelContainer.setX((this.player ? -41 : -50) - 8 * Math.max(levelStr.length - 3, 0));
}
setHpNumbers(hp: number, maxHp: number): void {
if (!this.player || !globalScene) {
return;
}
this.hpNumbersContainer.removeAll(true);
const hpStr = hp.toString();
const maxHpStr = maxHp.toString();
let offset = 0;
for (let i = maxHpStr.length - 1; i >= 0; i--) {
this.hpNumbersContainer.add(globalScene.add.image(offset++ * -8, 0, "numbers", maxHpStr[i]));
}
this.hpNumbersContainer.add(globalScene.add.image(offset++ * -8, 0, "numbers", "/"));
for (let i = hpStr.length - 1; i >= 0; i--) {
this.hpNumbersContainer.add(globalScene.add.image(offset++ * -8, 0, "numbers", hpStr[i]));
}
}
updateStats(stats: number[]): void {
this.statOrder.map((s, i) => {
if (s !== Stat.HP) {
this.statNumbers[i].setFrame(stats[s - 1].toString());
}
});
}
/**
* Request the flyoutMenu to toggle if available and hides or shows the effectiveness window where necessary
*/
toggleFlyout(visible: boolean): void {
this.flyoutMenu?.toggleFlyout(visible);
if (visible) {
this.effectivenessContainer?.setVisible(false);
} else {
this.updateEffectiveness(this.currentEffectiveness);
}
}
/**
* Show or hide the type effectiveness multiplier window
* Passing undefined will hide the window
*/
updateEffectiveness(effectiveness?: string) {
if (this.player) {
return;
}
this.currentEffectiveness = effectiveness;
if (!globalScene.typeHints || effectiveness === undefined || this.flyoutMenu?.flyoutVisible) {
this.effectivenessContainer.setVisible(false);
return;
}
this.effectivenessText.setText(effectiveness);
this.effectivenessWindow.width = 10 + this.effectivenessText.displayWidth;
this.effectivenessContainer.setVisible(true);
}
getBaseY(): number {
return this.baseY;
}
resetY(): void {
this.y = this.baseY;
}
}
export class PlayerBattleInfo extends BattleInfo {
constructor() {
super(Math.floor(globalScene.game.canvas.width / 6) - 10, -72, true);
}
}
export class EnemyBattleInfo extends BattleInfo {
constructor() {
super(140, -141, false);
}
setMini(_mini: boolean): void {} // Always mini
}

View File

@ -0,0 +1,688 @@
import type { default as Pokemon } from "../../field/pokemon";
import { getLocalizedSpriteKey, fixedInt, getShinyDescriptor } from "#app/utils/common";
import { addTextObject, TextStyle } from "../text";
import { getGenderSymbol, getGenderColor, Gender } from "../../data/gender";
import { StatusEffect } from "#enums/status-effect";
import { globalScene } from "#app/global-scene";
import { getTypeRgb } from "#app/data/type";
import { PokemonType } from "#enums/pokemon-type";
import { getVariantTint } from "#app/sprites/variant";
import { Stat } from "#enums/stat";
import i18next from "i18next";
/**
* Parameters influencing the position of elements within the battle info container
*/
export type BattleInfoParamList = {
/** X offset for the name text*/
nameTextX: number;
/** Y offset for the name text */
nameTextY: number;
/** X offset for the level container */
levelContainerX: number;
/** Y offset for the level container */
levelContainerY: number;
/** X offset for the hp bar */
hpBarX: number;
/** Y offset for the hp bar */
hpBarY: number;
/** Parameters for the stat box container */
statBox: {
/** The starting offset from the left of the label for the entries in the stat box */
xOffset: number;
/** The X padding between each number column */
paddingX: number;
/** The index of the stat entries at which paddingX is used instead of startingX */
statOverflow: number;
};
};
export default abstract class BattleInfo extends Phaser.GameObjects.Container {
public static readonly EXP_GAINS_DURATION_BASE = 1650;
protected baseY: number;
protected baseLvContainerX: number;
protected player: boolean;
protected mini: boolean;
protected boss: boolean;
protected bossSegments: number;
protected offset: boolean;
protected lastName: string | null;
protected lastTeraType: PokemonType;
protected lastStatus: StatusEffect;
protected lastHp: number;
protected lastMaxHp: number;
protected lastHpFrame: string | null;
protected lastExp: number;
protected lastLevelExp: number;
protected lastLevel: number;
protected lastLevelCapped: boolean;
protected lastStats: string;
protected box: Phaser.GameObjects.Sprite;
protected nameText: Phaser.GameObjects.Text;
protected genderText: Phaser.GameObjects.Text;
protected teraIcon: Phaser.GameObjects.Sprite;
protected shinyIcon: Phaser.GameObjects.Sprite;
protected fusionShinyIcon: Phaser.GameObjects.Sprite;
protected splicedIcon: Phaser.GameObjects.Sprite;
protected statusIndicator: Phaser.GameObjects.Sprite;
protected levelContainer: Phaser.GameObjects.Container;
protected hpBar: Phaser.GameObjects.Image;
protected levelNumbersContainer: Phaser.GameObjects.Container;
protected type1Icon: Phaser.GameObjects.Sprite;
protected type2Icon: Phaser.GameObjects.Sprite;
protected type3Icon: Phaser.GameObjects.Sprite;
protected expBar: Phaser.GameObjects.Image;
public expMaskRect: Phaser.GameObjects.Graphics;
protected statsContainer: Phaser.GameObjects.Container;
protected statsBox: Phaser.GameObjects.Sprite;
protected statValuesContainer: Phaser.GameObjects.Container;
protected statNumbers: Phaser.GameObjects.Sprite[];
get statOrder(): Stat[] {
return [];
}
/** Helper method used by the constructor to create the tera and shiny icons next to the name */
private constructIcons() {
const hitArea = new Phaser.Geom.Rectangle(0, 0, 12, 15);
const hitCallback = Phaser.Geom.Rectangle.Contains;
this.teraIcon = globalScene.add
.sprite(0, 0, "icon_tera")
.setName("icon_tera")
.setVisible(false)
.setOrigin(0)
.setScale(0.5)
.setInteractive(hitArea, hitCallback)
.setPositionRelative(this.nameText, 0, 2);
this.shinyIcon = globalScene.add
.sprite(0, 0, "shiny_star")
.setName("icon_shiny")
.setVisible(false)
.setOrigin(0)
.setScale(0.5)
.setInteractive(hitArea, hitCallback)
.setPositionRelative(this.nameText, 0, 2);
this.fusionShinyIcon = globalScene.add
.sprite(0, 0, "shiny_star_2")
.setName("icon_fusion_shiny")
.setVisible(false)
.setOrigin(0)
.setScale(0.5)
.copyPosition(this.shinyIcon);
this.splicedIcon = globalScene.add
.sprite(0, 0, "icon_spliced")
.setName("icon_spliced")
.setVisible(false)
.setOrigin(0)
.setScale(0.5)
.setInteractive(hitArea, hitCallback)
.setPositionRelative(this.nameText, 0, 2);
this.add([this.teraIcon, this.shinyIcon, this.fusionShinyIcon, this.splicedIcon]);
}
/**
* Submethod of the constructor that creates and adds the stats container to the battle info
*/
protected constructStatContainer({ xOffset, paddingX, statOverflow }: BattleInfoParamList["statBox"]): void {
this.statsContainer = globalScene.add.container(0, 0).setName("container_stats").setAlpha(0);
this.add(this.statsContainer);
this.statsBox = globalScene.add
.sprite(0, 0, `${this.getTextureName()}_stats`)
.setName("box_stats")
.setOrigin(1, 0.5);
this.statsContainer.add(this.statsBox);
const statLabels: Phaser.GameObjects.Sprite[] = [];
this.statNumbers = [];
this.statValuesContainer = globalScene.add.container();
this.statsContainer.add(this.statValuesContainer);
const startingX = -this.statsBox.width + xOffset;
// this gives us a different starting location from the left of the label and padding between stats for a player vs enemy
// since the player won't have HP to show, it doesn't need to change from the current version
for (const [i, s] of this.statOrder.entries()) {
const isHp = s === Stat.HP;
// we do a check for i > statOverflow to see when the stat labels go onto the next column
// For enemies, we have HP (i=0) by itself then a new column, so we check for i > 0
// For players, we don't have HP, so we start with i = 0 and i = 1 for our first column, and so need to check for i > 1
const statX =
i > statOverflow
? this.statNumbers[Math.max(i - 2, 0)].x + this.statNumbers[Math.max(i - 2, 0)].width + paddingX
: startingX; // we have the Math.max(i - 2, 0) in there so for i===1 to not return a negative number; since this is now based on anything >0 instead of >1, we need to allow for i-2 < 0
let statY = -this.statsBox.height / 2 + 4; // this is the baseline for the y-axis
if (isHp || s === Stat.SPD) {
statY += 5;
} else if (this.player === !!(i % 2)) {
// we compare i % 2 against this.player to tell us where to place the label
// because this.battleStatOrder for enemies has HP, this.battleStatOrder[1]=ATK, but for players
// this.battleStatOrder[0]=ATK, so this comparing i % 2 to this.player fixes this issue for us
statY += 10;
}
const statLabel = globalScene.add
.sprite(statX, statY, "pbinfo_stat", Stat[s])
.setName("icon_stat_label_" + i.toString())
.setOrigin(0);
statLabels.push(statLabel);
this.statValuesContainer.add(statLabel);
const statNumber = globalScene.add
.sprite(statX + statLabel.width, statY, "pbinfo_stat_numbers", !isHp ? "3" : "empty")
.setName("icon_stat_number_" + i.toString())
.setOrigin(0);
this.statNumbers.push(statNumber);
this.statValuesContainer.add(statNumber);
if (isHp) {
statLabel.setVisible(false);
statNumber.setVisible(false);
}
}
}
/**
* Submethod of the constructor that creates and adds the pokemon type icons to the battle info
*/
protected abstract constructTypeIcons(): void;
/**
* @param x - The x position of the battle info container
* @param y - The y position of the battle info container
* @param player - Whether this battle info belongs to a player or an enemy
* @param posParams - The parameters influencing the position of elements within the battle info container
*/
constructor(x: number, y: number, player: boolean, posParams: BattleInfoParamList) {
super(globalScene, x, y);
this.baseY = y;
this.player = player;
this.mini = !player;
this.boss = false;
this.offset = false;
this.lastName = null;
this.lastTeraType = PokemonType.UNKNOWN;
this.lastStatus = StatusEffect.NONE;
this.lastHp = -1;
this.lastMaxHp = -1;
this.lastHpFrame = null;
this.lastExp = -1;
this.lastLevelExp = -1;
this.lastLevel = -1;
this.baseLvContainerX = posParams.levelContainerX;
// Initially invisible and shown via Pokemon.showInfo
this.setVisible(false);
this.box = globalScene.add.sprite(0, 0, this.getTextureName()).setName("box").setOrigin(1, 0.5);
this.add(this.box);
this.nameText = addTextObject(player ? -115 : -124, player ? -15.2 : -11.2, "", TextStyle.BATTLE_INFO)
.setName("text_name")
.setOrigin(0);
this.add(this.nameText);
this.genderText = addTextObject(0, 0, "", TextStyle.BATTLE_INFO)
.setName("text_gender")
.setOrigin(0)
.setPositionRelative(this.nameText, 0, 2);
this.add(this.genderText);
this.constructIcons();
this.statusIndicator = globalScene.add
.sprite(0, 0, getLocalizedSpriteKey("statuses"))
.setName("icon_status")
.setVisible(false)
.setOrigin(0)
.setPositionRelative(this.nameText, 0, 11.5);
this.add(this.statusIndicator);
this.levelContainer = globalScene.add
.container(posParams.levelContainerX, posParams.levelContainerY)
.setName("container_level");
this.add(this.levelContainer);
const levelOverlay = globalScene.add.image(0, 0, "overlay_lv");
this.levelContainer.add(levelOverlay);
this.hpBar = globalScene.add.image(posParams.hpBarX, posParams.hpBarY, "overlay_hp").setName("hp_bar").setOrigin(0);
this.add(this.hpBar);
this.levelNumbersContainer = globalScene.add
.container(9.5, globalScene.uiTheme ? 0 : -0.5)
.setName("container_level");
this.levelContainer.add(this.levelNumbersContainer);
this.constructStatContainer(posParams.statBox);
this.constructTypeIcons();
}
getStatsValueContainer(): Phaser.GameObjects.Container {
return this.statValuesContainer;
}
//#region Initialization methods
initSplicedIcon(pokemon: Pokemon, baseWidth: number) {
this.splicedIcon.setPositionRelative(
this.nameText,
baseWidth + this.genderText.displayWidth + 1 + (this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0),
2.5,
);
this.splicedIcon.setVisible(pokemon.isFusion(true));
if (!this.splicedIcon.visible) {
return;
}
this.splicedIcon
.on("pointerover", () =>
globalScene.ui.showTooltip(
"",
`${pokemon.species.getName(pokemon.formIndex)}/${pokemon.fusionSpecies?.getName(pokemon.fusionFormIndex)}`,
),
)
.on("pointerout", () => globalScene.ui.hideTooltip());
}
/**
* Called by {@linkcode initInfo} to initialize the shiny icon
* @param pokemon - The pokemon object attached to this battle info
* @param baseXOffset - The x offset to use for the shiny icon
* @param doubleShiny - Whether the pokemon is shiny and its fusion species is also shiny
*/
protected initShinyIcon(pokemon: Pokemon, xOffset: number, doubleShiny: boolean) {
const baseVariant = !doubleShiny ? pokemon.getVariant(true) : pokemon.variant;
this.shinyIcon.setPositionRelative(
this.nameText,
xOffset +
this.genderText.displayWidth +
1 +
(this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0) +
(this.splicedIcon.visible ? this.splicedIcon.displayWidth + 1 : 0),
2.5,
);
this.shinyIcon
.setTexture(`shiny_star${doubleShiny ? "_1" : ""}`)
.setVisible(pokemon.isShiny())
.setTint(getVariantTint(baseVariant));
if (!this.shinyIcon.visible) {
return;
}
let shinyDescriptor = "";
if (doubleShiny || baseVariant) {
shinyDescriptor = " (" + getShinyDescriptor(baseVariant);
if (doubleShiny) {
shinyDescriptor += "/" + getShinyDescriptor(pokemon.fusionVariant);
}
shinyDescriptor += ")";
}
this.shinyIcon
.on("pointerover", () => globalScene.ui.showTooltip("", i18next.t("common:shinyOnHover") + shinyDescriptor))
.on("pointerout", () => globalScene.ui.hideTooltip());
}
initInfo(pokemon: Pokemon) {
this.updateNameText(pokemon);
const nameTextWidth = this.nameText.displayWidth;
this.name = pokemon.getNameToRender();
this.box.name = pokemon.getNameToRender();
this.genderText
.setText(getGenderSymbol(pokemon.gender))
.setColor(getGenderColor(pokemon.gender))
.setPositionRelative(this.nameText, nameTextWidth, 0);
this.lastTeraType = pokemon.getTeraType();
this.teraIcon
.setVisible(pokemon.isTerastallized)
.on("pointerover", () => {
if (pokemon.isTerastallized) {
globalScene.ui.showTooltip(
"",
i18next.t("fightUiHandler:teraHover", {
type: i18next.t(`pokemonInfo:Type.${PokemonType[this.lastTeraType]}`),
}),
);
}
})
.on("pointerout", () => globalScene.ui.hideTooltip())
.setPositionRelative(this.nameText, nameTextWidth + this.genderText.displayWidth + 1, 2);
const isFusion = pokemon.isFusion(true);
this.initSplicedIcon(pokemon, nameTextWidth);
const doubleShiny = isFusion && pokemon.shiny && pokemon.fusionShiny;
this.initShinyIcon(pokemon, nameTextWidth, doubleShiny);
this.fusionShinyIcon.setVisible(doubleShiny).copyPosition(this.shinyIcon);
if (isFusion) {
this.fusionShinyIcon.setTint(getVariantTint(pokemon.fusionVariant));
}
this.hpBar.setScale(pokemon.getHpRatio(true), 1);
this.lastHpFrame = this.hpBar.scaleX > 0.5 ? "high" : this.hpBar.scaleX > 0.25 ? "medium" : "low";
this.hpBar.setFrame(this.lastHpFrame);
this.lastHp = pokemon.hp;
this.lastMaxHp = pokemon.getMaxHp();
this.setLevel(pokemon.level);
this.lastLevel = pokemon.level;
this.shinyIcon.setVisible(pokemon.isShiny());
this.setTypes(pokemon.getTypes(true, false, undefined, true));
const stats = this.statOrder.map(() => 0);
this.lastStats = stats.join("");
this.updateStats(stats);
}
//#endregion
/**
* Return the texture name of the battle info box
*/
abstract getTextureName(): string;
setMini(_mini: boolean): void {}
toggleStats(visible: boolean): void {
globalScene.tweens.add({
targets: this.statsContainer,
duration: fixedInt(125),
ease: "Sine.easeInOut",
alpha: visible ? 1 : 0,
});
}
setOffset(offset: boolean): void {
if (this.offset === offset) {
return;
}
this.offset = offset;
this.x += 10 * (this.offset === this.player ? 1 : -1);
this.y += 27 * (this.offset ? 1 : -1);
this.baseY = this.y;
}
//#region Update methods and helpers
/**
* Update the status icon to match the pokemon's current status
* @param pokemon - The pokemon object attached to this battle info
* @param xOffset - The offset from the name text
*/
updateStatusIcon(pokemon: Pokemon, xOffset = 0) {
if (this.lastStatus !== (pokemon.status?.effect || StatusEffect.NONE)) {
this.lastStatus = pokemon.status?.effect || StatusEffect.NONE;
if (this.lastStatus !== StatusEffect.NONE) {
this.statusIndicator.setFrame(StatusEffect[this.lastStatus].toLowerCase());
}
this.statusIndicator.setVisible(!!this.lastStatus).setPositionRelative(this.nameText, xOffset, 11.5);
}
}
/** Update the pokemon name inside the container */
protected updateName(name: string): boolean {
if (this.lastName === name) {
return false;
}
this.nameText.setText(name).setPositionRelative(this.box, -this.nameText.displayWidth, 0);
this.lastName = name;
return true;
}
protected updateTeraType(ty: PokemonType): boolean {
if (this.lastTeraType === ty) {
return false;
}
this.teraIcon
.setVisible(ty !== PokemonType.UNKNOWN)
.setTintFill(Phaser.Display.Color.GetColor(...getTypeRgb(ty)))
.setPositionRelative(this.nameText, this.nameText.displayWidth + this.genderText.displayWidth + 1, 2);
this.lastTeraType = ty;
return true;
}
/**
* Update the type icons to match the pokemon's types
*/
setTypes(types: PokemonType[]): void {
this.type1Icon
.setTexture(`pbinfo_${this.player ? "player" : "enemy"}_type${types.length > 1 ? "1" : ""}`)
.setFrame(PokemonType[types[0]].toLowerCase());
this.type2Icon.setVisible(types.length > 1);
this.type3Icon.setVisible(types.length > 2);
if (types.length > 1) {
this.type2Icon.setFrame(PokemonType[types[1]].toLowerCase());
}
if (types.length > 2) {
this.type3Icon.setFrame(PokemonType[types[2]].toLowerCase());
}
}
/**
* Called by {@linkcode updateInfo} to update the position of the tera, spliced, and shiny icons
* @param isFusion - Whether the pokemon is a fusion or not
*/
protected updateIconDisplay(isFusion: boolean): void {
this.teraIcon.setPositionRelative(this.nameText, this.nameText.displayWidth + this.genderText.displayWidth + 1, 2);
this.splicedIcon
.setVisible(isFusion)
.setPositionRelative(
this.nameText,
this.nameText.displayWidth +
this.genderText.displayWidth +
1 +
(this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0),
1.5,
);
this.shinyIcon.setPositionRelative(
this.nameText,
this.nameText.displayWidth +
this.genderText.displayWidth +
1 +
(this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0) +
(this.splicedIcon.visible ? this.splicedIcon.displayWidth + 1 : 0),
2.5,
);
}
//#region Hp Bar Display handling
/**
* Called every time the hp frame is updated by the tween
* @param pokemon - The pokemon object attached to this battle info
*/
protected updateHpFrame(): void {
const hpFrame = this.hpBar.scaleX > 0.5 ? "high" : this.hpBar.scaleX > 0.25 ? "medium" : "low";
if (hpFrame !== this.lastHpFrame) {
this.hpBar.setFrame(hpFrame);
this.lastHpFrame = hpFrame;
}
}
/**
* Called by every frame in the hp animation tween created in {@linkcode updatePokemonHp}
* @param _pokemon - The pokemon the battle-info bar belongs to
*/
protected onHpTweenUpdate(_pokemon: Pokemon): void {
this.updateHpFrame();
}
/** Update the pokemonHp bar */
protected updatePokemonHp(pokemon: Pokemon, resolve: (r: void | PromiseLike<void>) => void, instant?: boolean): void {
let duration = !instant ? Phaser.Math.Clamp(Math.abs(this.lastHp - pokemon.hp) * 5, 250, 5000) : 0;
const speed = globalScene.hpBarSpeed;
if (speed) {
duration = speed >= 3 ? 0 : duration / Math.pow(2, speed);
}
globalScene.tweens.add({
targets: this.hpBar,
ease: "Sine.easeOut",
scaleX: pokemon.getHpRatio(true),
duration: duration,
onUpdate: () => {
this.onHpTweenUpdate(pokemon);
},
onComplete: () => {
this.updateHpFrame();
resolve();
},
});
this.lastMaxHp = pokemon.getMaxHp();
}
//#endregion
async updateInfo(pokemon: Pokemon, instant?: boolean): Promise<void> {
let resolve: (r: void | PromiseLike<void>) => void = () => {};
const promise = new Promise<void>(r => (resolve = r));
if (!globalScene) {
return resolve();
}
const gender: Gender = pokemon.summonData?.illusion?.gender ?? pokemon.gender;
this.genderText.setText(getGenderSymbol(gender)).setColor(getGenderColor(gender));
const nameUpdated = this.updateName(pokemon.getNameToRender());
const teraTypeUpdated = this.updateTeraType(pokemon.isTerastallized ? pokemon.getTeraType() : PokemonType.UNKNOWN);
const isFusion = pokemon.isFusion(true);
if (nameUpdated || teraTypeUpdated) {
this.updateIconDisplay(isFusion);
}
this.updateStatusIcon(pokemon);
if (this.lastHp !== pokemon.hp || this.lastMaxHp !== pokemon.getMaxHp()) {
return this.updatePokemonHp(pokemon, resolve, instant);
}
if (!this.player && this.lastLevel !== pokemon.level) {
this.setLevel(pokemon.level);
this.lastLevel = pokemon.level;
}
const stats = pokemon.getStatStages();
const statsStr = stats.join("");
if (this.lastStats !== statsStr) {
this.updateStats(stats);
this.lastStats = statsStr;
}
this.shinyIcon.setVisible(pokemon.isShiny(true));
const doubleShiny = isFusion && pokemon.shiny && pokemon.fusionShiny;
const baseVariant = !doubleShiny ? pokemon.getVariant(true) : pokemon.variant;
this.shinyIcon.setTint(getVariantTint(baseVariant));
this.fusionShinyIcon.setVisible(doubleShiny).setPosition(this.shinyIcon.x, this.shinyIcon.y);
if (isFusion) {
this.fusionShinyIcon.setTint(getVariantTint(pokemon.fusionVariant));
}
resolve();
await promise;
}
//#endregion
updateNameText(pokemon: Pokemon): void {
let displayName = pokemon.getNameToRender().replace(/[♂♀]/g, "");
let nameTextWidth: number;
const nameSizeTest = addTextObject(0, 0, displayName, TextStyle.BATTLE_INFO);
nameTextWidth = nameSizeTest.displayWidth;
const gender = pokemon.summonData.illusion?.gender ?? pokemon.gender;
while (
nameTextWidth >
(this.player || !this.boss ? 60 : 98) -
((gender !== Gender.GENDERLESS ? 6 : 0) +
(pokemon.fusionSpecies ? 8 : 0) +
(pokemon.isShiny() ? 8 : 0) +
(Math.min(pokemon.level.toString().length, 3) - 3) * 8)
) {
displayName = `${displayName.slice(0, displayName.endsWith(".") ? -2 : -1).trimEnd()}.`;
nameSizeTest.setText(displayName);
nameTextWidth = nameSizeTest.displayWidth;
}
nameSizeTest.destroy();
this.nameText.setText(displayName);
this.lastName = pokemon.getNameToRender();
if (this.nameText.visible) {
this.nameText.setInteractive(
new Phaser.Geom.Rectangle(0, 0, this.nameText.width, this.nameText.height),
Phaser.Geom.Rectangle.Contains,
);
}
}
/**
* Set the level numbers container to display the provided level
*
* @remarks
* The numbers in the pokemon's level uses images for each number rather than a text object with a special font.
* This method sets the images for each digit of the level number and then positions the level container based
* on the number of digits.
*
* @param level - The level to display
* @param textureKey - The texture key for the level numbers
*/
setLevel(level: number, textureKey: "numbers" | "numbers_red" = "numbers"): void {
this.levelNumbersContainer.removeAll(true);
const levelStr = level.toString();
for (let i = 0; i < levelStr.length; i++) {
this.levelNumbersContainer.add(globalScene.add.image(i * 8, 0, textureKey, levelStr[i]));
}
this.levelContainer.setX(this.baseLvContainerX - 8 * Math.max(levelStr.length - 3, 0));
}
updateStats(stats: number[]): void {
for (const [i, s] of this.statOrder.entries()) {
if (s !== Stat.HP) {
this.statNumbers[i].setFrame(stats[s - 1].toString());
}
}
}
getBaseY(): number {
return this.baseY;
}
resetY(): void {
this.y = this.baseY;
}
}

View File

@ -0,0 +1,235 @@
import { globalScene } from "#app/global-scene";
import BattleFlyout from "../battle-flyout";
import { addTextObject, TextStyle } from "#app/ui/text";
import { addWindow, WindowVariant } from "#app/ui/ui-theme";
import { Stat } from "#enums/stat";
import i18next from "i18next";
import type { EnemyPokemon } from "#app/field/pokemon";
import type { GameObjects } from "phaser";
import BattleInfo from "./battle-info";
import type { BattleInfoParamList } from "./battle-info";
export class EnemyBattleInfo extends BattleInfo {
protected player: false = false;
protected championRibbon: Phaser.GameObjects.Sprite;
protected ownedIcon: Phaser.GameObjects.Sprite;
protected flyoutMenu: BattleFlyout;
protected hpBarSegmentDividers: GameObjects.Rectangle[] = [];
// #region Type effectiveness hint objects
protected effectivenessContainer: Phaser.GameObjects.Container;
protected effectivenessWindow: Phaser.GameObjects.NineSlice;
protected effectivenessText: Phaser.GameObjects.Text;
protected currentEffectiveness?: string;
// #endregion
override get statOrder(): Stat[] {
return [Stat.HP, Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.ACC, Stat.EVA, Stat.SPD];
}
override getTextureName(): string {
return this.boss ? "pbinfo_enemy_boss_mini" : "pbinfo_enemy_mini";
}
override constructTypeIcons(): void {
this.type1Icon = globalScene.add.sprite(-15, -15.5, "pbinfo_enemy_type1").setName("icon_type_1").setOrigin(0);
this.type2Icon = globalScene.add.sprite(-15, -2.5, "pbinfo_enemy_type2").setName("icon_type_2").setOrigin(0);
this.type3Icon = globalScene.add.sprite(0, 15.5, "pbinfo_enemy_type3").setName("icon_type_3").setOrigin(0);
this.add([this.type1Icon, this.type2Icon, this.type3Icon]);
}
constructor() {
const posParams: BattleInfoParamList = {
nameTextX: -124,
nameTextY: -11.2,
levelContainerX: -50,
levelContainerY: -5,
hpBarX: -71,
hpBarY: 4.5,
statBox: {
xOffset: 5,
paddingX: 2,
statOverflow: 0,
},
};
super(140, -141, false, posParams);
this.ownedIcon = globalScene.add
.sprite(0, 0, "icon_owned")
.setName("icon_owned")
.setVisible(false)
.setOrigin(0, 0)
.setPositionRelative(this.nameText, 0, 11.75);
this.championRibbon = globalScene.add
.sprite(0, 0, "champion_ribbon")
.setName("icon_champion_ribbon")
.setVisible(false)
.setOrigin(0, 0)
.setPositionRelative(this.nameText, 8, 11.75);
// Ensure these two icons are positioned below the stats container
this.addAt([this.ownedIcon, this.championRibbon], this.getIndex(this.statsContainer));
this.flyoutMenu = new BattleFlyout(this.player);
this.add(this.flyoutMenu);
this.moveBelow<Phaser.GameObjects.GameObject>(this.flyoutMenu, this.box);
this.effectivenessContainer = globalScene.add
.container(0, 0)
.setVisible(false)
.setPositionRelative(this.type1Icon, 22, 4);
this.add(this.effectivenessContainer);
this.effectivenessText = addTextObject(5, 4.5, "", TextStyle.BATTLE_INFO);
this.effectivenessWindow = addWindow(0, 0, 0, 20, undefined, false, undefined, undefined, WindowVariant.XTHIN);
this.effectivenessContainer.add([this.effectivenessWindow, this.effectivenessText]);
}
override initInfo(pokemon: EnemyPokemon): void {
this.flyoutMenu.initInfo(pokemon);
super.initInfo(pokemon);
if (this.nameText.visible) {
this.nameText
.on("pointerover", () =>
globalScene.ui.showTooltip(
"",
i18next.t("battleInfo:generation", {
generation: i18next.t(`starterSelectUiHandler:gen${pokemon.species.generation}`),
}),
),
)
.on("pointerout", () => globalScene.ui.hideTooltip());
}
const dexEntry = globalScene.gameData.dexData[pokemon.species.speciesId];
this.ownedIcon.setVisible(!!dexEntry.caughtAttr);
const opponentPokemonDexAttr = pokemon.getDexAttr();
if (
globalScene.gameMode.isClassic &&
globalScene.gameData.starterData[pokemon.species.getRootSpeciesId()].classicWinCount > 0 &&
globalScene.gameData.starterData[pokemon.species.getRootSpeciesId(true)].classicWinCount > 0
) {
this.championRibbon.setVisible(true);
}
// Check if Player owns all genders and forms of the Pokemon
const missingDexAttrs = (dexEntry.caughtAttr & opponentPokemonDexAttr) < opponentPokemonDexAttr;
const ownedAbilityAttrs = globalScene.gameData.starterData[pokemon.species.getRootSpeciesId()].abilityAttr;
// Check if the player owns ability for the root form
const playerOwnsThisAbility = pokemon.checkIfPlayerHasAbilityOfStarter(ownedAbilityAttrs);
if (missingDexAttrs || !playerOwnsThisAbility) {
this.ownedIcon.setTint(0x808080);
}
if (this.boss) {
this.updateBossSegmentDividers(pokemon as EnemyPokemon);
}
}
/**
* Show or hide the type effectiveness multiplier window
* Passing undefined will hide the window
*/
updateEffectiveness(effectiveness?: string) {
this.currentEffectiveness = effectiveness;
if (!globalScene.typeHints || effectiveness === undefined || this.flyoutMenu.flyoutVisible) {
this.effectivenessContainer.setVisible(false);
return;
}
this.effectivenessText.setText(effectiveness);
this.effectivenessWindow.width = 10 + this.effectivenessText.displayWidth;
this.effectivenessContainer.setVisible(true);
}
/**
* Request the flyoutMenu to toggle if available and hides or shows the effectiveness window where necessary
*/
toggleFlyout(visible: boolean): void {
this.flyoutMenu.toggleFlyout(visible);
if (visible) {
this.effectivenessContainer.setVisible(false);
} else {
this.updateEffectiveness(this.currentEffectiveness);
}
}
updateBossSegments(pokemon: EnemyPokemon): void {
const boss = !!pokemon.bossSegments;
if (boss !== this.boss) {
this.boss = boss;
[
this.nameText,
this.genderText,
this.teraIcon,
this.splicedIcon,
this.shinyIcon,
this.ownedIcon,
this.championRibbon,
this.statusIndicator,
this.levelContainer,
this.statValuesContainer,
].map(e => (e.x += 48 * (boss ? -1 : 1)));
this.hpBar.x += 38 * (boss ? -1 : 1);
this.hpBar.y += 2 * (this.boss ? -1 : 1);
this.hpBar.setTexture(`overlay_hp${boss ? "_boss" : ""}`);
this.box.setTexture(this.getTextureName());
this.statsBox.setTexture(`${this.getTextureName()}_stats`);
}
this.bossSegments = boss ? pokemon.bossSegments : 0;
this.updateBossSegmentDividers(pokemon);
}
updateBossSegmentDividers(pokemon: EnemyPokemon): void {
while (this.hpBarSegmentDividers.length) {
this.hpBarSegmentDividers.pop()?.destroy();
}
if (this.boss && this.bossSegments > 1) {
const uiTheme = globalScene.uiTheme;
const maxHp = pokemon.getMaxHp();
for (let s = 1; s < this.bossSegments; s++) {
const dividerX = (Math.round((maxHp / this.bossSegments) * s) / maxHp) * this.hpBar.width;
const divider = globalScene.add.rectangle(
0,
0,
1,
this.hpBar.height - (uiTheme ? 0 : 1),
pokemon.bossSegmentIndex >= s ? 0xffffff : 0x404040,
);
divider.setOrigin(0.5, 0).setName("hpBar_divider_" + s.toString());
this.add(divider);
this.moveBelow(divider as Phaser.GameObjects.GameObject, this.statsContainer);
divider.setPositionRelative(this.hpBar, dividerX, uiTheme ? 0 : 1);
this.hpBarSegmentDividers.push(divider);
}
}
}
override updateStatusIcon(pokemon: EnemyPokemon): void {
super.updateStatusIcon(pokemon, (this.ownedIcon.visible ? 8 : 0) + (this.championRibbon.visible ? 8 : 0));
}
protected override updatePokemonHp(
pokemon: EnemyPokemon,
resolve: (r: void | PromiseLike<void>) => void,
instant?: boolean,
): void {
super.updatePokemonHp(pokemon, resolve, instant);
this.lastHp = pokemon.hp;
}
}

View File

@ -0,0 +1,242 @@
import { getLevelRelExp, getLevelTotalExp } from "#app/data/exp";
import type { PlayerPokemon } from "#app/field/pokemon";
import { globalScene } from "#app/global-scene";
import { ExpGainsSpeed } from "#enums/exp-gains-speed";
import { Stat } from "#enums/stat";
import BattleInfo from "./battle-info";
import type { BattleInfoParamList } from "./battle-info";
export class PlayerBattleInfo extends BattleInfo {
protected player: true = true;
protected hpNumbersContainer: Phaser.GameObjects.Container;
override get statOrder(): Stat[] {
return [Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.ACC, Stat.EVA, Stat.SPD];
}
override getTextureName(): string {
return this.mini ? "pbinfo_player_mini" : "pbinfo_player";
}
override constructTypeIcons(): void {
this.type1Icon = globalScene.add.sprite(-139, -17, "pbinfo_player_type1").setName("icon_type_1").setOrigin(0);
this.type2Icon = globalScene.add.sprite(-139, -1, "pbinfo_player_type2").setName("icon_type_2").setOrigin(0);
this.type3Icon = globalScene.add.sprite(-154, -17, "pbinfo_player_type3").setName("icon_type_3").setOrigin(0);
this.add([this.type1Icon, this.type2Icon, this.type3Icon]);
}
constructor() {
const posParams: BattleInfoParamList = {
nameTextX: -115,
nameTextY: -15.2,
levelContainerX: -41,
levelContainerY: -10,
hpBarX: -61,
hpBarY: -1,
statBox: {
xOffset: 8,
paddingX: 4,
statOverflow: 1,
},
};
super(Math.floor(globalScene.game.canvas.width / 6) - 10, -72, true, posParams);
this.hpNumbersContainer = globalScene.add.container(-15, 10).setName("container_hp");
// hp number container must be beneath the stat container for overlay to display properly
this.addAt(this.hpNumbersContainer, this.getIndex(this.statsContainer));
const expBar = globalScene.add.image(-98, 18, "overlay_exp").setName("overlay_exp").setOrigin(0);
this.add(expBar);
const expMaskRect = globalScene.make
.graphics({})
.setScale(6)
.fillStyle(0xffffff)
.beginPath()
.fillRect(127, 126, 85, 2);
const expMask = expMaskRect.createGeometryMask();
expBar.setMask(expMask);
this.expBar = expBar;
this.expMaskRect = expMaskRect;
}
override initInfo(pokemon: PlayerPokemon): void {
super.initInfo(pokemon);
this.setHpNumbers(pokemon.hp, pokemon.getMaxHp());
this.expMaskRect.x = (pokemon.levelExp / getLevelTotalExp(pokemon.level, pokemon.species.growthRate)) * 510;
this.lastExp = pokemon.exp;
this.lastLevelExp = pokemon.levelExp;
this.statValuesContainer.setPosition(8, 7);
}
override setMini(mini: boolean): void {
if (this.mini === mini) {
return;
}
this.mini = mini;
this.box.setTexture(this.getTextureName());
this.statsBox.setTexture(`${this.getTextureName()}_stats`);
if (this.player) {
this.y -= 12 * (mini ? 1 : -1);
this.baseY = this.y;
}
const offsetElements = [
this.nameText,
this.genderText,
this.teraIcon,
this.splicedIcon,
this.shinyIcon,
this.statusIndicator,
this.levelContainer,
];
offsetElements.forEach(el => (el.y += 1.5 * (mini ? -1 : 1)));
[this.type1Icon, this.type2Icon, this.type3Icon].forEach(el => {
el.x += 4 * (mini ? 1 : -1);
el.y += -8 * (mini ? 1 : -1);
});
this.statValuesContainer.x += 2 * (mini ? 1 : -1);
this.statValuesContainer.y += -7 * (mini ? 1 : -1);
const toggledElements = [this.hpNumbersContainer, this.expBar];
toggledElements.forEach(el => el.setVisible(!mini));
}
/**
* Updates the Hp Number text (that is the "HP/Max HP" text that appears below the player's health bar)
* while the health bar is tweening.
* @param pokemon - The Pokemon the health bar belongs to.
*/
protected override onHpTweenUpdate(pokemon: PlayerPokemon): void {
const tweenHp = Math.ceil(this.hpBar.scaleX * pokemon.getMaxHp());
this.setHpNumbers(tweenHp, pokemon.getMaxHp());
this.lastHp = tweenHp;
this.updateHpFrame();
}
updatePokemonExp(pokemon: PlayerPokemon, instant?: boolean, levelDurationMultiplier = 1): Promise<void> {
const levelUp = this.lastLevel < pokemon.level;
const relLevelExp = getLevelRelExp(this.lastLevel + 1, pokemon.species.growthRate);
const levelExp = levelUp ? relLevelExp : pokemon.levelExp;
let ratio = relLevelExp ? levelExp / relLevelExp : 0;
if (this.lastLevel >= globalScene.getMaxExpLevel(true)) {
ratio = levelUp ? 1 : 0;
instant = true;
}
const durationMultiplier = Phaser.Tweens.Builders.GetEaseFunction("Sine.easeIn")(
1 - Math.max(this.lastLevel - 100, 0) / 150,
);
let duration =
this.visible && !instant
? ((levelExp - this.lastLevelExp) / relLevelExp) *
BattleInfo.EXP_GAINS_DURATION_BASE *
durationMultiplier *
levelDurationMultiplier
: 0;
const speed = globalScene.expGainsSpeed;
if (speed && speed >= ExpGainsSpeed.DEFAULT) {
duration = speed >= ExpGainsSpeed.SKIP ? ExpGainsSpeed.DEFAULT : duration / Math.pow(2, speed);
}
if (ratio === 1) {
this.lastLevelExp = 0;
this.lastLevel++;
} else {
this.lastExp = pokemon.exp;
this.lastLevelExp = pokemon.levelExp;
}
if (duration) {
globalScene.playSound("se/exp");
}
return new Promise(resolve => {
globalScene.tweens.add({
targets: this.expMaskRect,
ease: "Sine.easeIn",
x: ratio * 510,
duration: duration,
onComplete: () => {
if (!globalScene) {
return resolve();
}
if (duration) {
globalScene.sound.stopByKey("se/exp");
}
if (ratio === 1) {
globalScene.playSound("se/level_up");
this.setLevel(this.lastLevel);
globalScene.time.delayedCall(500 * levelDurationMultiplier, () => {
this.expMaskRect.x = 0;
this.updateInfo(pokemon, instant).then(() => resolve());
});
return;
}
resolve();
},
});
});
}
/**
* Updates the info on the info bar.
*
* In addition to performing all the steps of {@linkcode BattleInfo.updateInfo},
* it also updates the EXP Bar
*/
override async updateInfo(pokemon: PlayerPokemon, instant?: boolean): Promise<void> {
await super.updateInfo(pokemon, instant);
const isLevelCapped = pokemon.level >= globalScene.getMaxExpLevel();
const oldLevelCapped = this.lastLevelCapped;
this.lastLevelCapped = isLevelCapped;
if (this.lastExp !== pokemon.exp || this.lastLevel !== pokemon.level) {
const durationMultipler = Math.max(
Phaser.Tweens.Builders.GetEaseFunction("Cubic.easeIn")(1 - Math.min(pokemon.level - this.lastLevel, 10) / 10),
0.1,
);
await this.updatePokemonExp(pokemon, false, durationMultipler);
} else if (isLevelCapped !== oldLevelCapped) {
this.setLevel(pokemon.level);
}
}
/**
* Set the HP numbers text, that is the "HP/Max HP" text that appears below the player's health bar.
* @param hp - The current HP of the player.
* @param maxHp - The maximum HP of the player.
*/
setHpNumbers(hp: number, maxHp: number): void {
if (!globalScene) {
return;
}
this.hpNumbersContainer.removeAll(true);
const hpStr = hp.toString();
const maxHpStr = maxHp.toString();
let offset = 0;
for (let i = maxHpStr.length - 1; i >= 0; i--) {
this.hpNumbersContainer.add(globalScene.add.image(offset++ * -8, 0, "numbers", maxHpStr[i]));
}
this.hpNumbersContainer.add(globalScene.add.image(offset++ * -8, 0, "numbers", "/"));
for (let i = hpStr.length - 1; i >= 0; i--) {
this.hpNumbersContainer.add(globalScene.add.image(offset++ * -8, 0, "numbers", hpStr[i]));
}
}
/**
* Set the level numbers container to display the provided level
*
* Overrides the default implementation to handle displaying level capped numbers in red.
* @param level - The level to display
*/
override setLevel(level: number): void {
super.setLevel(level, level >= globalScene.getMaxExpLevel() ? "numbers_red" : "numbers");
}
}

View File

@ -10,7 +10,7 @@ import { getLocalizedSpriteKey, fixedInt, padInt } from "#app/utils/common";
import { MoveCategory } from "#enums/MoveCategory"; import { MoveCategory } from "#enums/MoveCategory";
import i18next from "i18next"; import i18next from "i18next";
import { Button } from "#enums/buttons"; import { Button } from "#enums/buttons";
import type { PokemonMove } from "#app/field/pokemon"; import type { EnemyPokemon, PokemonMove } from "#app/field/pokemon";
import type Pokemon from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon";
import type { CommandPhase } from "#app/phases/command-phase"; import type { CommandPhase } from "#app/phases/command-phase";
import MoveInfoOverlay from "./move-info-overlay"; import MoveInfoOverlay from "./move-info-overlay";
@ -279,7 +279,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle {
this.moveInfoOverlay.show(pokemonMove.getMove()); this.moveInfoOverlay.show(pokemonMove.getMove());
pokemon.getOpponents().forEach(opponent => { pokemon.getOpponents().forEach(opponent => {
opponent.updateEffectiveness(this.getEffectivenessText(pokemon, opponent, pokemonMove)); (opponent as EnemyPokemon).updateEffectiveness(this.getEffectivenessText(pokemon, opponent, pokemonMove));
}); });
} }
@ -391,7 +391,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle {
const opponents = (globalScene.getCurrentPhase() as CommandPhase).getPokemon().getOpponents(); const opponents = (globalScene.getCurrentPhase() as CommandPhase).getPokemon().getOpponents();
opponents.forEach(opponent => { opponents.forEach(opponent => {
opponent.updateEffectiveness(undefined); (opponent as EnemyPokemon).updateEffectiveness();
}); });
} }

View File

@ -8,7 +8,7 @@ import type Pokemon from "../field/pokemon";
import i18next from "i18next"; import i18next from "i18next";
import type { DexEntry, StarterDataEntry } from "../system/game-data"; import type { DexEntry, StarterDataEntry } from "../system/game-data";
import { DexAttr } from "../system/game-data"; import { DexAttr } from "../system/game-data";
import { fixedInt } from "#app/utils/common"; import { fixedInt, getShinyDescriptor } from "#app/utils/common";
import ConfirmUiHandler from "./confirm-ui-handler"; import ConfirmUiHandler from "./confirm-ui-handler";
import { StatsContainer } from "./stats-container"; import { StatsContainer } from "./stats-container";
import { TextStyle, addBBCodeTextObject, addTextObject, getTextColor } from "./text"; import { TextStyle, addBBCodeTextObject, addTextObject, getTextColor } from "./text";
@ -343,18 +343,19 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
this.pokemonShinyIcon.setVisible(pokemon.isShiny()); this.pokemonShinyIcon.setVisible(pokemon.isShiny());
this.pokemonShinyIcon.setTint(getVariantTint(baseVariant)); this.pokemonShinyIcon.setTint(getVariantTint(baseVariant));
if (this.pokemonShinyIcon.visible) { if (this.pokemonShinyIcon.visible) {
const shinyDescriptor = let shinyDescriptor = "";
doubleShiny || baseVariant if (doubleShiny || baseVariant) {
? `${baseVariant === 2 ? i18next.t("common:epicShiny") : baseVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}${doubleShiny ? `/${pokemon.fusionVariant === 2 ? i18next.t("common:epicShiny") : pokemon.fusionVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}` : ""}` shinyDescriptor = " (" + getShinyDescriptor(baseVariant);
: ""; if (doubleShiny) {
this.pokemonShinyIcon.on("pointerover", () => shinyDescriptor += "/" + getShinyDescriptor(pokemon.fusionVariant);
globalScene.ui.showTooltip( }
"", shinyDescriptor += ")";
`${i18next.t("common:shinyOnHover")}${shinyDescriptor ? ` (${shinyDescriptor})` : ""}`, }
true, this.pokemonShinyIcon
), .on("pointerover", () =>
); globalScene.ui.showTooltip("", i18next.t("common:shinyOnHover") + shinyDescriptor, true),
this.pokemonShinyIcon.on("pointerout", () => globalScene.ui.hideTooltip()); )
.on("pointerout", () => globalScene.ui.hideTooltip());
const newShiny = BigInt(1 << (pokemon.shiny ? 1 : 0)); const newShiny = BigInt(1 << (pokemon.shiny ? 1 : 0));
const newVariant = BigInt(1 << (pokemon.variant + 4)); const newVariant = BigInt(1 << (pokemon.variant + 4));

View File

@ -11,6 +11,7 @@ import {
isNullOrUndefined, isNullOrUndefined,
toReadableString, toReadableString,
formatStat, formatStat,
getShinyDescriptor,
} from "#app/utils/common"; } from "#app/utils/common";
import type { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import type { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
import { getStarterValueFriendshipCap, speciesStarterCosts } from "#app/data/balance/starters"; import { getStarterValueFriendshipCap, speciesStarterCosts } from "#app/data/balance/starters";
@ -444,18 +445,19 @@ export default class SummaryUiHandler extends UiHandler {
this.shinyIcon.setVisible(this.pokemon.isShiny(false)); this.shinyIcon.setVisible(this.pokemon.isShiny(false));
this.shinyIcon.setTint(getVariantTint(baseVariant)); this.shinyIcon.setTint(getVariantTint(baseVariant));
if (this.shinyIcon.visible) { if (this.shinyIcon.visible) {
const shinyDescriptor = let shinyDescriptor = "";
doubleShiny || baseVariant if (doubleShiny || baseVariant) {
? `${baseVariant === 2 ? i18next.t("common:epicShiny") : baseVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}${doubleShiny ? `/${this.pokemon.fusionVariant === 2 ? i18next.t("common:epicShiny") : this.pokemon.fusionVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}` : ""}` shinyDescriptor = " (" + getShinyDescriptor(baseVariant);
: ""; if (doubleShiny) {
this.shinyIcon.on("pointerover", () => shinyDescriptor += "/" + getShinyDescriptor(this.pokemon.fusionVariant);
globalScene.ui.showTooltip( }
"", shinyDescriptor += ")";
`${i18next.t("common:shinyOnHover")}${shinyDescriptor ? ` (${shinyDescriptor})` : ""}`, }
true, this.shinyIcon
), .on("pointerover", () =>
); globalScene.ui.showTooltip("", i18next.t("common:shinyOnHover") + shinyDescriptor, true),
this.shinyIcon.on("pointerout", () => globalScene.ui.hideTooltip()); )
.on("pointerout", () => globalScene.ui.hideTooltip());
} }
this.fusionShinyIcon.setPosition(this.shinyIcon.x, this.shinyIcon.y); this.fusionShinyIcon.setPosition(this.shinyIcon.x, this.shinyIcon.y);

View File

@ -2,6 +2,7 @@ import { MoneyFormat } from "#enums/money-format";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import i18next from "i18next"; import i18next from "i18next";
import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; import { pokerogueApi } from "#app/plugins/api/pokerogue-api";
import type { Variant } from "#app/sprites/variant";
export type nil = null | undefined; export type nil = null | undefined;
@ -576,3 +577,18 @@ export function animationFileName(move: Moves): string {
export function camelCaseToKebabCase(str: string): string { export function camelCaseToKebabCase(str: string): string {
return str.replace(/[A-Z]+(?![a-z])|[A-Z]/g, (s, o) => (o ? "-" : "") + s.toLowerCase()); return str.replace(/[A-Z]+(?![a-z])|[A-Z]/g, (s, o) => (o ? "-" : "") + s.toLowerCase());
} }
/** Get the localized shiny descriptor for the provided variant
* @param variant - The variant to get the shiny descriptor for
* @returns The localized shiny descriptor
*/
export function getShinyDescriptor(variant: Variant): string {
switch (variant) {
case 2:
return i18next.t("common:epicShiny");
case 1:
return i18next.t("common:rareShiny");
case 0:
return i18next.t("common:commonShiny");
}
}

View File

@ -1,3 +1,4 @@
export interface MockGameObject { export interface MockGameObject {
name: string; name: string;
destroy?(): void;
} }

View File

@ -2,199 +2,253 @@ import type MockTextureManager from "#test/testUtils/mocks/mockTextureManager";
import type { MockGameObject } from "../mockGameObject"; import type { MockGameObject } from "../mockGameObject";
export default class MockContainer implements MockGameObject { export default class MockContainer implements MockGameObject {
protected x; protected x: number;
protected y; protected y: number;
protected scene; protected scene;
protected width; protected width: number;
protected height; protected height: number;
protected visible; protected visible: boolean;
private alpha; private alpha: number;
private style; private style;
public frame; public frame;
protected textureManager; protected textureManager;
public list: MockGameObject[] = []; public list: MockGameObject[] = [];
public name: string; public name: string;
constructor(textureManager: MockTextureManager, x, y) { constructor(textureManager: MockTextureManager, x: number, y: number) {
this.x = x; this.x = x;
this.y = y; this.y = y;
this.frame = {}; this.frame = {};
this.textureManager = textureManager; this.textureManager = textureManager;
} }
setVisible(visible) { setVisible(visible: boolean): this {
this.visible = visible; this.visible = visible;
return this;
} }
once(_event, _callback, _source) {} once(_event, _callback, _source): this {
return this;
}
off(_event, _callback, _source) {} off(_event, _callback, _source): this {
return this;
}
removeFromDisplayList() { removeFromDisplayList(): this {
// same as remove or destroy // same as remove or destroy
return this;
} }
removeBetween(_startIndex, _endIndex, _destroyChild) { removeBetween(_startIndex, _endIndex, _destroyChild): this {
// Removes multiple children across an index range // Removes multiple children across an index range
return this;
} }
addedToScene() { addedToScene() {
// This callback is invoked when this Game Object is added to a Scene. // This callback is invoked when this Game Object is added to a Scene.
} }
setSize(_width, _height) { setSize(_width: number, _height: number): this {
// Sets the size of this Game Object. // Sets the size of this Game Object.
return this;
} }
setMask() { setMask(): this {
/// Sets the mask that this Game Object will use to render with. /// Sets the mask that this Game Object will use to render with.
return this;
} }
setPositionRelative(_source, _x, _y) { setPositionRelative(_source, _x, _y): this {
/// Sets the position of this Game Object to be a relative position from the source Game Object. /// Sets the position of this Game Object to be a relative position from the source Game Object.
return this;
} }
setInteractive = () => null; setInteractive(): this {
return this;
}
setOrigin(x, y) { setOrigin(x = 0.5, y = x): this {
this.x = x; this.x = x;
this.y = y; this.y = y;
return this;
} }
setAlpha(alpha) { setAlpha(alpha = 1): this {
this.alpha = alpha; this.alpha = alpha;
return this;
} }
setFrame(_frame, _updateSize?: boolean, _updateOrigin?: boolean) { setFrame(_frame, _updateSize?: boolean, _updateOrigin?: boolean): this {
// Sets the frame this Game Object will use to render with. // Sets the frame this Game Object will use to render with.
return this;
} }
setScale(_scale) { setScale(_x = 1, _y = _x): this {
// Sets the scale of this Game Object. // Sets the scale of this Game Object.
return this;
} }
setPosition(x, y) { setPosition(x = 0, y = x, _z = 0, _w = 0): this {
this.x = x; this.x = x;
this.y = y; this.y = y;
return this;
} }
setX(x) { setX(x = 0): this {
this.x = x; this.x = x;
return this;
} }
setY(y) { setY(y = 0): this {
this.y = y; this.y = y;
return this;
} }
destroy() { destroy() {
this.list = []; this.list = [];
} }
setShadow(_shadowXpos, _shadowYpos, _shadowColor) { setShadow(_shadowXpos, _shadowYpos, _shadowColor): this {
// Sets the shadow settings for this Game Object. // Sets the shadow settings for this Game Object.
return this;
} }
setLineSpacing(_lineSpacing) { setLineSpacing(_lineSpacing): this {
// Sets the line spacing value of this Game Object. // Sets the line spacing value of this Game Object.
return this;
} }
setText(_text) { setText(_text): this {
// Sets the text this Game Object will display. // Sets the text this Game Object will display.
return this;
} }
setAngle(_angle) { setAngle(_angle): this {
// Sets the angle of this Game Object. // Sets the angle of this Game Object.
return this;
} }
setShadowOffset(_offsetX, _offsetY) { setShadowOffset(_offsetX, _offsetY): this {
// Sets the shadow offset values. // Sets the shadow offset values.
return this;
} }
setWordWrapWidth(_width) { setWordWrapWidth(_width) {
// Sets the width (in pixels) to use for wrapping lines. // Sets the width (in pixels) to use for wrapping lines.
} }
setFontSize(_fontSize) { setFontSize(_fontSize): this {
// Sets the font size of this Game Object. // Sets the font size of this Game Object.
return this;
} }
getBounds() { getBounds() {
return { width: this.width, height: this.height }; return { width: this.width, height: this.height };
} }
setColor(_color) { setColor(_color): this {
// Sets the tint of this Game Object. // Sets the tint of this Game Object.
return this;
} }
setShadowColor(_color) { setShadowColor(_color): this {
// Sets the shadow color. // Sets the shadow color.
return this;
} }
setTint(_color) { setTint(_color: this) {
// Sets the tint of this Game Object. // Sets the tint of this Game Object.
return this;
} }
setStrokeStyle(_thickness, _color) { setStrokeStyle(_thickness, _color): this {
// Sets the stroke style for the graphics. // Sets the stroke style for the graphics.
return this; return this;
} }
setDepth(_depth) { setDepth(_depth): this {
// Sets the depth of this Game Object. // Sets the depth of this Game Object.\
return this;
} }
setTexture(_texture) { setTexture(_texture): this {
// Sets the texture this Game Object will use to render with. // Sets the texture this Game Object will use to render with.\
return this;
} }
clearTint() { clearTint(): this {
// Clears any previously set tint. // Clears any previously set tint.\
return this;
} }
sendToBack() { sendToBack(): this {
// Sends this Game Object to the back of its parent's display list. // Sends this Game Object to the back of its parent's display list.\
return this;
} }
moveTo(_obj) { moveTo(_obj): this {
// Moves this Game Object to the given index in the list. // Moves this Game Object to the given index in the list.\
return this;
} }
moveAbove(_obj) { moveAbove(_obj): this {
// Moves this Game Object to be above the given Game Object in the display list. // Moves this Game Object to be above the given Game Object in the display list.
return this;
} }
moveBelow(_obj) { moveBelow(_obj): this {
// Moves this Game Object to be below the given Game Object in the display list. // Moves this Game Object to be below the given Game Object in the display list.
return this;
} }
setName(name: string) { setName(name: string): this {
this.name = name; this.name = name;
return this;
} }
bringToTop(_obj) { bringToTop(_obj): this {
// Brings this Game Object to the top of its parents display list. // Brings this Game Object to the top of its parents display list.
return this;
} }
on(_event, _callback, _source) {} on(_event, _callback, _source): this {
return this;
}
add(obj) { add(...obj: MockGameObject[]): this {
// Adds a child to this Game Object. // Adds a child to this Game Object.
this.list.push(obj); this.list.push(...obj);
return this;
} }
removeAll() { removeAll(): this {
// Removes all Game Objects from this Container. // Removes all Game Objects from this Container.
this.list = []; this.list = [];
return this;
} }
addAt(obj, index) { addAt(obj: MockGameObject | MockGameObject[], index = 0): this {
// Adds a Game Object to this Container at the given index. // Adds a Game Object to this Container at the given index.
this.list.splice(index, 0, obj); if (!Array.isArray(obj)) {
obj = [obj];
}
this.list.splice(index, 0, ...obj);
return this;
} }
remove(obj) { remove(obj: MockGameObject | MockGameObject[], destroyChild = false): this {
const index = this.list.indexOf(obj); if (!Array.isArray(obj)) {
if (index !== -1) { obj = [obj];
this.list.splice(index, 1);
} }
for (const item of obj) {
const index = this.list.indexOf(item);
if (index !== -1) {
this.list.splice(index, 1);
}
if (destroyChild) {
item.destroy?.();
}
}
return this;
} }
getIndex(obj) { getIndex(obj) {
@ -210,15 +264,28 @@ export default class MockContainer implements MockGameObject {
return this.list; return this.list;
} }
getByName(key: string) { getByName(key: string): MockGameObject | null {
return this.list.find(v => v.name === key) ?? new MockContainer(this.textureManager, 0, 0); return this.list.find(v => v.name === key) ?? new MockContainer(this.textureManager, 0, 0);
} }
disableInteractive = () => null; disableInteractive(): this {
return this;
}
each(method) { each(method): this {
for (const item of this.list) { for (const item of this.list) {
method(item); method(item);
} }
return this;
}
copyPosition(source: { x?: number; y?: number }): this {
if (source.x !== undefined) {
this.x = source.x;
}
if (source.y !== undefined) {
this.y = source.y;
}
return this;
} }
} }

View File

@ -8,57 +8,76 @@ export default class MockGraphics implements MockGameObject {
this.scene = textureManager.scene; this.scene = textureManager.scene;
} }
fillStyle(_color) { fillStyle(_color): this {
// Sets the fill style to be used by the fill methods. // Sets the fill style to be used by the fill methods.
return this;
} }
beginPath() { beginPath(): this {
// Starts a new path by emptying the list of sub-paths. Call this method when you want to create a new path. // Starts a new path by emptying the list of sub-paths. Call this method when you want to create a new path.
return this;
} }
fillRect(_x, _y, _width, _height) { fillRect(_x, _y, _width, _height): this {
// Adds a rectangle shape to the path which is filled when you call fill(). // Adds a rectangle shape to the path which is filled when you call fill().
return this;
} }
createGeometryMask() { createGeometryMask(): this {
// Creates a geometry mask. // Creates a geometry mask.
return this;
} }
setOrigin(_x, _y) {} setOrigin(_x, _y): this {
return this;
}
setAlpha(_alpha) {} setAlpha(_alpha): this {
return this;
}
setVisible(_visible) {} setVisible(_visible): this {
return this;
}
setName(_name) {} setName(_name) {
return this;
}
once(_event, _callback, _source) {} once(_event, _callback, _source) {
return this;
}
removeFromDisplayList() { removeFromDisplayList(): this {
// same as remove or destroy // same as remove or destroy
return this;
} }
addedToScene() { addedToScene() {
// This callback is invoked when this Game Object is added to a Scene. // This callback is invoked when this Game Object is added to a Scene.
} }
setPositionRelative(_source, _x, _y) { setPositionRelative(_source, _x, _y): this {
/// Sets the position of this Game Object to be a relative position from the source Game Object. /// Sets the position of this Game Object to be a relative position from the source Game Object.
return this;
} }
destroy() { destroy() {
this.list = []; this.list = [];
} }
setScale(_scale) { setScale(_scale): this {
// Sets the scale of this Game Object. return this;
} }
off(_event, _callback, _source) {} off(_event, _callback, _source): this {
return this;
}
add(obj) { add(obj): this {
// Adds a child to this Game Object. // Adds a child to this Game Object.
this.list.push(obj); this.list.push(obj);
return this;
} }
removeAll() { removeAll() {
@ -90,4 +109,8 @@ export default class MockGraphics implements MockGameObject {
getAll() { getAll() {
return this.list; return this.list;
} }
copyPosition(_source): this {
return this;
}
} }

View File

@ -10,34 +10,47 @@ export default class MockRectangle implements MockGameObject {
this.fillColor = fillColor; this.fillColor = fillColor;
this.scene = textureManager.scene; this.scene = textureManager.scene;
} }
setOrigin(_x, _y) {} setOrigin(_x, _y): this {
return this;
}
setAlpha(_alpha) {} setAlpha(_alpha): this {
setVisible(_visible) {} return this;
}
setVisible(_visible): this {
return this;
}
setName(_name) {} setName(_name): this {
return this;
}
once(_event, _callback, _source) {} once(_event, _callback, _source): this {
return this;
}
removeFromDisplayList() { removeFromDisplayList(): this {
// same as remove or destroy // same as remove or destroy
return this;
} }
addedToScene() { addedToScene() {
// This callback is invoked when this Game Object is added to a Scene. // This callback is invoked when this Game Object is added to a Scene.
} }
setPositionRelative(_source, _x, _y) { setPositionRelative(_source, _x, _y): this {
/// Sets the position of this Game Object to be a relative position from the source Game Object. /// Sets the position of this Game Object to be a relative position from the source Game Object.
return this;
} }
destroy() { destroy() {
this.list = []; this.list = [];
} }
add(obj) { add(obj): this {
// Adds a child to this Game Object. // Adds a child to this Game Object.
this.list.push(obj); this.list.push(obj);
return this;
} }
removeAll() { removeAll() {
@ -45,16 +58,18 @@ export default class MockRectangle implements MockGameObject {
this.list = []; this.list = [];
} }
addAt(obj, index) { addAt(obj, index): this {
// Adds a Game Object to this Container at the given index. // Adds a Game Object to this Container at the given index.
this.list.splice(index, 0, obj); this.list.splice(index, 0, obj);
return this;
} }
remove(obj) { remove(obj): this {
const index = this.list.indexOf(obj); const index = this.list.indexOf(obj);
if (index !== -1) { if (index !== -1) {
this.list.splice(index, 1); this.list.splice(index, 1);
} }
return this;
} }
getIndex(obj) { getIndex(obj) {
@ -69,9 +84,12 @@ export default class MockRectangle implements MockGameObject {
getAll() { getAll() {
return this.list; return this.list;
} }
setScale(_scale) { setScale(_scale): this {
// return this.phaserText.setScale(scale); // return this.phaserText.setScale(scale);
return this;
} }
off() {} off(): this {
return this;
}
} }

View File

@ -1,6 +1,5 @@
import Phaser from "phaser"; import Phaser from "phaser";
import type { MockGameObject } from "../mockGameObject"; import type { MockGameObject } from "../mockGameObject";
import Sprite = Phaser.GameObjects.Sprite;
import Frame = Phaser.Textures.Frame; import Frame = Phaser.Textures.Frame;
export default class MockSprite implements MockGameObject { export default class MockSprite implements MockGameObject {
@ -21,7 +20,9 @@ export default class MockSprite implements MockGameObject {
Phaser.GameObjects.Sprite.prototype.setInteractive = this.setInteractive; Phaser.GameObjects.Sprite.prototype.setInteractive = this.setInteractive;
// @ts-ignore // @ts-ignore
Phaser.GameObjects.Sprite.prototype.setTexture = this.setTexture; Phaser.GameObjects.Sprite.prototype.setTexture = this.setTexture;
// @ts-ignore
Phaser.GameObjects.Sprite.prototype.setSizeToFrame = this.setSizeToFrame; Phaser.GameObjects.Sprite.prototype.setSizeToFrame = this.setSizeToFrame;
// @ts-ignore
Phaser.GameObjects.Sprite.prototype.setFrame = this.setFrame; Phaser.GameObjects.Sprite.prototype.setFrame = this.setFrame;
// Phaser.GameObjects.Sprite.prototype.disable = this.disable; // Phaser.GameObjects.Sprite.prototype.disable = this.disable;
@ -37,46 +38,55 @@ export default class MockSprite implements MockGameObject {
}; };
} }
setTexture(_key: string, _frame?: string | number) { setTexture(_key: string, _frame?: string | number): this {
return this; return this;
} }
setSizeToFrame(_frame?: boolean | Frame): Sprite { setSizeToFrame(_frame?: boolean | Frame): this {
return {} as Sprite; return this;
} }
setPipeline(obj) { setPipeline(obj): this {
// Sets the pipeline of this Game Object. // Sets the pipeline of this Game Object.
return this.phaserSprite.setPipeline(obj); this.phaserSprite.setPipeline(obj);
return this;
} }
off(_event, _callback, _source) {} off(_event, _callback, _source): this {
return this;
}
setTintFill(color) { setTintFill(color): this {
// Sets the tint fill color. // Sets the tint fill color.
return this.phaserSprite.setTintFill(color); this.phaserSprite.setTintFill(color);
return this;
} }
setScale(scale) { setScale(scale = 1): this {
return this.phaserSprite.setScale(scale); this.phaserSprite.setScale(scale);
return this;
} }
setOrigin(x, y) { setOrigin(x = 0.5, y = x): this {
return this.phaserSprite.setOrigin(x, y); this.phaserSprite.setOrigin(x, y);
return this;
} }
setSize(width, height) { setSize(width, height): this {
// Sets the size of this Game Object. // Sets the size of this Game Object.
return this.phaserSprite.setSize(width, height); this.phaserSprite.setSize(width, height);
return this;
} }
once(event, callback, source) { once(event, callback, source): this {
return this.phaserSprite.once(event, callback, source); this.phaserSprite.once(event, callback, source);
return this;
} }
removeFromDisplayList() { removeFromDisplayList(): this {
// same as remove or destroy // same as remove or destroy
return this.phaserSprite.removeFromDisplayList(); this.phaserSprite.removeFromDisplayList();
return this;
} }
addedToScene() { addedToScene() {
@ -84,97 +94,117 @@ export default class MockSprite implements MockGameObject {
return this.phaserSprite.addedToScene(); return this.phaserSprite.addedToScene();
} }
setVisible(visible) { setVisible(visible): this {
return this.phaserSprite.setVisible(visible); this.phaserSprite.setVisible(visible);
return this;
} }
setPosition(x, y) { setPosition(x, y): this {
return this.phaserSprite.setPosition(x, y); this.phaserSprite.setPosition(x, y);
return this;
} }
setRotation(radians) { setRotation(radians): this {
return this.phaserSprite.setRotation(radians); this.phaserSprite.setRotation(radians);
return this;
} }
stop() { stop(): this {
return this.phaserSprite.stop(); this.phaserSprite.stop();
return this;
} }
setInteractive = () => null; setInteractive(): this {
return this;
on(event, callback, source) {
return this.phaserSprite.on(event, callback, source);
} }
setAlpha(alpha) { on(event, callback, source): this {
return this.phaserSprite.setAlpha(alpha); this.phaserSprite.on(event, callback, source);
return this;
} }
setTint(color) { setAlpha(alpha): this {
this.phaserSprite.setAlpha(alpha);
return this;
}
setTint(color): this {
// Sets the tint of this Game Object. // Sets the tint of this Game Object.
return this.phaserSprite.setTint(color); this.phaserSprite.setTint(color);
return this;
} }
setFrame(frame, _updateSize?: boolean, _updateOrigin?: boolean) { setFrame(frame, _updateSize?: boolean, _updateOrigin?: boolean): this {
// Sets the frame this Game Object will use to render with. // Sets the frame this Game Object will use to render with.
this.frame = frame; this.frame = frame;
return frame; return this;
} }
setPositionRelative(source, x, y) { setPositionRelative(source, x, y): this {
/// Sets the position of this Game Object to be a relative position from the source Game Object. /// Sets the position of this Game Object to be a relative position from the source Game Object.
return this.phaserSprite.setPositionRelative(source, x, y); this.phaserSprite.setPositionRelative(source, x, y);
return this;
} }
setY(y) { setY(y: number): this {
return this.phaserSprite.setY(y); this.phaserSprite.setY(y);
return this;
} }
setCrop(x, y, width, height) { setCrop(x: number, y: number, width: number, height: number): this {
// Sets the crop size of this Game Object. // Sets the crop size of this Game Object.
return this.phaserSprite.setCrop(x, y, width, height); this.phaserSprite.setCrop(x, y, width, height);
return this;
} }
clearTint() { clearTint(): this {
// Clears any previously set tint. // Clears any previously set tint.
return this.phaserSprite.clearTint(); this.phaserSprite.clearTint();
return this;
} }
disableInteractive() { disableInteractive(): this {
// Disables Interactive features of this Game Object. // Disables Interactive features of this Game Object.
return null; return this;
} }
apply() { apply() {
return this.phaserSprite.apply(); this.phaserSprite.apply();
return this;
} }
play() { play(): this {
// return this.phaserSprite.play(); // return this.phaserSprite.play();
return this; return this;
} }
setPipelineData(key, value) { setPipelineData(key: string, value: any): this {
this.pipelineData[key] = value; this.pipelineData[key] = value;
return this;
} }
destroy() { destroy() {
return this.phaserSprite.destroy(); return this.phaserSprite.destroy();
} }
setName(name) { setName(name: string): this {
return this.phaserSprite.setName(name); this.phaserSprite.setName(name);
return this;
} }
setAngle(angle) { setAngle(angle): this {
return this.phaserSprite.setAngle(angle); this.phaserSprite.setAngle(angle);
return this;
} }
setMask() {} setMask(): this {
return this;
}
add(obj) { add(obj): this {
// Adds a child to this Game Object. // Adds a child to this Game Object.
this.list.push(obj); this.list.push(obj);
return this;
} }
removeAll() { removeAll() {
@ -182,16 +212,18 @@ export default class MockSprite implements MockGameObject {
this.list = []; this.list = [];
} }
addAt(obj, index) { addAt(obj, index): this {
// Adds a Game Object to this Container at the given index. // Adds a Game Object to this Container at the given index.
this.list.splice(index, 0, obj); this.list.splice(index, 0, obj);
return this;
} }
remove(obj) { remove(obj): this {
const index = this.list.indexOf(obj); const index = this.list.indexOf(obj);
if (index !== -1) { if (index !== -1) {
this.list.splice(index, 1); this.list.splice(index, 1);
} }
return this;
} }
getIndex(obj) { getIndex(obj) {
@ -206,4 +238,9 @@ export default class MockSprite implements MockGameObject {
getAll() { getAll() {
return this.list; return this.list;
} }
copyPosition(obj): this {
this.phaserSprite.copyPosition(obj);
return this;
}
} }

View File

@ -107,42 +107,51 @@ export default class MockText implements MockGameObject {
} }
} }
setScale(_scale) { setScale(_scale): this {
// return this.phaserText.setScale(scale); // return this.phaserText.setScale(scale);
return this;
} }
setShadow(_shadowXpos, _shadowYpos, _shadowColor) { setShadow(_shadowXpos, _shadowYpos, _shadowColor): this {
// Sets the shadow settings for this Game Object. // Sets the shadow settings for this Game Object.
// return this.phaserText.setShadow(shadowXpos, shadowYpos, shadowColor); // return this.phaserText.setShadow(shadowXpos, shadowYpos, shadowColor);
return this;
} }
setLineSpacing(_lineSpacing) { setLineSpacing(_lineSpacing): this {
// Sets the line spacing value of this Game Object. // Sets the line spacing value of this Game Object.
// return this.phaserText.setLineSpacing(lineSpacing); // return this.phaserText.setLineSpacing(lineSpacing);
return this;
} }
setOrigin(_x, _y) { setOrigin(_x, _y): this {
// return this.phaserText.setOrigin(x, y); // return this.phaserText.setOrigin(x, y);
return this;
} }
once(_event, _callback, _source) { once(_event, _callback, _source): this {
// return this.phaserText.once(event, callback, source); // return this.phaserText.once(event, callback, source);
return this;
} }
off(_event, _callback, _obj) {} off(_event, _callback, _obj) {}
removedFromScene() {} removedFromScene() {}
addToDisplayList() {} addToDisplayList(): this {
return this;
setStroke(_color, _thickness) {
// Sets the stroke color and thickness.
// return this.phaserText.setStroke(color, thickness);
} }
removeFromDisplayList() { setStroke(_color, _thickness): this {
// Sets the stroke color and thickness.
// return this.phaserText.setStroke(color, thickness);
return this;
}
removeFromDisplayList(): this {
// same as remove or destroy // same as remove or destroy
// return this.phaserText.removeFromDisplayList(); // return this.phaserText.removeFromDisplayList();
return this;
} }
addedToScene() { addedToScene() {
@ -154,12 +163,14 @@ export default class MockText implements MockGameObject {
// return this.phaserText.setVisible(visible); // return this.phaserText.setVisible(visible);
} }
setY(_y) { setY(_y): this {
// return this.phaserText.setY(y); // return this.phaserText.setY(y);
return this;
} }
setX(_x) { setX(_x): this {
// return this.phaserText.setX(x); // return this.phaserText.setX(x);
return this;
} }
/** /**
@ -169,27 +180,33 @@ export default class MockText implements MockGameObject {
* @param z The z position of this Game Object. Default 0. * @param z The z position of this Game Object. Default 0.
* @param w The w position of this Game Object. Default 0. * @param w The w position of this Game Object. Default 0.
*/ */
setPosition(_x?: number, _y?: number, _z?: number, _w?: number) {} setPosition(_x?: number, _y?: number, _z?: number, _w?: number): this {
return this;
}
setText(text) { setText(text): this {
// Sets the text this Game Object will display. // Sets the text this Game Object will display.
// return this.phaserText.setText\(text); // return this.phaserText.setText\(text);
this.text = text; this.text = text;
return this;
} }
setAngle(_angle) { setAngle(_angle): this {
// Sets the angle of this Game Object. // Sets the angle of this Game Object.
// return this.phaserText.setAngle(angle); // return this.phaserText.setAngle(angle);
return this;
} }
setPositionRelative(_source, _x, _y) { setPositionRelative(_source, _x, _y): this {
/// Sets the position of this Game Object to be a relative position from the source Game Object. /// Sets the position of this Game Object to be a relative position from the source Game Object.
// return this.phaserText.setPositionRelative(source, x, y); // return this.phaserText.setPositionRelative(source, x, y);
return this;
} }
setShadowOffset(_offsetX, _offsetY) { setShadowOffset(_offsetX, _offsetY): this {
// Sets the shadow offset values. // Sets the shadow offset values.
// return this.phaserText.setShadowOffset(offsetX, offsetY); // return this.phaserText.setShadowOffset(offsetX, offsetY);
return this;
} }
setWordWrapWidth(width) { setWordWrapWidth(width) {
@ -197,9 +214,10 @@ export default class MockText implements MockGameObject {
this.wordWrapWidth = width; this.wordWrapWidth = width;
} }
setFontSize(_fontSize) { setFontSize(_fontSize): this {
// Sets the font size of this Game Object. // Sets the font size of this Game Object.
// return this.phaserText.setFontSize(fontSize); // return this.phaserText.setFontSize(fontSize);
return this;
} }
getBounds() { getBounds() {
@ -209,25 +227,31 @@ export default class MockText implements MockGameObject {
}; };
} }
setColor(color: string) { setColor(color: string): this {
this.color = color; this.color = color;
return this;
} }
setInteractive = () => null; setInteractive(): this {
return this;
}
setShadowColor(_color) { setShadowColor(_color): this {
// Sets the shadow color. // Sets the shadow color.
// return this.phaserText.setShadowColor(color); // return this.phaserText.setShadowColor(color);
return this;
} }
setTint(_color) { setTint(_color): this {
// Sets the tint of this Game Object. // Sets the tint of this Game Object.
// return this.phaserText.setTint(color); // return this.phaserText.setTint(color);
return this;
} }
setStrokeStyle(_thickness, _color) { setStrokeStyle(_thickness, _color): this {
// Sets the stroke style for the graphics. // Sets the stroke style for the graphics.
// return this.phaserText.setStrokeStyle(thickness, color); // return this.phaserText.setStrokeStyle(thickness, color);
return this;
} }
destroy() { destroy() {
@ -235,20 +259,24 @@ export default class MockText implements MockGameObject {
this.list = []; this.list = [];
} }
setAlpha(_alpha) { setAlpha(_alpha): this {
// return this.phaserText.setAlpha(alpha); // return this.phaserText.setAlpha(alpha);
return this;
} }
setName(name: string) { setName(name: string): this {
this.name = name; this.name = name;
return this;
} }
setAlign(_align) { setAlign(_align): this {
// return this.phaserText.setAlign(align); // return this.phaserText.setAlign(align);
return this;
} }
setMask() { setMask(): this {
/// Sets the mask that this Game Object will use to render with. /// Sets the mask that this Game Object will use to render with.
return this;
} }
getBottomLeft() { getBottomLeft() {
@ -265,37 +293,43 @@ export default class MockText implements MockGameObject {
}; };
} }
disableInteractive() { disableInteractive(): this {
// Disables interaction with this Game Object. // Disables interaction with this Game Object.
return this;
} }
clearTint() { clearTint(): this {
// Clears tint on this Game Object. // Clears tint on this Game Object.
return this;
} }
add(obj) { add(obj): this {
// Adds a child to this Game Object. // Adds a child to this Game Object.
this.list.push(obj); this.list.push(obj);
return this;
} }
removeAll() { removeAll(): this {
// Removes all Game Objects from this Container. // Removes all Game Objects from this Container.
this.list = []; this.list = [];
return this;
} }
addAt(obj, index) { addAt(obj, index): this {
// Adds a Game Object to this Container at the given index. // Adds a Game Object to this Container at the given index.
this.list.splice(index, 0, obj); this.list.splice(index, 0, obj);
return this;
} }
remove(obj) { remove(obj): this {
const index = this.list.indexOf(obj); const index = this.list.indexOf(obj);
if (index !== -1) { if (index !== -1) {
this.list.splice(index, 1); this.list.splice(index, 1);
} }
return this;
} }
getIndex(obj) { getIndex(obj): number {
const index = this.list.indexOf(obj); const index = this.list.indexOf(obj);
return index || -1; return index || -1;
} }

View File

@ -70,10 +70,10 @@ export function initTestFile() {
* @param x The relative x position * @param x The relative x position
* @param y The relative y position * @param y The relative y position
*/ */
const setPositionRelative = function (guideObject: any, x: number, y: number) { const setPositionRelative = function (guideObject: any, x: number, y: number): any {
const offsetX = guideObject.width * (-0.5 + (0.5 - guideObject.originX)); const offsetX = guideObject.width * (-0.5 + (0.5 - guideObject.originX));
const offsetY = guideObject.height * (-0.5 + (0.5 - guideObject.originY)); const offsetY = guideObject.height * (-0.5 + (0.5 - guideObject.originY));
this.setPosition(guideObject.x + offsetX + x, guideObject.y + offsetY + y); return this.setPosition(guideObject.x + offsetX + x, guideObject.y + offsetY + y);
}; };
Phaser.GameObjects.Container.prototype.setPositionRelative = setPositionRelative; Phaser.GameObjects.Container.prototype.setPositionRelative = setPositionRelative;