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" : "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(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, instant?: boolean, ): void { super.updatePokemonHp(pokemon, resolve, instant); this.lastHp = pokemon.hp; } }