Fixed constructor (maybe), moved deepCopy and deepMergeSpriteData to own file

`common.ts` is hella bloated so seems legit
This commit is contained in:
Bertie690 2025-04-21 10:42:58 -04:00
parent 169789f25e
commit 2746a658df
11 changed files with 137 additions and 126 deletions

View File

@ -1,4 +1,5 @@
import { BooleanHolder, type NumberHolder, randSeedItem, deepCopy } from "#app/utils/common";
import { BooleanHolder, type NumberHolder, randSeedItem } from "#app/utils/common";
import { deepCopy } from "#app/utils/data";
import i18next from "i18next";
import type { DexAttrProps, GameData } from "#app/system/game-data";
import { defaultStarterSpecies } from "#app/system/game-data";

View File

@ -1,4 +1,5 @@
export enum Biome {
// TODO: Should -1 be part of the enum signature (for "unknown place")
TOWN,
PLAINS,
GRASS,

View File

@ -819,7 +819,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
loadPromises.push(loadMoveAnimations(this.getMoveset().map(m => m.getMove().id)));
// Load the assets for the species form
const formIndex = !!this.summonData.illusion && useIllusion ? this.summonData.illusion.formIndex : this.formIndex;
const formIndex = useIllusion && this.summonData.illusion ? this.summonData.illusion.formIndex : this.formIndex;
loadPromises.push(
this.getSpeciesForm(false, useIllusion).loadAssets(
this.getGender(useIllusion) === Gender.FEMALE,
@ -836,9 +836,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
);
}
if (this.getFusionSpeciesForm()) {
const fusionFormIndex = !!this.summonData.illusion && useIllusion ? this.summonData.illusion.fusionFormIndex : this.fusionFormIndex;
const fusionShiny = !!this.summonData.illusion && !useIllusion ? this.summonData.illusion.basePokemon!.fusionShiny : this.fusionShiny;
const fusionVariant = !!this.summonData.illusion && !useIllusion ? this.summonData.illusion.basePokemon!.fusionVariant : this.fusionVariant;
// TODO: why is fusionFormIndex using illusion if defined but fusionShiny/variant aren't?
const fusionFormIndex = useIllusion && this.summonData.illusion ? this.summonData.illusion.fusionFormIndex : this.fusionFormIndex;
const fusionShiny = !useIllusion && this.summonData.illusion?.basePokemon ? this.summonData.illusion.basePokemon.fusionShiny : this.fusionShiny;
const fusionVariant = !useIllusion && this.summonData.illusion?.basePokemon ? this.summonData.illusion.basePokemon.fusionVariant : this.fusionVariant;
loadPromises.push(this.getFusionSpeciesForm(false, useIllusion).loadAssets(
this.getFusionGender(false, useIllusion) === Gender.FEMALE,
fusionFormIndex,
@ -1006,7 +1007,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}
getSpriteId(ignoreOverride?: boolean): string {
const formIndex: integer = !!this.summonData.illusion ? this.summonData.illusion.formIndex! : this.formIndex;
const formIndex = this.summonData.illusion?.formIndex ?? this.formIndex;
return this.getSpeciesForm(ignoreOverride, true).getSpriteId(
this.getGender(ignoreOverride, true) === Gender.FEMALE,
formIndex,
@ -1020,7 +1021,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
back = this.isPlayer();
}
const formIndex: integer = !!this.summonData.illusion ? this.summonData.illusion.formIndex! : this.formIndex;
const formIndex = this.summonData.illusion?.formIndex ?? this.formIndex;
return this.getSpeciesForm(ignoreOverride, true).getSpriteId(
this.getGender(ignoreOverride, true) === Gender.FEMALE,
@ -1045,7 +1046,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}
getFusionSpriteId(ignoreOverride?: boolean): string {
const fusionFormIndex: integer = !!this.summonData.illusion ? this.summonData.illusion.fusionFormIndex! : this.fusionFormIndex;
const fusionFormIndex = this.summonData.illusion?.fusionFormIndex ?? this.fusionFormIndex;
return this.getFusionSpeciesForm(ignoreOverride, true).getSpriteId(
this.getFusionGender(ignoreOverride, true) === Gender.FEMALE,
fusionFormIndex,
@ -1059,7 +1060,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
back = this.isPlayer();
}
const fusionFormIndex: integer = !!this.summonData.illusion ? this.summonData.illusion.fusionFormIndex! : this.fusionFormIndex;
const fusionFormIndex = this.summonData.illusion?.fusionFormIndex ?? this.fusionFormIndex;
return this.getFusionSpeciesForm(ignoreOverride, true).getSpriteId(
this.getFusionGender(ignoreOverride, true) === Gender.FEMALE,
@ -1085,7 +1086,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}
getIconAtlasKey(ignoreOverride?: boolean): string {
const formIndex: integer = !!this.summonData.illusion ? this.summonData.illusion.formIndex : this.formIndex;
const formIndex = this.summonData.illusion?.formIndex ?? this.formIndex;
return this.getSpeciesForm(ignoreOverride, true).getIconAtlasKey(
formIndex,
this.shiny,
@ -1102,7 +1103,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}
getIconId(ignoreOverride?: boolean): string {
const formIndex: integer = !!this.summonData.illusion ? this.summonData.illusion.formIndex : this.formIndex;
const formIndex = this.summonData.illusion?.formIndex ?? this.formIndex;
return this.getSpeciesForm(ignoreOverride, true).getIconId(
this.getGender(ignoreOverride, true) === Gender.FEMALE,
formIndex,
@ -1112,7 +1113,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}
getFusionIconId(ignoreOverride?: boolean): string {
const fusionFormIndex: integer = !!this.summonData.illusion ? this.summonData.illusion.fusionFormIndex! : this.fusionFormIndex;
const fusionFormIndex = this.summonData.illusion?.fusionFormIndex ?? this.fusionFormIndex;
return this.getFusionSpeciesForm(ignoreOverride, true).getIconId(
this.getFusionGender(ignoreOverride, true) === Gender.FEMALE,
fusionFormIndex,
@ -1128,7 +1129,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
*/
getSpeciesForm(ignoreOverride: boolean = false, useIllusion: boolean = false): PokemonSpeciesForm {
const species: PokemonSpecies = useIllusion && this.summonData.illusion ? getPokemonSpecies(this.summonData.illusion.species) : this.species;
const formIndex: integer = useIllusion && this.summonData.illusion ? this.summonData.illusion.formIndex : this.formIndex;
const formIndex = useIllusion && this.summonData.illusion ? this.summonData.illusion.formIndex : this.formIndex;
if (!ignoreOverride && this.summonData.speciesForm) {
return this.summonData.speciesForm;
@ -1145,10 +1146,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* @param {boolean} useIllusion - Whether we want the fusionSpeciesForm of the illusion or not.
*/
getFusionSpeciesForm(ignoreOverride?: boolean, useIllusion: boolean = false): PokemonSpeciesForm {
const fusionSpecies: PokemonSpecies = useIllusion && !!this.summonData.illusion ? this.summonData.illusion.fusionSpecies! : this.fusionSpecies!;
const fusionFormIndex: integer = useIllusion && !!this.summonData.illusion ? this.summonData.illusion.fusionFormIndex! : this.fusionFormIndex;
const fusionSpecies: PokemonSpecies = useIllusion && this.summonData.illusion ? this.summonData.illusion.fusionSpecies! : this.fusionSpecies!;
const fusionFormIndex = useIllusion && this.summonData.illusion ? this.summonData.illusion.fusionFormIndex! : this.fusionFormIndex;
if (!ignoreOverride && this.summonData.speciesForm) {
if (!ignoreOverride && this.summonData.fusionSpeciesForm) {
return this.summonData.fusionSpeciesForm;
}
if (
@ -1817,9 +1818,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* @param {boolean} useIllusion - Whether we want the fake or real gender (illusion ability).
*/
getGender(ignoreOverride?: boolean, useIllusion: boolean = false): Gender {
if (useIllusion && !!this.summonData.illusion) {
return this.summonData.illusion.gender!;
} else if (!ignoreOverride && this.summonData.gender !== undefined) {
if (useIllusion && this.summonData.illusion) {
return this.summonData.illusion.gender;
} else if (!ignoreOverride && !isNullOrUndefined(this.summonData.gender)) {
return this.summonData.gender;
}
return this.gender;
@ -1829,9 +1830,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* @param {boolean} useIllusion - Whether we want the fake or real gender (illusion ability).
*/
getFusionGender(ignoreOverride?: boolean, useIllusion: boolean = false): Gender {
if (useIllusion && !!this.summonData.illusion) {
return this.summonData.illusion.fusionGender!;
} else if (!ignoreOverride && this.summonData.fusionGender !== undefined) {
if (useIllusion && this.summonData.illusion?.fusionGender) {
return this.summonData.illusion.fusionGender;
} else if (!ignoreOverride && !isNullOrUndefined(this.summonData.fusionGender)) {
return this.summonData.fusionGender;
}
return this.fusionGender;
@ -1841,8 +1842,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* @param {boolean} useIllusion - Whether we want the fake or real shininess (illusion ability).
*/
isShiny(useIllusion: boolean = false): boolean {
if (!useIllusion && !!this.summonData.illusion) {
return this.summonData.illusion.basePokemon?.shiny || (!!this.summonData.illusion.fusionSpecies && this.summonData.illusion.basePokemon?.fusionShiny) || false;
if (!useIllusion && this.summonData.illusion) {
return this.summonData.illusion.basePokemon?.shiny || (this.summonData.illusion.fusionSpecies && this.summonData.illusion.basePokemon?.fusionShiny) || false;
} else {
return this.shiny || (this.isFusion(useIllusion) && this.fusionShiny);
}
@ -1854,7 +1855,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* @returns `true` if the {@linkcode Pokemon} is shiny and the fusion is shiny as well, `false` otherwise
*/
isDoubleShiny(useIllusion: boolean = false): boolean {
if (!useIllusion && !!this.summonData.illusion) {
if (!useIllusion && this.summonData.illusion?.basePokemon) {
return this.isFusion(false) && this.summonData.illusion.basePokemon.shiny && this.summonData.illusion.basePokemon.fusionShiny;
} else {
return this.isFusion(useIllusion) && this.shiny && this.fusionShiny;
@ -1865,7 +1866,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* @param {boolean} useIllusion - Whether we want the fake or real variant (illusion ability).
*/
getVariant(useIllusion: boolean = false): Variant {
if (!useIllusion && !!this.summonData.illusion) {
if (!useIllusion && this.summonData.illusion) {
return !this.isFusion(false)
? this.summonData.illusion.basePokemon!.variant
: Math.max(this.variant, this.fusionVariant) as Variant;
@ -1878,12 +1879,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
getBaseVariant(doubleShiny: boolean): Variant {
if (doubleShiny) {
return !!this.summonData.illusion
? this.summonData.illusion.basePokemon!.variant
: this.variant;
} else {
return this.getVariant();
return this.summonData.illusion?.basePokemon?.variant ?? this.variant;
}
return this.getVariant();
}
getLuck(): number {
@ -1891,18 +1890,17 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}
isFusion(useIllusion: boolean = false): boolean {
if (useIllusion && !!this.summonData.illusion) {
if (useIllusion && this.summonData.illusion) {
return !!this.summonData.illusion.fusionSpecies;
} else {
return !!this.fusionSpecies;
}
return !!this.fusionSpecies;
}
/**
* @param {boolean} useIllusion - Whether we want the fake name or the real name of the Pokemon (for Illusion ability).
*/
getName(useIllusion: boolean = false): string {
return (!useIllusion && !!this.summonData.illusion && this.summonData.illusion.basePokemon)
return (!useIllusion && this.summonData.illusion?.basePokemon)
? this.summonData.illusion.basePokemon.name
: this.name;
}
@ -7887,8 +7885,8 @@ export class PokemonSummonData {
public moveQueue: TurnMove[] = [];
public tags: BattlerTag[] = [];
public abilitySuppressed = false;
public speciesForm: PokemonSpeciesForm | null;
public fusionSpeciesForm: PokemonSpeciesForm;
public speciesForm: PokemonSpeciesForm | null = null;
public fusionSpeciesForm: PokemonSpeciesForm | null = null;
public ability: Abilities = Abilities.NONE;
public passiveAbility: Abilities = Abilities.NONE;
public gender: Gender;
@ -7913,12 +7911,18 @@ export class PokemonSummonData {
public waveTurnCount = 1;
/** The list of moves the pokemon has used since entering the battle */
public moveHistory: TurnMove[] = [];
constructor(source?: PokemonSummonData | Partial<PokemonSummonData>) {
if (!isNullOrUndefined(source)) {
Object.assign(this, source)
}
}
}
/**
Persistent data for a {@linkcode Pokemon}.
Resets at the start of a new battle (but not on switch).
*/
* Persistent data for a {@linkcode Pokemon}.
* Resets at the start of a new battle (but not on switch).
*/
export class PokemonBattleData {
/** counts the hits the pokemon received during this battle; used for {@linkcode Moves.RAGE_FIST} */
public hitCount = 0;
@ -7926,20 +7930,29 @@ export class PokemonBattleData {
public hasEatenBerry: boolean = false;
/** A list of all berries eaten in this current battle; used by {@linkcode Abilities.HARVEST} */
public berriesEaten: BerryType[] = [];
constructor(source?: PokemonBattleData | Partial<PokemonBattleData>) {
if (!isNullOrUndefined(source)) {
this.hitCount = source.hitCount ?? 0;
this.hasEatenBerry = source.hasEatenBerry ?? this.hasEatenBerry;
this.berriesEaten = source.berriesEaten ?? this.berriesEaten;
}
}
}
/**
Temporary data for a {@linkcode Pokemon}.
Resets on new wave.
*/
* Temporary data for a {@linkcode Pokemon}.
* Resets on new wave/battle start.
*/
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.
*/
* 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<Abilities> = new Set<Abilities>;
/** Whether the pokemon's ability has been revealed or not */
public abilityRevealed = false;
}

View File

@ -1,5 +1,6 @@
import Phaser from "phaser";
import { deepCopy, getEnumValues } from "#app/utils/common";
import { getEnumValues } from "#app/utils/common";
import { deepCopy } from "#app/utils/data";
import pad_generic from "./configs/inputs/pad_generic";
import pad_unlicensedSNES from "./configs/inputs/pad_unlicensedSNES";
import pad_xbox360 from "./configs/inputs/pad_xbox360";

View File

@ -280,6 +280,7 @@ export class EncounterPhase extends BattlePhase {
});
if (!this.loaded && battle.battleType !== BattleType.MYSTERY_ENCOUNTER) {
// generate modifiers for MEs, overriding prior ones as applicable
regenerateModifierPoolThresholds(
globalScene.getEnemyField(),
battle.battleType === BattleType.TRAINER ? ModifierPoolType.TRAINER : ModifierPoolType.WILD,
@ -292,8 +293,8 @@ export class EncounterPhase extends BattlePhase {
}
}
if (battle.battleType === BattleType.TRAINER) {
globalScene.currentBattle.trainer!.genAI(globalScene.getEnemyParty());
if (battle.battleType === BattleType.TRAINER && globalScene.currentBattle.trainer) {
globalScene.currentBattle.trainer.genAI(globalScene.getEnemyParty());
}
globalScene.ui.setMode(UiMode.MESSAGE).then(() => {
@ -336,7 +337,7 @@ export class EncounterPhase extends BattlePhase {
for (const pokemon of globalScene.getPlayerParty()) {
// Only reset wave data, not battle data
if (pokemon) {
pokemon.resetWaveData();
pokemon.resetBattleAndWaveData();
}
}

View File

@ -4,17 +4,12 @@ import type { Gender } from "../data/gender";
import { Nature } from "#enums/nature";
import type { PokeballType } from "#enums/pokeball";
import { getPokemonSpecies, getPokemonSpeciesForm } from "../data/pokemon-species";
import { Status } from "../data/status-effect";
import Pokemon, {
EnemyPokemon,
type PokemonMove,
type PokemonSummonData,
type PokemonBattleData,
} from "../field/pokemon";
import type { Status } from "../data/status-effect";
import Pokemon, { EnemyPokemon, PokemonBattleData, PokemonMove, PokemonSummonData } from "../field/pokemon";
import { TrainerSlot } from "#enums/trainer-slot";
import type { Variant } from "#app/sprites/variant";
import type { Biome } from "#enums/biome";
import type { Moves } from "#enums/moves";
import { Moves } from "#enums/moves";
import type { Species } from "#enums/species";
import { CustomPokemonData } from "#app/data/custom-pokemon-data";
import type { PokemonType } from "#enums/pokemon-type";
@ -67,8 +62,8 @@ export default class PokemonData {
public bossSegments?: number;
// Effects that need to be preserved between waves
public summonData: PokemonSummonData;
public battleData: PokemonBattleData;
public summonData: PokemonSummonData = new PokemonSummonData();
public battleData: PokemonBattleData = new PokemonBattleData();
public summonDataSpeciesFormIndex: number;
public customPokemonData: CustomPokemonData;
@ -79,14 +74,14 @@ export default class PokemonData {
* or JSON representation thereof.
* @param source The {@linkcode Pokemon} to convert into data (or a JSON object representing one)
*/
// TODO: Remove any from type signature
// TODO: Remove any from type signature in favor of 2 separate method funcs
constructor(source: Pokemon | any) {
const sourcePokemon = source instanceof Pokemon ? source : undefined;
this.id = source.id;
this.player = sourcePokemon ? sourcePokemon.isPlayer() : source.player;
this.species = sourcePokemon ? sourcePokemon.species.speciesId : source.species;
this.nickname =
sourcePokemon?.summonData.illusion?.basePokemon.nickname ?? sourcePokemon?.nickname ?? source.nickname;
this.player = sourcePokemon?.isPlayer() ?? source.player;
this.species = sourcePokemon?.species.speciesId ?? source.species;
this.nickname = sourcePokemon?.summonData.illusion?.basePokemon.nickname ?? source.nickname;
this.formIndex = Math.max(Math.min(source.formIndex, getPokemonSpecies(this.species).forms.length - 1), 0);
this.abilityIndex = source.abilityIndex;
this.passive = source.passive;
@ -95,60 +90,49 @@ export default class PokemonData {
this.pokeball = source.pokeball;
this.level = source.level;
this.exp = source.exp;
this.levelExp = source.levelExp;
this.gender = source.gender;
this.hp = source.hp;
this.stats = source.stats;
this.ivs = source.ivs;
// TODO: Can't we move some of this verification stuff to an upgrade script?
this.nature = source.nature ?? Nature.HARDY;
this.moveset = source.moveset ?? [new PokemonMove(Moves.TACKLE), new PokemonMove(Moves.GROWL)];
this.status = source.status ?? null;
this.friendship = source.friendship ?? getPokemonSpecies(this.species).baseFriendship;
this.metLevel = source.metLevel || 5;
this.metBiome = source.metBiome ?? -1;
this.metSpecies = source.metSpecies;
this.metWave = source.metWave ?? (this.metBiome === -1 ? -1 : 0);
this.luck = source.luck ?? (source.shiny ? source.variant + 1 : 0);
this.pauseEvolutions = !!source.pauseEvolutions;
this.pokerus = !!source.pokerus;
this.usedTMs = source.usedTMs ?? [];
this.evoCounter = source.evoCounter ?? 0;
this.teraType = source.teraType as PokemonType;
this.isTerastallized = !!source.isTerastallized;
this.stellarTypesBoosted = source.stellarTypesBoosted ?? [];
this.fusionSpecies = sourcePokemon ? sourcePokemon.fusionSpecies?.speciesId : source.fusionSpecies;
this.fusionSpecies = sourcePokemon?.fusionSpecies?.speciesId ?? source.fusionSpecies;
this.fusionFormIndex = source.fusionFormIndex;
this.fusionAbilityIndex = source.fusionAbilityIndex;
this.fusionShiny =
sourcePokemon?.summonData.illusion?.basePokemon.fusionShiny ?? sourcePokemon?.fusionShiny ?? source.fusionShiny;
this.fusionVariant =
sourcePokemon?.summonData.illusion?.basePokemon.fusionVariant ??
sourcePokemon?.fusionVariant ??
source.fusionVariant;
this.fusionShiny = sourcePokemon?.summonData.illusion?.basePokemon.fusionShiny ?? source.fusionShiny;
this.fusionVariant = sourcePokemon?.summonData.illusion?.basePokemon.fusionVariant ?? source.fusionVariant;
this.fusionGender = source.fusionGender;
this.fusionLuck = source.fusionLuck ?? (source.fusionShiny ? source.fusionVariant + 1 : 0);
this.fusionCustomPokemonData = new CustomPokemonData(source.fusionCustomPokemonData);
this.fusionTeraType = (source.fusionTeraType ?? 0) as PokemonType;
this.usedTMs = source.usedTMs ?? [];
this.customPokemonData = new CustomPokemonData(source.customPokemonData);
this.moveset = sourcePokemon?.moveset ?? source.moveset;
this.levelExp = source.levelExp;
this.hp = source.hp;
this.pauseEvolutions = !!source.pauseEvolutions;
this.evoCounter = source.evoCounter ?? 0;
this.boss = (source instanceof EnemyPokemon && !!source.bossSegments) || (!this.player && !!source.boss);
this.bossSegments = source.bossSegments ?? 0;
this.status =
sourcePokemon?.status ??
(source.status
? new Status(source.status.effect, source.status.toxicTurnCount, source.status.sleepTurnsRemaining)
: null);
this.summonData = source.summonData;
this.battleData = source.battleData;
this.summonData = new PokemonSummonData(source.summonData);
this.battleData = new PokemonBattleData(source.battleData);
this.summonDataSpeciesFormIndex =
sourcePokemon?.summonData.speciesForm?.formIndex ?? source.summonDataSpeciesFormIndex;
this.summonDataSpeciesFormIndex = sourcePokemon
? this.getSummonDataSpeciesFormIndex()
: source.summonDataSpeciesFormIndex;
this.customPokemonData = new CustomPokemonData(source.customPokemonData);
this.fusionCustomPokemonData = new CustomPokemonData(source.fusionCustomPokemonData);
}
toPokemon(battleType?: BattleType, partyMemberIndex = 0, double = false): Pokemon {
@ -197,16 +181,4 @@ export default class PokemonData {
}
return ret;
}
/**
* Method to save summon data species form index
* Necessary in case the pokemon is transformed
* to reload the correct form
*/
getSummonDataSpeciesFormIndex(): number {
if (this.summonData.speciesForm) {
return this.summonData.speciesForm.formIndex;
}
return 0;
}
}

View File

@ -1,5 +1,6 @@
import type { SessionSaveMigrator } from "#app/@types/SessionSaveMigrator";
import { loadBattlerTag } from "#app/data/battler-tags";
import { Status } from "#app/data/status-effect";
import { PokemonMove } from "#app/field/pokemon";
import type { SessionSaveData } from "#app/system/game-data";
import PokemonData from "#app/system/pokemon-data";
@ -14,8 +15,14 @@ import { PokeballType } from "#enums/pokeball";
const migratePartyData: SessionSaveMigrator = {
version: "1.9.0",
migrate: (data: SessionSaveData): void => {
// this stuff is copied straight from the constructor fwiw
const mapParty = (pkmnData: PokemonData) => {
// this stuff is copied straight from the constructor fwiw
pkmnData.status &&= new Status(
pkmnData.status.effect,
pkmnData.status.toxicTurnCount,
pkmnData.status.sleepTurnsRemaining,
);
// remove empty moves from moveset
pkmnData.moveset = pkmnData.moveset.filter(m => !!m) ?? [
new PokemonMove(Moves.TACKLE),
new PokemonMove(Moves.GROWL),

View File

@ -467,16 +467,6 @@ export function truncateString(str: string, maxLength = 10) {
return str;
}
/**
* Perform a deep copy of an object.
* @param values - The object to be deep copied.
* @returns A new object that is a deep copy of the input.
*/
export function deepCopy(values: object): object {
// Convert the object to a JSON string and parse it back to an object to perform a deep copy
return JSON.parse(JSON.stringify(values));
}
/**
* Convert a space-separated string into a capitalized and underscored string.
* @param input - The string to be converted.

View File

@ -1,3 +1,13 @@
/**
* Perform a deep copy of an object.
* @param values - The object to be deep copied.
* @returns A new object that is a deep copy of the input.
*/
export function deepCopy(values: object): object {
// Convert the object to a JSON string and parse it back to an object to perform a deep copy
return JSON.parse(JSON.stringify(values));
}
/**
* Deeply merge two JSON objects' common properties together.
* This copies all values from `source` that match properties inside `dest`,
@ -18,16 +28,17 @@ export function deepMergeSpriteData(dest: object, source: object) {
const sourceVal = source[key];
return (
// Somewhat redundant, but makes it clear that we're explicitly interested in properties that exist in both
// 1st part somewhat redundant, but makes it clear that we're explicitly interested in properties that exist in both
key in source && Array.isArray(sourceVal) === Array.isArray(destVal) && typeof sourceVal === typeof destVal
);
});
for (const key of matchingKeys) {
if (typeof source[key] === "object" && source[key] !== null && !Array.isArray(source[key])) {
deepMergeSpriteData(dest[key], source[key]);
} else {
// Pure objects get recursed into; everything else gets overwritten
if (typeof source[key] !== "object" || source[key] === null || Array.isArray(source[key])) {
dest[key] = source[key];
} else {
deepMergeSpriteData(dest[key], source[key]);
}
}
}

View File

@ -7,6 +7,7 @@ import type Move from "#app/data/moves/move";
import GameManager from "#test/testUtils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { BattleType } from "#enums/battle-type";
describe("Moves - Rage Fist", () => {
let phaserGame: Phaser.Game;
@ -126,18 +127,31 @@ describe("Moves - Rage Fist", () => {
expect(game.scene.getPlayerPokemon()?.battleData.hitCount).toBe(4);
});
it("should reset hits recieved during trainer battles", async () => {
game.override.enemySpecies(Species.MAGIKARP).startingWave(19);
it("should reset hits recieved before trainer battles", async () => {
game.override.enemySpecies(Species.MAGIKARP).moveset(Moves.DOUBLE_IRON_BASH);
await game.classicMode.startBattle([Species.MARSHADOW]);
await game.classicMode.startBattle([Species.MAGIKARP]);
const marshadow = game.scene.getPlayerPokemon()!;
expect(marshadow).toBeDefined();
// beat up a magikarp
game.move.select(Moves.RAGE_FIST);
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.phaseInterceptor.to("TurnEndPhase");
expect(game.isVictory()).toBe(true);
expect(marshadow.battleData.hitCount).toBe(2);
expect(move.calculateBattlePower).toHaveLastReturnedWith(150);
game.override.battleType(BattleType.TRAINER);
await game.toNextWave();
expect(game.scene.lastEnemyTrainer).not.toBeNull();
expect(marshadow.battleData.hitCount).toBe(0);
game.move.select(Moves.RAGE_FIST);
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.phaseInterceptor.to("BerryPhase", false);
await game.phaseInterceptor.to("TurnEndPhase");
expect(move.calculateBattlePower).toHaveLastReturnedWith(150);
});

View File

@ -2,7 +2,7 @@ import cfg_keyboard_qwerty from "#app/configs/inputs/cfg_keyboard_qwerty";
import { getKeyWithKeycode, getKeyWithSettingName } from "#app/configs/inputs/configHandler";
import type { InterfaceConfig } from "#app/inputs-controller";
import { SettingKeyboard } from "#app/system/settings/settings-keyboard";
import { deepCopy } from "#app/utils/common";
import { deepCopy } from "#app/utils/data";
import { Button } from "#enums/buttons";
import { Device } from "#enums/devices";
import { InGameManip } from "#test/settingMenu/helpers/inGameManip";