diff --git a/src/@types/attack-move-result.ts b/src/@types/attack-move-result.ts new file mode 100644 index 00000000000..c6aedf8096d --- /dev/null +++ b/src/@types/attack-move-result.ts @@ -0,0 +1,12 @@ +import type { BattlerIndex } from "#enums/battler-index"; +import type { DamageResult } from "#app/@types/damage-result"; +import type { MoveId } from "#enums/move-id"; + +export interface AttackMoveResult { + move: MoveId; + result: DamageResult; + damage: number; + critical: boolean; + sourceId: number; + sourceBattlerIndex: BattlerIndex; +} diff --git a/src/@types/damage-result.ts b/src/@types/damage-result.ts new file mode 100644 index 00000000000..b6eb6b82770 --- /dev/null +++ b/src/@types/damage-result.ts @@ -0,0 +1,21 @@ +import type { HitResult } from "#enums/hit-result"; + +/** Union type containing all damage-dealing {@linkcode HitResult}s. */ +export type DamageResult = + | HitResult.EFFECTIVE + | HitResult.SUPER_EFFECTIVE + | HitResult.NOT_VERY_EFFECTIVE + | HitResult.ONE_HIT_KO + | HitResult.CONFUSION + | HitResult.INDIRECT_KO + | HitResult.INDIRECT; + +/** Interface containing the results of a damage calculation for a given move. */ +export interface DamageCalculationResult { + /** `true` if the move was cancelled (thus suppressing "No Effect" messages) */ + cancelled: boolean; + /** The effectiveness of the move */ + result: HitResult; + /** The damage dealt by the move */ + damage: number; +} diff --git a/src/@types/illusion-data.ts b/src/@types/illusion-data.ts new file mode 100644 index 00000000000..98e39af11f5 --- /dev/null +++ b/src/@types/illusion-data.ts @@ -0,0 +1,41 @@ +import type { Gender } from "#app/data/gender"; +import type PokemonSpecies from "#app/data/pokemon-species"; +import type { Variant } from "#app/sprites/variant"; +import type { PokeballType } from "#enums/pokeball"; +import type { SpeciesId } from "#enums/species-id"; + +/** + * Data pertaining to a Pokemon's Illusion. + */ +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: SpeciesId; + /** 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; +} diff --git a/src/@types/turn-move.ts b/src/@types/turn-move.ts new file mode 100644 index 00000000000..a5a69eb3749 --- /dev/null +++ b/src/@types/turn-move.ts @@ -0,0 +1,13 @@ +import type { BattlerIndex } from "#enums/battler-index"; +import type { MoveId } from "#enums/move-id"; +import type { MoveResult } from "#enums/move-result"; +import type { MoveUseMode } from "#enums/move-use-mode"; + +/** A record of a move having been used. */ +export interface TurnMove { + move: MoveId; + targets: BattlerIndex[]; + useMode: MoveUseMode; + result?: MoveResult; + turn?: number; +} diff --git a/src/battle.ts b/src/battle.ts index 245705f4801..e33efa21090 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -17,7 +17,8 @@ import { MoneyMultiplierModifier, type PokemonHeldItemModifier } from "./modifie import type { PokeballType } from "#enums/pokeball"; import { trainerConfigs } from "#app/data/trainers/trainer-config"; import { SpeciesFormKey } from "#enums/species-form-key"; -import type { EnemyPokemon, PlayerPokemon, TurnMove } from "#app/field/pokemon"; +import type { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; +import type { TurnMove } from "./@types/turn-move"; import type Pokemon from "#app/field/pokemon"; import { ArenaTagType } from "#enums/arena-tag-type"; import { BattleSpec } from "#enums/battle-spec"; diff --git a/src/data/custom-pokemon-data.ts b/src/data/custom-pokemon-data.ts deleted file mode 100644 index 252e302ccf3..00000000000 --- a/src/data/custom-pokemon-data.ts +++ /dev/null @@ -1,31 +0,0 @@ -import type { AbilityId } from "#enums/ability-id"; -import type { PokemonType } from "#enums/pokemon-type"; -import type { Nature } from "#enums/nature"; - -/** - * Data that can customize a Pokemon in non-standard ways from its Species. - * Includes abilities, nature, changed types, etc. - */ -export class CustomPokemonData { - // TODO: Change the default value for all these from -1 to something a bit more sensible - /** - * The scale at which to render this Pokemon's sprite. - */ - public spriteScale = -1; - public ability: AbilityId | -1; - public passive: AbilityId | -1; - public nature: Nature | -1; - public types: PokemonType[]; - /** Deprecated but needed for session save migration */ - // TODO: Remove this once pre-session migration is implemented - public hitsRecCount: number | null = null; - - constructor(data?: CustomPokemonData | Partial) { - this.spriteScale = data?.spriteScale ?? -1; - this.ability = data?.ability ?? -1; - this.passive = data?.passive ?? -1; - this.nature = data?.nature ?? -1; - this.types = data?.types ?? []; - this.hitsRecCount = data?.hitsRecCount ?? null; - } -} diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index f94c59bb463..e6b9f51ef11 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -13,7 +13,8 @@ import { TypeBoostTag, } from "../battler-tags"; import { getPokemonNameWithAffix } from "../../messages"; -import type { AttackMoveResult, TurnMove } from "../../field/pokemon"; +import type { TurnMove } from "#app/@types/turn-move"; +import type { AttackMoveResult } from "#app/@types/attack-move-result"; import type Pokemon from "../../field/pokemon"; import type { EnemyPokemon } from "#app/field/pokemon"; import { PokemonMove } from "./pokemon-move"; diff --git a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts index 9bdaa603540..ad0597b3839 100644 --- a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts +++ b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts @@ -44,7 +44,7 @@ import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { EncounterBattleAnim } from "#app/data/battle-anims"; import { MoveCategory } from "#enums/MoveCategory"; -import { CustomPokemonData } from "#app/data/custom-pokemon-data"; +import { CustomPokemonData } from "#app/data/pokemon/pokemon-data"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { EncounterAnim } from "#enums/encounter-anims"; import { Challenges } from "#enums/challenges"; diff --git a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts index 4169fd6d7c5..d6a85dee119 100644 --- a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts +++ b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts @@ -29,7 +29,7 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { BerryType } from "#enums/berry-type"; import { Stat } from "#enums/stat"; -import { CustomPokemonData } from "#app/data/custom-pokemon-data"; +import { CustomPokemonData } from "#app/data/pokemon/pokemon-data"; import { randSeedInt } from "#app/utils/common"; import { MoveUseMode } from "#enums/move-use-mode"; diff --git a/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts b/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts index 37d16075543..b853539acf5 100644 --- a/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts @@ -25,7 +25,7 @@ import { BattlerIndex } from "#enums/battler-index"; import { BattlerTagType } from "#enums/battler-tag-type"; import { BerryType } from "#enums/berry-type"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { CustomPokemonData } from "#app/data/custom-pokemon-data"; +import { CustomPokemonData } from "#app/data/pokemon/pokemon-data"; import { Stat } from "#enums/stat"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { MoveUseMode } from "#enums/move-use-mode"; diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index bb74f11ce60..5cb83cd04db 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -43,7 +43,7 @@ import { TrainerSlot } from "#enums/trainer-slot"; import type PokemonSpecies from "#app/data/pokemon-species"; import type { IEggOptions } from "#app/data/egg"; import { Egg } from "#app/data/egg"; -import type { CustomPokemonData } from "#app/data/custom-pokemon-data"; +import type { CustomPokemonData } from "#app/data/pokemon/pokemon-data"; import type HeldModifierConfig from "#app/@types/held-modifier-config"; import type { Variant } from "#app/sprites/variant"; import { StatusEffect } from "#enums/status-effect"; diff --git a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts index f6ac5b0d38b..f3655217b5a 100644 --- a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts @@ -33,7 +33,7 @@ import { modifierTypes } from "#app/data/data-lists"; import { Gender } from "#app/data/gender"; import type { PermanentStat } from "#enums/stat"; import { SummaryUiMode } from "#app/ui/summary-ui-handler"; -import { CustomPokemonData } from "#app/data/custom-pokemon-data"; +import { CustomPokemonData } from "#app/data/pokemon/pokemon-data"; import type { AbilityId } from "#enums/ability-id"; import type { PokeballType } from "#enums/pokeball"; import { StatusEffect } from "#enums/status-effect"; diff --git a/src/data/pokemon/pokemon-data.ts b/src/data/pokemon/pokemon-data.ts new file mode 100644 index 00000000000..db60b4ce1b4 --- /dev/null +++ b/src/data/pokemon/pokemon-data.ts @@ -0,0 +1,208 @@ +import { type BattlerTag, loadBattlerTag } from "#app/data/battler-tags"; +import type { Gender } from "#app/data/gender"; +import type { PokemonSpeciesForm } from "#app/data/pokemon-species"; +import type { TypeDamageMultiplier } from "#app/data/type"; +import { isNullOrUndefined } from "#app/utils/common"; +import type { AbilityId } from "#enums/ability-id"; +import type { BerryType } from "#enums/berry-type"; +import type { MoveId } from "#enums/move-id"; +import type { PokemonType } from "#enums/pokemon-type"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; +import type { TurnMove } from "#app/@types/turn-move"; +import type { AttackMoveResult } from "#app/@types/attack-move-result"; +import type { Nature } from "#enums/nature"; +import type { IllusionData } from "#app/@types/illusion-data"; + +/** + * Permanent data that can customize a Pokemon in non-standard ways from its Species. + * Includes abilities, nature, changed types, etc. + */ +export class CustomPokemonData { + // TODO: Change the default value for all these from -1 to something a bit more sensible + /** + * The scale at which to render this Pokemon's sprite. + */ + public spriteScale = -1; + public ability: AbilityId | -1; + public passive: AbilityId | -1; + public nature: Nature | -1; + public types: PokemonType[]; + /** Deprecated but needed for session save migration */ + // TODO: Remove this once pre-session migration is implemented + public hitsRecCount: number | null = null; + + constructor(data?: CustomPokemonData | Partial) { + this.spriteScale = data?.spriteScale ?? -1; + this.ability = data?.ability ?? -1; + this.passive = data?.passive ?? -1; + this.nature = data?.nature ?? -1; + this.types = data?.types ?? []; + this.hitsRecCount = data?.hitsRecCount ?? null; + } +} + +/** + * 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]; + /** + * A queue of moves yet to be executed, used by charging, recharging and frenzy moves. + * So long as this array is nonempty, this Pokemon's corresponding `CommandPhase` will be skipped over entirely + * in favor of using the queued move. + * TODO: Clean up a lot of the code surrounding the move queue. + */ + 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: AbilityId | undefined; + public passiveAbility: AbilityId | 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 AbilityId.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 = 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 MoveId.FAKE_OUT | Fake Out} and {@linkcode MoveId.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 MoveId.RAGE_FIST} */ + public hitCount = 0; + /** Whether this Pokemon has eaten a berry this battle; used for {@linkcode MoveId.BELCH} */ + public hasEatenBerry = false; + /** Array containing all berries eaten and not yet recovered during this current battle; used by {@linkcode AbilityId.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 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?: MoveId; + public switchedInThisTurn = false; + public failedRunAway = false; + public joinedRound = false; + /** + * The amount of times this Pokemon has acted again and used a move in the current turn. + * Used to make sure multi-hits occur properly when the user is + * forced to act again in the same turn, and **must be incremented** by any effects that grant extra actions. + */ + public extraTurns = 0; + /** + * All berries eaten by this pokemon in this turn. + * Saved into {@linkcode PokemonSummonData | SummonData} by {@linkcode AbilityId.CUD_CHEW} on turn end. + * @see {@linkcode PokemonSummonData.berriesEatenLast} + */ + public berriesEaten: BerryType[] = []; +} diff --git a/src/field/damage-number-handler.ts b/src/field/damage-number-handler.ts index b8b3ed76e18..24c5e107d15 100644 --- a/src/field/damage-number-handler.ts +++ b/src/field/damage-number-handler.ts @@ -1,5 +1,5 @@ import { TextStyle, addTextObject } from "../ui/text"; -import type { DamageResult } from "./pokemon"; +import type { DamageResult } from "../@types/damage-result"; import type Pokemon from "./pokemon"; import { HitResult } from "#enums/hit-result"; import { formatStat, fixedInt } from "#app/utils/common"; diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index e9cc4f70d70..7776633dcf6 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -100,7 +100,6 @@ import { TarShotTag, AutotomizedTag, PowerTrickTag, - loadBattlerTag, type GrudgeTag, } from "../data/battler-tags"; import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type"; @@ -166,7 +165,14 @@ import { getPokemonNameWithAffix } from "#app/messages"; import { Challenges } from "#enums/challenges"; import { PokemonAnimType } from "#enums/pokemon-anim-type"; import { PLAYER_PARTY_MAX_SIZE } from "#app/constants"; -import { CustomPokemonData } from "#app/data/custom-pokemon-data"; +import { + CustomPokemonData, + PokemonBattleData, + PokemonSummonData, + PokemonTempSummonData, + PokemonTurnData, + PokemonWaveData, +} from "#app/data/pokemon/pokemon-data"; import { SwitchType } from "#enums/switch-type"; import { SpeciesFormKey } from "#enums/species-form-key"; import { getStatusEffectOverlapText } from "#app/data/status-effect"; @@ -187,9 +193,11 @@ import { FieldPosition } from "#enums/field-position"; import { LearnMoveSituation } from "#enums/learn-move-situation"; import { HitResult } from "#enums/hit-result"; import { AiType } from "#enums/ai-type"; -import type { MoveResult } from "#enums/move-result"; import { PokemonMove } from "#app/data/moves/pokemon-move"; import type { AbAttrMap, AbAttrString } from "#app/@types/ability-types"; +import type { IllusionData } from "#app/@types/illusion-data"; +import type { TurnMove } from "#app/@types/turn-move"; +import type { DamageCalculationResult, DamageResult } from "#app/@types/damage-result"; /** Base typeclass for damage parameter methods, used for DRY */ type damageParams = { @@ -6706,241 +6714,3 @@ export class EnemyPokemon extends Pokemon { this.battleInfo.toggleFlyout(visible); } } - -/** - * 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: SpeciesId; - /** 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: MoveId; - targets: BattlerIndex[]; - useMode: MoveUseMode; - result?: MoveResult; - turn?: number; -} - -export interface AttackMoveResult { - move: MoveId; - result: DamageResult; - damage: number; - critical: boolean; - sourceId: number; - 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]; - /** - * A queue of moves yet to be executed, used by charging, recharging and frenzy moves. - * So long as this array is nonempty, this Pokemon's corresponding `CommandPhase` will be skipped over entirely - * in favor of using the queued move. - * TODO: Clean up a lot of the code surrounding the move queue. - */ - 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: AbilityId | undefined; - public passiveAbility: AbilityId | 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 AbilityId.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 = 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 MoveId.FAKE_OUT | Fake Out} and {@linkcode MoveId.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 MoveId.RAGE_FIST} */ - public hitCount = 0; - /** Whether this Pokemon has eaten a berry this battle; used for {@linkcode MoveId.BELCH} */ - public hasEatenBerry = false; - /** Array containing all berries eaten and not yet recovered during this current battle; used by {@linkcode AbilityId.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 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?: MoveId; - public switchedInThisTurn = false; - public failedRunAway = false; - public joinedRound = false; - /** - * The amount of times this Pokemon has acted again and used a move in the current turn. - * Used to make sure multi-hits occur properly when the user is - * forced to act again in the same turn, and **must be incremented** by any effects that grant extra actions. - */ - public extraTurns = 0; - /** - * All berries eaten by this pokemon in this turn. - * Saved into {@linkcode PokemonSummonData | SummonData} by {@linkcode AbilityId.CUD_CHEW} on turn end. - * @see {@linkcode PokemonSummonData.berriesEatenLast} - */ - public berriesEaten: BerryType[] = []; -} - -export type DamageResult = - | HitResult.EFFECTIVE - | HitResult.SUPER_EFFECTIVE - | HitResult.NOT_VERY_EFFECTIVE - | HitResult.ONE_HIT_KO - | HitResult.CONFUSION - | HitResult.INDIRECT_KO - | HitResult.INDIRECT; - -/** Interface containing the results of a damage calculation for a given move */ -export interface DamageCalculationResult { - /** `true` if the move was cancelled (thus suppressing "No Effect" messages) */ - cancelled: boolean; - /** The effectiveness of the move */ - result: HitResult; - /** The damage dealt by the move */ - damage: number; -} diff --git a/src/phases/command-phase.ts b/src/phases/command-phase.ts index 754d54de70a..8281019b3c4 100644 --- a/src/phases/command-phase.ts +++ b/src/phases/command-phase.ts @@ -11,7 +11,8 @@ import { BattlerTagType } from "#app/enums/battler-tag-type"; import { BiomeId } from "#enums/biome-id"; import { MoveId } from "#enums/move-id"; import { PokeballType } from "#enums/pokeball"; -import type { PlayerPokemon, TurnMove } from "#app/field/pokemon"; +import type { PlayerPokemon } from "#app/field/pokemon"; +import type { TurnMove } from "#app/@types/turn-move"; import { FieldPosition } from "#enums/field-position"; import { getPokemonNameWithAffix } from "#app/messages"; import { Command } from "#enums/command"; diff --git a/src/phases/damage-anim-phase.ts b/src/phases/damage-anim-phase.ts index aa5a0a6c3e6..8dc377cdaf5 100644 --- a/src/phases/damage-anim-phase.ts +++ b/src/phases/damage-anim-phase.ts @@ -1,7 +1,7 @@ import { globalScene } from "#app/global-scene"; import type { BattlerIndex } from "#enums/battler-index"; import { BattleSpec } from "#enums/battle-spec"; -import type { DamageResult } from "#app/field/pokemon"; +import type { DamageResult } from "#app/@types/damage-result"; import { HitResult } from "#enums/hit-result"; import { fixedInt } from "#app/utils/common"; import { PokemonPhase } from "#app/phases/pokemon-phase"; diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index d7da1ab996c..ecb46bc8efd 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -27,7 +27,8 @@ import { MoveTarget } from "#enums/MoveTarget"; import { MoveCategory } from "#enums/MoveCategory"; import { SpeciesFormChangePostMoveTrigger } from "#app/data/pokemon-forms/form-change-triggers"; import { PokemonType } from "#enums/pokemon-type"; -import type { DamageResult, TurnMove } from "#app/field/pokemon"; +import type { DamageResult } from "#app/@types/damage-result"; +import type { TurnMove } from "#app/@types/turn-move"; import { PokemonMove } from "#app/data/moves/pokemon-move"; import type Pokemon from "#app/field/pokemon"; import { MoveResult } from "#enums/move-result"; diff --git a/src/system/pokemon-data.ts b/src/system/pokemon-data.ts index 050da57e0be..85f7b12a2a5 100644 --- a/src/system/pokemon-data.ts +++ b/src/system/pokemon-data.ts @@ -6,14 +6,14 @@ import { PokeballType } from "#enums/pokeball"; import { getPokemonSpeciesForm } from "#app/data/pokemon-species"; import { getPokemonSpecies } from "#app/utils/pokemon-utils"; import { Status } from "../data/status-effect"; -import Pokemon, { EnemyPokemon, PokemonBattleData, PokemonSummonData } from "../field/pokemon"; +import Pokemon, { EnemyPokemon } from "../field/pokemon"; import { PokemonMove } from "#app/data/moves/pokemon-move"; import { TrainerSlot } from "#enums/trainer-slot"; import type { Variant } from "#app/sprites/variant"; import type { BiomeId } from "#enums/biome-id"; import type { MoveId } from "#enums/move-id"; import type { SpeciesId } from "#enums/species-id"; -import { CustomPokemonData } from "#app/data/custom-pokemon-data"; +import { CustomPokemonData, PokemonBattleData, PokemonSummonData } from "#app/data/pokemon/pokemon-data"; import type { PokemonType } from "#enums/pokemon-type"; export default class PokemonData { diff --git a/src/system/version_migration/versions/v1_0_4.ts b/src/system/version_migration/versions/v1_0_4.ts index a08327a3b70..eacb4f7da1a 100644 --- a/src/system/version_migration/versions/v1_0_4.ts +++ b/src/system/version_migration/versions/v1_0_4.ts @@ -4,7 +4,7 @@ import { defaultStarterSpecies } from "#app/constants"; import { AbilityAttr } from "#enums/ability-attr"; import { DexAttr } from "#enums/dex-attr"; import { allSpecies } from "#app/data/data-lists"; -import { CustomPokemonData } from "#app/data/custom-pokemon-data"; +import { CustomPokemonData } from "#app/data/pokemon/pokemon-data"; import { isNullOrUndefined } from "#app/utils/common"; import type { SystemSaveMigrator } from "#app/@types/SystemSaveMigrator"; import type { SettingsSaveMigrator } from "#app/@types/SettingsSaveMigrator"; diff --git a/src/system/version_migration/versions/v1_10_0.ts b/src/system/version_migration/versions/v1_10_0.ts index 4d1dedf701e..6ed3253a0c9 100644 --- a/src/system/version_migration/versions/v1_10_0.ts +++ b/src/system/version_migration/versions/v1_10_0.ts @@ -1,6 +1,6 @@ import type { SessionSaveMigrator } from "#app/@types/SessionSaveMigrator"; import type { BattlerIndex } from "#enums/battler-index"; -import type { TurnMove } from "#app/field/pokemon"; +import type { TurnMove } from "#app/@types/turn-move"; import type { MoveResult } from "#enums/move-result"; import type { SessionSaveData } from "#app/system/game-data"; import { MoveUseMode } from "#enums/move-use-mode"; diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index cf6333f4580..60e9e846859 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -1,4 +1,5 @@ -import type { PlayerPokemon, TurnMove } from "#app/field/pokemon"; +import type { PlayerPokemon } from "#app/field/pokemon"; +import type { TurnMove } from "#app/@types/turn-move"; import type { PokemonMove } from "#app/data/moves/pokemon-move"; import type Pokemon from "#app/field/pokemon"; import { MoveResult } from "#enums/move-result"; diff --git a/test/battlerTags/stockpiling.test.ts b/test/battlerTags/stockpiling.test.ts index 37873db9eab..8f524ae7a4d 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/data/pokemon/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 06aedab19f4..55c620bbb40 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/@types/turn-move"; +import type { PokemonTurnData } from "#app/data/pokemon/pokemon-data"; import type Pokemon from "#app/field/pokemon"; import { MoveResult } from "#enums/move-result"; import type BattleScene from "#app/battle-scene"; diff --git a/test/field/pokemon.test.ts b/test/field/pokemon.test.ts index 774d46b18fe..4c7afcc91f5 100644 --- a/test/field/pokemon.test.ts +++ b/test/field/pokemon.test.ts @@ -5,7 +5,7 @@ import { PokeballType } from "#enums/pokeball"; import type BattleScene from "#app/battle-scene"; import { MoveId } from "#enums/move-id"; import { PokemonType } from "#enums/pokemon-type"; -import { CustomPokemonData } from "#app/data/custom-pokemon-data"; +import { CustomPokemonData } from "#app/data/pokemon/pokemon-data"; describe("Spec - Pokemon", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/steamroller.test.ts b/test/moves/steamroller.test.ts index 4eb011c47f5..67c63ff32e4 100644 --- a/test/moves/steamroller.test.ts +++ b/test/moves/steamroller.test.ts @@ -1,7 +1,7 @@ import { BattlerIndex } from "#enums/battler-index"; import { allMoves } from "#app/data/data-lists"; import { BattlerTagType } from "#app/enums/battler-tag-type"; -import type { DamageCalculationResult } from "#app/field/pokemon"; +import type { DamageCalculationResult } from "#app/@types/damage-result"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; diff --git a/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts b/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts index 96060e4114c..c1f13d61817 100644 --- a/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts +++ b/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts @@ -24,7 +24,7 @@ import { BerryModifier, PokemonBaseStatTotalModifier } from "#app/modifier/modif import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; -import { CustomPokemonData } from "#app/data/custom-pokemon-data"; +import { CustomPokemonData } from "#app/data/pokemon/pokemon-data"; import { CommandPhase } from "#app/phases/command-phase"; import { MovePhase } from "#app/phases/move-phase"; import { SelectModifierPhase } from "#app/phases/select-modifier-phase";