From 06b6beab020d516b5128d2215097ef9b2db8ed4c Mon Sep 17 00:00:00 2001 From: Bertie690 Date: Sun, 25 May 2025 18:49:18 -0400 Subject: [PATCH] Split up Pokemon data types to own files --- src/@types/pokemon-data.ts | 210 +++++++++++++++++++++++++++ src/field/pokemon.ts | 203 +------------------------- src/system/pokemon-data.ts | 3 +- test/battlerTags/stockpiling.test.ts | 2 +- test/battlerTags/substitute.test.ts | 3 +- 5 files changed, 218 insertions(+), 203 deletions(-) create mode 100644 src/@types/pokemon-data.ts diff --git a/src/@types/pokemon-data.ts b/src/@types/pokemon-data.ts new file mode 100644 index 00000000000..a18e41558f0 --- /dev/null +++ b/src/@types/pokemon-data.ts @@ -0,0 +1,210 @@ +import { type BattlerTag, loadBattlerTag } from "#app/data/battler-tags"; +import type { Gender } from "#app/data/gender"; +import type PokemonSpecies from "#app/data/pokemon-species"; +import type { PokemonSpeciesForm } from "#app/data/pokemon-species"; +import type { TypeDamageMultiplier } from "#app/data/type"; +import type { Variant } from "#app/sprites/variant"; +import { isNullOrUndefined } from "#app/utils/common"; +import type { Abilities } from "#enums/abilities"; +import type { BerryType } from "#enums/berry-type"; +import type { Moves } from "#enums/moves"; +import type { PokeballType } from "#enums/pokeball"; +import type { PokemonType } from "#enums/pokemon-type"; +import type { Species } from "#enums/species"; +import { type TurnMove, PokemonMove, type AttackMoveResult } from "#app/field/pokemon"; + +/** + * Persistent in-battle data for a {@linkcode Pokemon}. + * Resets on switch or new battle. + */ + +export class PokemonSummonData { + /** [Atk, Def, SpAtk, SpDef, Spd, Acc, Eva] */ + public statStages: number[] = [0, 0, 0, 0, 0, 0, 0]; + public moveQueue: TurnMove[] = []; + public tags: BattlerTag[] = []; + public abilitySuppressed = false; + + // Overrides for transform. + // TODO: Move these into a separate class & add rage fist hit count + public speciesForm: PokemonSpeciesForm | null = null; + public fusionSpeciesForm: PokemonSpeciesForm | null = null; + public ability: Abilities | undefined; + public passiveAbility: Abilities | undefined; + public gender: Gender | undefined; + public fusionGender: Gender | undefined; + public stats: number[] = [0, 0, 0, 0, 0, 0]; + public moveset: PokemonMove[] | null; + + // If not initialized this value will not be populated from save data. + public types: PokemonType[] = []; + public addedType: PokemonType | null = null; + + /** Data pertaining to this pokemon's illusion. */ + public illusion: IllusionData | null = null; + public illusionBroken = false; + + /** Array containing all berries eaten in the last turn; used by {@linkcode Abilities.CUD_CHEW} */ + public berriesEatenLast: BerryType[] = []; + + /** + * An array of all moves this pokemon has used since entering the battle. + * Used for most moves and abilities that check prior move usage or copy already-used moves. + */ + public moveHistory: TurnMove[] = []; + + constructor(source?: PokemonSummonData | Partial) { + if (isNullOrUndefined(source)) { + return; + } + + // TODO: Rework this into an actual generic function for use elsewhere + for (const [key, value] of Object.entries(source)) { + if (isNullOrUndefined(value) && this.hasOwnProperty(key)) { + continue; + } + + if (key === "moveset") { + this.moveset = value?.map((m: any) => PokemonMove.loadMove(m)); + continue; + } + + if (key === "tags") { + // load battler tags + this.tags = value.map((t: BattlerTag) => loadBattlerTag(t)); + continue; + } + this[key] = value; + } + } +} +/** + * Illusion property + */ +export interface IllusionData { + basePokemon: { + /** The actual name of the Pokemon */ + name: string; + /** The actual nickname of the Pokemon */ + nickname: string; + /** Whether the base pokemon is shiny or not */ + shiny: boolean; + /** The shiny variant of the base pokemon */ + variant: Variant; + /** Whether the fusion species of the base pokemon is shiny or not */ + fusionShiny: boolean; + /** The variant of the fusion species of the base pokemon */ + fusionVariant: Variant; + }; + /** The species of the illusion */ + species: Species; + /** The formIndex of the illusion */ + formIndex: number; + /** The gender of the illusion */ + gender: Gender; + /** The pokeball of the illusion */ + pokeball: PokeballType; + /** The fusion species of the illusion if it's a fusion */ + fusionSpecies?: PokemonSpecies; + /** The fusionFormIndex of the illusion */ + fusionFormIndex?: number; + /** The fusionGender of the illusion if it's a fusion */ + fusionGender?: Gender; + /** The level of the illusion (not used currently) */ + level?: number; +} +// TODO: Merge this inside `summmonData` but exclude from save if/when a save data serializer is added + +export class PokemonTempSummonData { + /** + * The number of turns this pokemon has spent without switching out. + * Only currently used for positioning the battle cursor. + */ + turnCount = 1; + + /** + * The number of turns this pokemon has spent in the active position since the start of the wave + * without switching out. + * Reset on switch and new wave, but not stored in `SummonData` to avoid being written to the save file. + + * Used to evaluate "first turn only" conditions such as + * {@linkcode Moves.FAKE_OUT | Fake Out} and {@linkcode Moves.FIRST_IMPRESSION | First Impression}). + */ + waveTurnCount = 1; +} +/** + * Persistent data for a {@linkcode Pokemon}. + * Resets at the start of a new battle (but not on switch). + */ + +export class PokemonBattleData { + /** Counter tracking direct hits this Pokemon has received during this battle; used for {@linkcode Moves.RAGE_FIST} */ + public hitCount = 0; + /** Whether this Pokemon has eaten a berry this battle; used for {@linkcode Moves.BELCH} */ + public hasEatenBerry = false; + /** Array containing all berries eaten and not yet recovered during this current battle; used by {@linkcode Abilities.HARVEST} */ + public berriesEaten: BerryType[] = []; + + constructor(source?: PokemonBattleData | Partial) { + if (!isNullOrUndefined(source)) { + this.hitCount = source.hitCount ?? 0; + this.hasEatenBerry = source.hasEatenBerry ?? false; + this.berriesEaten = source.berriesEaten ?? []; + } + } +} +/** + * Temporary data for a {@linkcode Pokemon}. + * Resets on new wave/battle start (but not on switch). + */ + +export class PokemonWaveData { + /** Whether the pokemon has endured due to a {@linkcode BattlerTagType.ENDURE_TOKEN} */ + public endured = false; + /** + * A set of all the abilities this {@linkcode Pokemon} has used in this wave. + * Used to track once per battle conditions, as well as (hopefully) by the updated AI for move effectiveness. + */ + public abilitiesApplied: Set = new Set(); + /** Whether the pokemon's ability has been revealed or not */ + public abilityRevealed = false; +} +/** + * Temporary data for a {@linkcode Pokemon}. + * Resets at the start of a new turn, as well as on switch. + */ + +export class PokemonTurnData { + public flinched = false; + public acted = false; + /** How many times the current move should hit the target(s) */ + public hitCount = 0; + /** + * - `-1` = Calculate how many hits are left + * - `0` = Move is finished + */ + public hitsLeft = -1; + public totalDamageDealt = 0; + public singleHitDamageDealt = 0; + public damageTaken = 0; + public attacksReceived: AttackMoveResult[] = []; + public order: number; + public statStagesIncreased = false; + public statStagesDecreased = false; + public moveEffectiveness: TypeDamageMultiplier | null = null; + public combiningPledge?: Moves; + public switchedInThisTurn = false; + public failedRunAway = false; + public joinedRound = false; + /** + * Used to make sure multi-hits occur properly when the user is + * forced to act again in the same turn + */ + public extraTurns = 0; + /** + * All berries eaten by this pokemon in this turn. + * Saved into {@linkcode PokemonSummonData | SummonData} by {@linkcode Abilities.CUD_CHEW} on turn end. + * @see {@linkcode PokemonSummonData.berriesEatenLast} + */ + public berriesEaten: BerryType[] = []; +} diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 62ec8081c5d..df2179c2980 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -124,7 +124,6 @@ import { TarShotTag, AutotomizedTag, PowerTrickTag, - loadBattlerTag, type GrudgeTag, } from "../data/battler-tags"; import { WeatherType } from "#enums/weather-type"; @@ -260,6 +259,7 @@ import { MoveFlags } from "#enums/MoveFlags"; import { timedEventManager } from "#app/global-event-manager"; import { loadMoveAnimations } from "#app/sprites/pokemon-asset-loader"; import { ResetStatusPhase } from "#app/phases/reset-status-phase"; +import { PokemonBattleData, PokemonSummonData, PokemonTempSummonData, PokemonWaveData, PokemonTurnData, type IllusionData } from "../@types/pokemon-data"; export enum LearnMoveSituation { MISC, @@ -3719,7 +3719,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // Status moves remain unchanged on weight, this encourages 1-2 movePool = baseWeights .filter(m => !this.moveset.some( - mo => + mo => m[0] === mo.moveId || (allMoves[m[0]].hasAttr(SacrificialAttr) && mo.getMove().hasAttr(SacrificialAttr)) // Only one self-KO move allowed )) @@ -3754,7 +3754,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } else { // Non-trainer pokemon just use normal weights movePool = baseWeights.filter(m => !this.moveset.some( - mo => + mo => m[0] === mo.moveId || (allMoves[m[0]].hasAttr(SacrificialAttr) && mo.getMove().hasAttr(SacrificialAttr)) // Only one self-KO move allowed )); @@ -7756,42 +7756,6 @@ export class EnemyPokemon extends Pokemon { } } -/** - * Illusion property - */ -interface IllusionData { - basePokemon: { - /** The actual name of the Pokemon */ - name: string; - /** The actual nickname of the Pokemon */ - nickname: string; - /** Whether the base pokemon is shiny or not */ - shiny: boolean; - /** The shiny variant of the base pokemon */ - variant: Variant; - /** Whether the fusion species of the base pokemon is shiny or not */ - fusionShiny: boolean; - /** The variant of the fusion species of the base pokemon */ - fusionVariant: Variant; - }; - /** The species of the illusion */ - species: Species; - /** The formIndex of the illusion */ - formIndex: number; - /** The gender of the illusion */ - gender: Gender; - /** The pokeball of the illusion */ - pokeball: PokeballType; - /** The fusion species of the illusion if it's a fusion */ - fusionSpecies?: PokemonSpecies; - /** The fusionFormIndex of the illusion */ - fusionFormIndex?: number; - /** The fusionGender of the illusion if it's a fusion */ - fusionGender?: Gender; - /** The level of the illusion (not used currently) */ - level?: number -} - export interface TurnMove { move: Moves; targets: BattlerIndex[]; @@ -7810,167 +7774,6 @@ export interface AttackMoveResult { sourceBattlerIndex: BattlerIndex; } -/** - * Persistent in-battle data for a {@linkcode Pokemon}. - * Resets on switch or new battle. - */ -export class PokemonSummonData { - /** [Atk, Def, SpAtk, SpDef, Spd, Acc, Eva] */ - public statStages: number[] = [0, 0, 0, 0, 0, 0, 0]; - public moveQueue: TurnMove[] = []; - public tags: BattlerTag[] = []; - public abilitySuppressed = false; - - // Overrides for transform. - // TODO: Move these into a separate class & add rage fist hit count - public speciesForm: PokemonSpeciesForm | null = null; - public fusionSpeciesForm: PokemonSpeciesForm | null = null; - public ability: Abilities | undefined; - public passiveAbility: Abilities | undefined; - public gender: Gender | undefined; - public fusionGender: Gender | undefined; - public stats: number[] = [0, 0, 0, 0, 0, 0]; - public moveset: PokemonMove[] | null; - - // If not initialized this value will not be populated from save data. - public types: PokemonType[] = []; - public addedType: PokemonType | null = null; - - /** Data pertaining to this pokemon's illusion. */ - public illusion: IllusionData | null = null; - public illusionBroken: boolean = false; - - /** Array containing all berries eaten in the last turn; used by {@linkcode Abilities.CUD_CHEW} */ - public berriesEatenLast: BerryType[] = []; - - /** - * An array of all moves this pokemon has used since entering the battle. - * Used for most moves and abilities that check prior move usage or copy already-used moves. - */ - public moveHistory: TurnMove[] = []; - - constructor(source?: PokemonSummonData | Partial) { - if (isNullOrUndefined(source)) { - return; - } - - // TODO: Rework this into an actual generic function for use elsewhere - for (const [key, value] of Object.entries(source)) { - if (isNullOrUndefined(value) && this.hasOwnProperty(key)) { - continue; - } - - if (key === "moveset") { - this.moveset = value?.map((m: any) => PokemonMove.loadMove(m)); - continue; - } - - if (key === "tags") { - // load battler tags - this.tags = value.map((t: BattlerTag) => loadBattlerTag(t)); - continue; - } - this[key] = value; - } - } -} - - // TODO: Merge this inside `summmonData` but exclude from save if/when a save data serializer is added -export class PokemonTempSummonData { - /** - * The number of turns this pokemon has spent without switching out. - * Only currently used for positioning the battle cursor. - */ - turnCount: number = 1; - - /** - * The number of turns this pokemon has spent in the active position since the start of the wave - * without switching out. - * Reset on switch and new wave, but not stored in `SummonData` to avoid being written to the save file. - - * Used to evaluate "first turn only" conditions such as - * {@linkcode Moves.FAKE_OUT | Fake Out} and {@linkcode Moves.FIRST_IMPRESSION | First Impression}). - */ - waveTurnCount = 1; - -} - -/** - * Persistent data for a {@linkcode Pokemon}. - * Resets at the start of a new battle (but not on switch). - */ -export class PokemonBattleData { - /** Counter tracking direct hits this Pokemon has received during this battle; used for {@linkcode Moves.RAGE_FIST} */ - public hitCount = 0; - /** Whether this Pokemon has eaten a berry this battle; used for {@linkcode Moves.BELCH} */ - public hasEatenBerry: boolean = false; - /** Array containing all berries eaten and not yet recovered during this current battle; used by {@linkcode Abilities.HARVEST} */ - public berriesEaten: BerryType[] = []; - - constructor(source?: PokemonBattleData | Partial) { - if (!isNullOrUndefined(source)) { - this.hitCount = source.hitCount ?? 0; - this.hasEatenBerry = source.hasEatenBerry ?? false; - this.berriesEaten = source.berriesEaten ?? []; - } - } -} - -/** - * Temporary data for a {@linkcode Pokemon}. - * Resets on new wave/battle start (but not on switch). - */ -export class PokemonWaveData { - /** Whether the pokemon has endured due to a {@linkcode BattlerTagType.ENDURE_TOKEN} */ - public endured = false; - /** - * A set of all the abilities this {@linkcode Pokemon} has used in this wave. - * Used to track once per battle conditions, as well as (hopefully) by the updated AI for move effectiveness. - */ - public abilitiesApplied: Set = new Set; - /** Whether the pokemon's ability has been revealed or not */ - public abilityRevealed = false; -} - -/** - * Temporary data for a {@linkcode Pokemon}. - * Resets at the start of a new turn, as well as on switch. - */ -export class PokemonTurnData { - public flinched = false; - public acted = false; - /** How many times the current move should hit the target(s) */ - public hitCount = 0; - /** - * - `-1` = Calculate how many hits are left - * - `0` = Move is finished - */ - public hitsLeft = -1; - public totalDamageDealt = 0; - public singleHitDamageDealt = 0; - public damageTaken = 0; - public attacksReceived: AttackMoveResult[] = []; - public order: number; - public statStagesIncreased = false; - public statStagesDecreased = false; - public moveEffectiveness: TypeDamageMultiplier | null = null; - public combiningPledge?: Moves; - public switchedInThisTurn = false; - public failedRunAway = false; - public joinedRound = false; - /** - * Used to make sure multi-hits occur properly when the user is - * forced to act again in the same turn - */ - public extraTurns = 0; - /** - * All berries eaten by this pokemon in this turn. - * Saved into {@linkcode PokemonSummonData | SummonData} by {@linkcode Abilities.CUD_CHEW} on turn end. - * @see {@linkcode PokemonSummonData.berriesEatenLast} - */ - public berriesEaten: BerryType[] = [] -} - export enum AiType { RANDOM, SMART_RANDOM, diff --git a/src/system/pokemon-data.ts b/src/system/pokemon-data.ts index 00169678ed0..cb45c7c8e50 100644 --- a/src/system/pokemon-data.ts +++ b/src/system/pokemon-data.ts @@ -5,7 +5,8 @@ import { Nature } from "#enums/nature"; import { PokeballType } from "#enums/pokeball"; import { getPokemonSpecies, getPokemonSpeciesForm } from "../data/pokemon-species"; import { Status } from "../data/status-effect"; -import Pokemon, { EnemyPokemon, PokemonBattleData, PokemonMove, PokemonSummonData } from "../field/pokemon"; +import Pokemon, { EnemyPokemon, PokemonMove } from "../field/pokemon"; +import { PokemonBattleData, PokemonSummonData } from "#app/@types/pokemon-data"; import { TrainerSlot } from "#enums/trainer-slot"; import type { Variant } from "#app/sprites/variant"; import type { Biome } from "#enums/biome"; diff --git a/test/battlerTags/stockpiling.test.ts b/test/battlerTags/stockpiling.test.ts index 20fade13d92..a42250b8f6f 100644 --- a/test/battlerTags/stockpiling.test.ts +++ b/test/battlerTags/stockpiling.test.ts @@ -1,6 +1,6 @@ import { StockpilingTag } from "#app/data/battler-tags"; import type Pokemon from "#app/field/pokemon"; -import { PokemonSummonData } from "#app/field/pokemon"; +import { PokemonSummonData } from "#app/@types/pokemon-data"; import * as messages from "#app/messages"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { Stat } from "#enums/stat"; diff --git a/test/battlerTags/substitute.test.ts b/test/battlerTags/substitute.test.ts index 827b9f48f85..53a1d5755b1 100644 --- a/test/battlerTags/substitute.test.ts +++ b/test/battlerTags/substitute.test.ts @@ -1,5 +1,6 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import type { PokemonTurnData, TurnMove } from "#app/field/pokemon"; +import type { TurnMove } from "#app/field/pokemon"; +import type { PokemonTurnData } from "#app/@types/pokemon-data"; import type Pokemon from "#app/field/pokemon"; import { MoveResult } from "#app/field/pokemon"; import type BattleScene from "#app/battle-scene";