[Refactor] Add methods isPlayer and isEnemy to reduce circular imports

https://github.com/pagefaultgames/pokerogue/pull/5902

* Added functions `isPlayer` and `isEnemy` for type checking

* Apply suggestions from Kev code review

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

* Fix merge issue

* Split imports

---------

Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
This commit is contained in:
Bertie690 2025-06-07 20:44:58 -04:00 committed by GitHub
parent d3bc33cd4e
commit ef6029ae4b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 63 additions and 54 deletions

View File

@ -811,6 +811,7 @@ export default class BattleScene extends SceneBase {
}
}
// TODO: Add a `getPartyOnSide` function for getting the party of a pokemon
public getPlayerParty(): PlayerPokemon[] {
return this.party;
}
@ -3086,9 +3087,9 @@ export default class BattleScene extends SceneBase {
const removeOld = itemModifier.stackCount === 0;
if (!removeOld || !source || this.removeModifier(itemModifier, !source.isPlayer())) {
if (!removeOld || !source || this.removeModifier(itemModifier, source.isEnemy())) {
const addModifier = () => {
if (!matchingModifier || this.removeModifier(matchingModifier, !target.isPlayer())) {
if (!matchingModifier || this.removeModifier(matchingModifier, target.isEnemy())) {
if (target.isPlayer()) {
this.addModifier(newItemModifier, ignoreUpdate, playSound, false, instant);
if (source && itemLost) {
@ -3492,12 +3493,12 @@ export default class BattleScene extends SceneBase {
}
if (matchingFormChange) {
let phase: Phase;
if (pokemon instanceof PlayerPokemon && !matchingFormChange.quiet) {
if (pokemon.isPlayer() && !matchingFormChange.quiet) {
phase = new FormChangePhase(pokemon, matchingFormChange, modal);
} else {
phase = new QuietFormChangePhase(pokemon, matchingFormChange);
}
if (pokemon instanceof PlayerPokemon && !matchingFormChange.quiet && modal) {
if (pokemon.isPlayer() && !matchingFormChange.quiet && modal) {
this.overridePhase(phase);
} else if (delayed) {
this.pushPhase(phase);
@ -3595,7 +3596,7 @@ export default class BattleScene extends SceneBase {
activePokemon = activePokemon.concat(this.getEnemyParty());
for (const p of activePokemon) {
keys.push(p.getSpriteKey(true));
if (p instanceof PlayerPokemon) {
if (p.isPlayer()) {
keys.push(p.getBattleSpriteKey(true, true));
}
keys.push(p.species.getCryKey(p.formIndex));
@ -3611,7 +3612,7 @@ export default class BattleScene extends SceneBase {
* @param pokemon The (enemy) pokemon
*/
initFinalBossPhaseTwo(pokemon: Pokemon): void {
if (pokemon instanceof EnemyPokemon && pokemon.isBoss() && !pokemon.formIndex && pokemon.bossSegmentIndex < 1) {
if (pokemon.isEnemy() && pokemon.isBoss() && !pokemon.formIndex && pokemon.bossSegmentIndex < 1) {
this.fadeOutBgm(fixedInt(2000), false);
this.ui.showDialogue(
battleSpecDialogue[BattleSpec.FINAL_BOSS].firstStageWin,

View File

@ -2617,7 +2617,7 @@ export class PostSummonUserFieldRemoveStatusEffectAbAttr extends PostSummonAbAtt
}
override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean {
const party = pokemon instanceof PlayerPokemon ? globalScene.getPlayerField() : globalScene.getEnemyField();
const party = pokemon.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField();
return party.filter(p => p.isAllowedInBattle()).length > 0;
}
@ -2629,7 +2629,7 @@ export class PostSummonUserFieldRemoveStatusEffectAbAttr extends PostSummonAbAtt
* @param args - n/a
*/
override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void {
const party = pokemon instanceof PlayerPokemon ? globalScene.getPlayerField() : globalScene.getEnemyField();
const party = pokemon.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField();
const allowedParty = party.filter(p => p.isAllowedInBattle());
if (!simulated) {
@ -5539,7 +5539,7 @@ class ForceSwitchOutHelper {
* - Whether there are available party members to switch in.
* - If the Pokémon is still alive (hp > 0), and if so, it leaves the field and a new SwitchPhase is initiated.
*/
if (switchOutTarget instanceof PlayerPokemon) {
if (switchOutTarget.isPlayer()) {
if (globalScene.getPlayerParty().filter((p) => p.isAllowedInBattle() && !p.isOnField()).length < 1) {
return false;
}
@ -5608,7 +5608,7 @@ class ForceSwitchOutHelper {
*/
public getSwitchOutCondition(pokemon: Pokemon, opponent: Pokemon): boolean {
const switchOutTarget = pokemon;
const player = switchOutTarget instanceof PlayerPokemon;
const player = switchOutTarget.isPlayer();
if (player) {
const blockedByAbility = new BooleanHolder(false);

View File

@ -837,7 +837,7 @@ export default class Move implements Localizable {
aura.applyPreAttack(source, null, simulated, target, this, [ power ]);
}
const alliedField: Pokemon[] = source instanceof PlayerPokemon ? globalScene.getPlayerField() : globalScene.getEnemyField();
const alliedField: Pokemon[] = source.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField();
alliedField.forEach(p => applyPreAttackAbAttrs(UserFieldMoveTypePowerBoostAbAttr, p, target, this, simulated, power));
power.value *= typeChangeMovePowerMultiplier.value;
@ -4125,7 +4125,7 @@ export class FriendshipPowerAttr extends VariablePowerAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const power = args[0] as NumberHolder;
const friendshipPower = Math.floor(Math.min(user instanceof PlayerPokemon ? user.friendship : user.species.baseFriendship, 255) / 2.5);
const friendshipPower = Math.floor(Math.min(user.isPlayer() ? user.friendship : user.species.baseFriendship, 255) / 2.5);
power.value = Math.max(!this.invert ? friendshipPower : 102 - friendshipPower, 1);
return true;
@ -6149,14 +6149,14 @@ export class RevivalBlessingAttr extends MoveEffectAttr {
* @param target {@linkcode Pokemon} target of this move
* @param move {@linkcode Move} being used
* @param args N/A
* @returns Promise, true if function succeeds.
* @returns `true` if function succeeds.
*/
override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
// If user is player, checks if the user has fainted pokemon
if (user instanceof PlayerPokemon) {
if (user.isPlayer()) {
globalScene.unshiftPhase(new RevivalBlessingPhase(user));
return true;
} else if (user instanceof EnemyPokemon && user.hasTrainer() && globalScene.getEnemyParty().findIndex((p) => p.isFainted() && !p.isBoss()) > -1) {
} else if (user.isEnemy() && user.hasTrainer() && globalScene.getEnemyParty().findIndex((p) => p.isFainted() && !p.isBoss()) > -1) {
// If used by an enemy trainer with at least one fainted non-boss Pokemon, this
// revives one of said Pokemon selected at random.
const faintedPokemon = globalScene.getEnemyParty().filter((p) => p.isFainted() && !p.isBoss());
@ -6187,10 +6187,8 @@ export class RevivalBlessingAttr extends MoveEffectAttr {
getCondition(): MoveConditionFunc {
return (user, target, move) =>
(user instanceof PlayerPokemon && globalScene.getPlayerParty().some((p) => p.isFainted())) ||
(user instanceof EnemyPokemon &&
user.hasTrainer() &&
globalScene.getEnemyParty().some((p) => p.isFainted() && !p.isBoss()));
user.hasTrainer() &&
(user.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty()).some((p: Pokemon) => p.isFainted() && !p.isBoss());
}
override getUserBenefitScore(user: Pokemon, _target: Pokemon, _move: Move): number {
@ -6228,7 +6226,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
// (e.g. when it uses Flip Turn), make it spit out the Tatsugiri before switching out.
switchOutTarget.lapseTag(BattlerTagType.COMMANDED);
if (switchOutTarget instanceof PlayerPokemon) {
if (switchOutTarget.isPlayer()) {
/**
* Check if Wimp Out/Emergency Exit activates due to being hit by U-turn or Volt Switch
* If it did, the user of U-turn or Volt Switch will not be switched out.
@ -6382,7 +6380,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
getSwitchOutCondition(): MoveConditionFunc {
return (user, target, move) => {
const switchOutTarget = (this.selfSwitch ? user : target);
const player = switchOutTarget instanceof PlayerPokemon;
const player = switchOutTarget.isPlayer();
const forceSwitchAttr = move.getAttrs(ForceSwitchOutAttr).find(attr => attr.switchType === SwitchType.FORCE_SWITCH);
if (!this.selfSwitch) {
@ -9857,7 +9855,7 @@ export function initMoves() {
const lastEnemyFaint = globalScene.currentBattle.enemyFaintsHistory[globalScene.currentBattle.enemyFaintsHistory.length - 1];
return (
(lastPlayerFaint !== undefined && turn - lastPlayerFaint.turn === 1 && user.isPlayer()) ||
(lastEnemyFaint !== undefined && turn - lastEnemyFaint.turn === 1 && !user.isPlayer())
(lastEnemyFaint !== undefined && turn - lastEnemyFaint.turn === 1 && user.isEnemy())
) ? 2 : 1;
}),
new AttackMove(MoveId.FINAL_GAMBIT, PokemonType.FIGHTING, MoveCategory.SPECIAL, -1, 100, 5, -1, 0, 5)

View File

@ -503,7 +503,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (level > 1) {
const fused = new BooleanHolder(globalScene.gameMode.isSplicedOnly);
if (!fused.value && !this.isPlayer() && !this.hasTrainer()) {
if (!fused.value && this.isEnemy() && !this.hasTrainer()) {
globalScene.applyModifier(EnemyFusionChanceModifier, false, fused);
}
@ -788,7 +788,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return true;
}
abstract isPlayer(): boolean;
abstract isPlayer(): this is PlayerPokemon;
abstract isEnemy(): this is EnemyPokemon;
abstract hasTrainer(): boolean;
@ -2050,7 +2052,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (Overrides.ABILITY_OVERRIDE && this.isPlayer()) {
return allAbilities[Overrides.ABILITY_OVERRIDE];
}
if (Overrides.OPP_ABILITY_OVERRIDE && !this.isPlayer()) {
if (Overrides.OPP_ABILITY_OVERRIDE && this.isEnemy()) {
return allAbilities[Overrides.OPP_ABILITY_OVERRIDE];
}
if (this.isFusion()) {
@ -2080,7 +2082,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (Overrides.PASSIVE_ABILITY_OVERRIDE && this.isPlayer()) {
return allAbilities[Overrides.PASSIVE_ABILITY_OVERRIDE];
}
if (Overrides.OPP_PASSIVE_ABILITY_OVERRIDE && !this.isPlayer()) {
if (Overrides.OPP_PASSIVE_ABILITY_OVERRIDE && this.isEnemy()) {
return allAbilities[Overrides.OPP_PASSIVE_ABILITY_OVERRIDE];
}
if (!isNullOrUndefined(this.customPokemonData.passive) && this.customPokemonData.passive !== -1) {
@ -2152,7 +2154,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
// returns override if valid for current case
if (
(Overrides.HAS_PASSIVE_ABILITY_OVERRIDE === false && this.isPlayer()) ||
(Overrides.OPP_HAS_PASSIVE_ABILITY_OVERRIDE === false && !this.isPlayer())
(Overrides.OPP_HAS_PASSIVE_ABILITY_OVERRIDE === false && this.isEnemy())
) {
return false;
}
@ -2160,7 +2162,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
((Overrides.PASSIVE_ABILITY_OVERRIDE !== AbilityId.NONE || Overrides.HAS_PASSIVE_ABILITY_OVERRIDE) &&
this.isPlayer()) ||
((Overrides.OPP_PASSIVE_ABILITY_OVERRIDE !== AbilityId.NONE || Overrides.OPP_HAS_PASSIVE_ABILITY_OVERRIDE) &&
!this.isPlayer())
this.isEnemy())
) {
return true;
}
@ -2169,7 +2171,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const { currentBattle, gameMode } = globalScene;
const waveIndex = currentBattle?.waveIndex;
if (
this instanceof EnemyPokemon &&
this.isEnemy() &&
(currentBattle?.battleSpec === BattleSpec.FINAL_BOSS ||
gameMode.isEndlessMinorBoss(waveIndex) ||
gameMode.isEndlessMajorBoss(waveIndex))
@ -2979,9 +2981,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
let fusionOverride: PokemonSpecies | undefined = undefined;
if (forStarter && this instanceof PlayerPokemon && Overrides.STARTER_FUSION_SPECIES_OVERRIDE) {
if (forStarter && this.isPlayer() && Overrides.STARTER_FUSION_SPECIES_OVERRIDE) {
fusionOverride = getPokemonSpecies(Overrides.STARTER_FUSION_SPECIES_OVERRIDE);
} else if (this instanceof EnemyPokemon && Overrides.OPP_FUSION_SPECIES_OVERRIDE) {
} else if (this.isEnemy() && Overrides.OPP_FUSION_SPECIES_OVERRIDE) {
fusionOverride = getPokemonSpecies(Overrides.OPP_FUSION_SPECIES_OVERRIDE);
}
@ -3292,7 +3294,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.battleInfo.setX(this.battleInfo.x + (this.isPlayer() ? 150 : !this.isBoss() ? -150 : -198));
this.battleInfo.setVisible(true);
if (this.isPlayer()) {
this.battleInfo.expMaskRect.x += 150;
// TODO: How do you get this to not require a private property access?
this["battleInfo"].expMaskRect.x += 150;
}
globalScene.tweens.add({
targets: [this.battleInfo, this.battleInfo.expMaskRect],
@ -3313,7 +3316,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
ease: "Cubic.easeIn",
onComplete: () => {
if (this.isPlayer()) {
this.battleInfo.expMaskRect.x -= 150;
// TODO: How do you get this to not require a private property access?
this["battleInfo"].expMaskRect.x -= 150;
}
this.battleInfo.setVisible(false);
this.battleInfo.setX(this.battleInfo.x - (this.isPlayer() ? 150 : !this.isBoss() ? -150 : -198));
@ -3408,7 +3412,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* @returns An array of Pokémon on the allied field.
*/
getAlliedField(): Pokemon[] {
return this instanceof PlayerPokemon ? globalScene.getPlayerField() : globalScene.getEnemyField();
return this.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField();
}
/**
@ -4260,7 +4264,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
// Copy all stat stages
for (const s of BATTLE_STATS) {
const sourceStage = source.getStatStage(s);
if (this instanceof PlayerPokemon && sourceStage === 6) {
if (this.isPlayer() && sourceStage === 6) {
globalScene.validateAchv(achvs.TRANSFER_MAX_STAT_STAGE);
}
this.setStatStage(s, sourceStage);
@ -5468,7 +5472,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
heldItem.stackCount--;
if (heldItem.stackCount <= 0) {
globalScene.removeModifier(heldItem, !this.isPlayer());
globalScene.removeModifier(heldItem, this.isEnemy());
}
if (forBattle) {
applyPostItemLostAbAttrs(PostItemLostAbAttr, this, false);
@ -5541,15 +5545,19 @@ export class PlayerPokemon extends Pokemon {
this.battleInfo.initInfo(this);
}
isPlayer(): boolean {
override isPlayer(): this is PlayerPokemon {
return true;
}
hasTrainer(): boolean {
override isEnemy(): this is EnemyPokemon {
return false;
}
override hasTrainer(): boolean {
return true;
}
isBoss(): boolean {
override isBoss(): boolean {
return false;
}
@ -6494,15 +6502,19 @@ export class EnemyPokemon extends Pokemon {
return [sortedBenefitScores[targetIndex][0]];
}
isPlayer() {
override isPlayer(): this is PlayerPokemon {
return false;
}
hasTrainer(): boolean {
override isEnemy(): this is EnemyPokemon {
return true;
}
override hasTrainer(): boolean {
return !!this.trainerSlot;
}
isBoss(): boolean {
override isBoss(): boolean {
return !!this.bossSegments;
}

View File

@ -16,7 +16,7 @@ export function getPokemonNameWithAffix(pokemon: Pokemon | undefined, useIllusio
switch (globalScene.currentBattle.battleSpec) {
case BattleSpec.DEFAULT:
return !pokemon.isPlayer()
return pokemon.isEnemy()
? pokemon.hasTrainer()
? i18next.t("battle:foePokemonWithAffix", {
pokemonName: pokemon.getNameToRender(useIllusion),
@ -26,7 +26,7 @@ export function getPokemonNameWithAffix(pokemon: Pokemon | undefined, useIllusio
})
: pokemon.getNameToRender(useIllusion);
case BattleSpec.FINAL_BOSS:
return !pokemon.isPlayer()
return pokemon.isEnemy()
? i18next.t("battle:foePokemonWithAffix", { pokemonName: pokemon.getNameToRender(useIllusion) })
: pokemon.getNameToRender(useIllusion);
default:

View File

@ -18,7 +18,8 @@ import { BattleSpec } from "#app/enums/battle-spec";
import { StatusEffect } from "#app/enums/status-effect";
import type { EnemyPokemon } from "#app/field/pokemon";
import type Pokemon from "#app/field/pokemon";
import { HitResult, PlayerPokemon, PokemonMove } from "#app/field/pokemon";
import { HitResult, PokemonMove } from "#app/field/pokemon";
import type { PlayerPokemon } from "#app/field/pokemon";
import { getPokemonNameWithAffix } from "#app/messages";
import { PokemonInstantReviveModifier } from "#app/modifier/modifier";
import { SwitchType } from "#enums/switch-type";
@ -203,7 +204,7 @@ export class FaintPhase extends PokemonPhase {
}
pokemon.faintCry(() => {
if (pokemon instanceof PlayerPokemon) {
if (pokemon.isPlayer()) {
pokemon.addFriendship(-FRIENDSHIP_LOSS_FROM_FAINT);
}
pokemon.hideInfo();

View File

@ -887,7 +887,7 @@ export class MoveEffectPhase extends PokemonPhase {
sourceBattlerIndex: user.getBattlerIndex(),
});
if (user.isPlayer() && !target.isPlayer()) {
if (user.isPlayer() && target.isEnemy()) {
globalScene.applyModifiers(DamageMoneyRewardModifier, true, user, new NumberHolder(damage));
}

View File

@ -6,7 +6,6 @@ import { getTypeRgb } from "#app/data/type";
import { BattleSpec } from "#app/enums/battle-spec";
import { BattlerTagType } from "#app/enums/battler-tag-type";
import type Pokemon from "#app/field/pokemon";
import { EnemyPokemon } from "#app/field/pokemon";
import { getPokemonNameWithAffix } from "#app/messages";
import { BattlePhase } from "./battle-phase";
import type { MovePhase } from "./move-phase";
@ -158,7 +157,7 @@ export class QuietFormChangePhase extends BattlePhase {
end(): void {
this.pokemon.findAndRemoveTags(t => t.tagType === BattlerTagType.AUTOTOMIZED);
if (globalScene?.currentBattle.battleSpec === BattleSpec.FINAL_BOSS && this.pokemon instanceof EnemyPokemon) {
if (globalScene?.currentBattle.battleSpec === BattleSpec.FINAL_BOSS && this.pokemon.isEnemy()) {
globalScene.playBgm();
globalScene.unshiftPhase(
new PokemonHealPhase(this.pokemon.getBattlerIndex(), this.pokemon.getMaxHp(), null, false, false, false, true),

View File

@ -139,7 +139,6 @@ export class SwitchSummonPhase extends SummonPhase {
return;
}
if (this.switchType === SwitchType.BATON_PASS) {
// If switching via baton pass, update opposing tags coming from the prior pokemon
(this.player ? globalScene.getEnemyField() : globalScene.getPlayerField()).forEach((enemyPokemon: Pokemon) =>

View File

@ -8,7 +8,7 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vite
import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { runMysteryEncounterToEnd } from "#test/mystery-encounter/encounter-test-utils";
import type BattleScene from "#app/battle-scene";
import { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
import { PokemonMove } from "#app/field/pokemon";
import { AnOfferYouCantRefuseEncounter } from "#app/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
@ -105,7 +105,7 @@ describe("An Offer You Can't Refuse - Mystery Encounter", () => {
i18next.t("ability:intimidate.name"),
);
expect(AnOfferYouCantRefuseEncounter.dialogueTokens?.moveOrAbility).toBe(i18next.t("ability:intimidate.name"));
expect(AnOfferYouCantRefuseEncounter.misc.pokemon instanceof PlayerPokemon).toBeTruthy();
expect(AnOfferYouCantRefuseEncounter.misc.pokemon.isPlayer()).toBeTruthy();
expect(AnOfferYouCantRefuseEncounter.misc?.price?.toString()).toBe(
AnOfferYouCantRefuseEncounter.dialogueTokens?.price,
);

View File

@ -10,7 +10,6 @@ import {
runSelectMysteryEncounterOption,
} from "#test/mystery-encounter/encounter-test-utils";
import type BattleScene from "#app/battle-scene";
import { PlayerPokemon } from "#app/field/pokemon";
import { HUMAN_TRANSITABLE_BIOMES } from "#app/data/mystery-encounters/mystery-encounters";
import {
getSalesmanSpeciesOffer,
@ -99,7 +98,7 @@ describe("The Pokemon Salesman - Mystery Encounter", () => {
expect(ThePokemonSalesmanEncounter.dialogueTokens?.purchasePokemon).toBeDefined();
expect(ThePokemonSalesmanEncounter.dialogueTokens?.price).toBeDefined();
expect(ThePokemonSalesmanEncounter.misc.pokemon instanceof PlayerPokemon).toBeTruthy();
expect(ThePokemonSalesmanEncounter.misc.pokemon.isPlayer()).toBeTruthy();
expect(ThePokemonSalesmanEncounter.misc?.price?.toString()).toBe(ThePokemonSalesmanEncounter.dialogueTokens?.price);
expect(onInitResult).toBe(true);
});