From ba7be773dcb6901a9181eb2f6511a161d13e22c2 Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Mon, 22 Sep 2025 12:41:40 -0500 Subject: [PATCH] Revert use of typed arrays --- src/@types/common.ts | 3 + src/@types/save-data.ts | 2 +- src/battle-scene.ts | 13 ++- src/data/abilities/ability.ts | 45 +++++---- src/data/moves/move.ts | 11 ++- .../mystery-encounter-requirements.ts | 19 ++-- .../utils/encounter-phase-utils.ts | 2 +- src/data/pokemon-forms.ts | 3 +- .../pokemon-forms/form-change-triggers.ts | 7 +- src/data/pokemon/pokemon-data.ts | 12 +-- .../positional-tags/load-positional-tag.ts | 2 +- src/field/arena.ts | 3 +- src/field/pokemon.ts | 33 ++++--- src/system/game-data.ts | 2 +- src/system/pokemon-data.ts | 6 +- src/ui/containers/stats-container.ts | 2 +- src/ui/handlers/battle-message-ui-handler.ts | 4 +- src/ui/handlers/starter-select-ui-handler.ts | 2 +- src/utils/array.ts | 2 +- src/utils/common.ts | 9 +- test/abilities/beast-boost.test.ts | 6 +- test/battle/battle-order.test.ts | 26 +++--- test/escape-calculations.test.ts | 92 +++++++++++-------- test/moves/rollout.test.ts | 4 +- .../fun-and-games-encounter.test.ts | 2 +- .../encounters/part-timer-encounter.test.ts | 6 +- 26 files changed, 164 insertions(+), 154 deletions(-) diff --git a/src/@types/common.ts b/src/@types/common.ts index 93d88a3b680..4126733397e 100644 --- a/src/@types/common.ts +++ b/src/@types/common.ts @@ -1 +1,4 @@ export type ConditionFn = (args?: any[]) => boolean; + +/** Alias for the constructor of a class */ +export type Constructor = new (...args: unknown[]) => T; diff --git a/src/@types/save-data.ts b/src/@types/save-data.ts index c737f5097ad..ae359c20949 100644 --- a/src/@types/save-data.ts +++ b/src/@types/save-data.ts @@ -123,7 +123,7 @@ export interface Starter { pokerus: boolean; nickname?: string; teraType?: PokemonType; - ivs: Uint8Array; + ivs: number[]; } export type RunHistoryData = Record; diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 225f806d491..6da361dbf2f 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -118,9 +118,9 @@ import type { TrainerData } from "#system/trainer-data"; import type { Voucher } from "#system/voucher"; import { vouchers } from "#system/voucher"; import { trainerConfigs } from "#trainers/trainer-config"; +import type { Constructor } from "#types/common"; import type { HeldModifierConfig } from "#types/held-modifier-config"; import type { Localizable } from "#types/locales"; -import type { ReadonlyUint8Array } from "#types/typed-arrays"; import { AbilityBar } from "#ui/ability-bar"; import { ArenaFlyout } from "#ui/arena-flyout"; import { CandyBar } from "#ui/candy-bar"; @@ -133,7 +133,6 @@ import { UI } from "#ui/ui"; import { addUiThemeOverrides } from "#ui/ui-theme"; import { BooleanHolder, - type Constructor, fixedInt, formatMoney, getIvsFromId, @@ -867,7 +866,7 @@ export class BattleScene extends SceneBase { gender?: Gender, shiny?: boolean, variant?: Variant, - ivs?: ReadonlyUint8Array | number[], + ivs?: number[], nature?: Nature, dataSource?: Pokemon | PokemonData, postProcess?: (playerPokemon: PlayerPokemon) => void, @@ -898,12 +897,12 @@ export class BattleScene extends SceneBase { if (Overrides.IVS_OVERRIDE.some(value => !isBetween(value, 0, 31))) { throw new Error("All IVs in the player IV override must be between 0 and 31!"); } - pokemon.ivs = new Uint8Array(Overrides.IVS_OVERRIDE); + pokemon.ivs = Overrides.IVS_OVERRIDE; } else { if (!isBetween(Overrides.IVS_OVERRIDE, 0, 31)) { throw new Error("The Player IV override must be a value between 0 and 31!"); } - pokemon.ivs = new Uint8Array(6).fill(Overrides.IVS_OVERRIDE); + pokemon.ivs = new Array(6).fill(Overrides.IVS_OVERRIDE); } if (Overrides.NATURE_OVERRIDE !== null) { @@ -963,12 +962,12 @@ export class BattleScene extends SceneBase { if (Overrides.ENEMY_IVS_OVERRIDE.some(value => !isBetween(value, 0, 31))) { throw new Error("All IVs in the enemy IV override must be between 0 and 31!"); } - pokemon.ivs = new Uint8Array(Overrides.ENEMY_IVS_OVERRIDE); + pokemon.ivs = Overrides.ENEMY_IVS_OVERRIDE; } else { if (!isBetween(Overrides.ENEMY_IVS_OVERRIDE, 0, 31)) { throw new Error("The Enemy IV override must be a value between 0 and 31!"); } - pokemon.ivs = new Uint8Array(6).fill(Overrides.ENEMY_IVS_OVERRIDE); + pokemon.ivs = new Array(6).fill(Overrides.ENEMY_IVS_OVERRIDE); } if (Overrides.ENEMY_NATURE_OVERRIDE !== null) { diff --git a/src/data/abilities/ability.ts b/src/data/abilities/ability.ts index 81d35d3a24c..d602cfb9651 100644 --- a/src/data/abilities/ability.ts +++ b/src/data/abilities/ability.ts @@ -62,11 +62,10 @@ import type { PokemonDefendCondition, PokemonStatStageChangeCondition, } from "#types/ability-types"; +import type { Constructor } from "#types/common"; import type { Localizable } from "#types/locales"; import type { Closed, Exact } from "#types/type-helpers"; -import type { GenericUint8Array, ReadonlyGenericInt8Array, ReadonlyGenericUint8Array } from "#types/typed-arrays"; import { coerceArray } from "#utils/array"; -import type { Constructor } from "#utils/common"; import { BooleanHolder, NumberHolder, randSeedFloat, randSeedInt, randSeedItem, toDmgValue } from "#utils/common"; import { toCamelCase } from "#utils/strings"; import i18next from "i18next"; @@ -1211,13 +1210,13 @@ export class PostDefendTerrainChangeAbAttr extends PostDefendAbAttr { export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr { private chance: number; - private readonly effects: ReadonlyGenericUint8Array; + private readonly effects: readonly StatusEffect[]; constructor(chance: number, ...effects: StatusEffect[]) { super(true); this.chance = chance; - this.effects = new Uint8Array(effects); + this.effects = effects; } override canApply({ pokemon, move, opponent: attacker }: PostMoveInteractionAbAttrParams): boolean { @@ -2181,14 +2180,14 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr { export class PostAttackApplyStatusEffectAbAttr extends PostAttackAbAttr { private contactRequired: boolean; private chance: number; - private readonly effects: ReadonlyGenericUint8Array; + private readonly effects: readonly StatusEffect[]; constructor(contactRequired: boolean, chance: number, ...effects: StatusEffect[]) { super(); this.contactRequired = contactRequired; this.chance = chance; - this.effects = new Uint8Array(effects); + this.effects = effects; } override canApply(params: PostMoveInteractionAbAttrParams): boolean { @@ -2941,7 +2940,7 @@ export class PostSummonTerrainChangeAbAttr extends PostSummonAbAttr { * Heals a status effect if the Pokemon is afflicted with it upon switch in (or gain) */ export class PostSummonHealStatusAbAttr extends PostSummonRemoveEffectAbAttr { - private readonly immuneEffects: ReadonlyGenericUint8Array; + private readonly immuneEffects: readonly StatusEffect[]; private statusHealed: StatusEffect; /** @@ -2949,7 +2948,7 @@ export class PostSummonHealStatusAbAttr extends PostSummonRemoveEffectAbAttr { */ constructor(...immuneEffects: StatusEffect[]) { super(); - this.immuneEffects = new Uint8Array(immuneEffects); + this.immuneEffects = immuneEffects; } public override canApply({ pokemon }: AbAttrBaseParams): boolean { @@ -3050,7 +3049,7 @@ export class PostSummonCopyAbilityAbAttr extends PostSummonAbAttr { * Removes supplied status effects from the user's field. */ export class PostSummonUserFieldRemoveStatusEffectAbAttr extends PostSummonAbAttr { - private readonly statusEffect: ReadonlyGenericUint8Array; + private readonly statusEffect: readonly StatusEffect[]; /** * @param statusEffect - The status effects to be removed from the user's field. @@ -3058,7 +3057,7 @@ export class PostSummonUserFieldRemoveStatusEffectAbAttr extends PostSummonAbAtt constructor(...statusEffect: StatusEffect[]) { super(false); - this.statusEffect = new Uint8Array(statusEffect); + this.statusEffect = statusEffect; } override canApply({ pokemon }: AbAttrBaseParams): boolean { @@ -3638,7 +3637,7 @@ export class PreSetStatusAbAttr extends AbAttr { * Provides immunity to status effects to specified targets. */ export class PreSetStatusEffectImmunityAbAttr extends PreSetStatusAbAttr { - protected readonly immuneEffects: ReadonlyGenericUint8Array; + protected readonly immuneEffects: readonly StatusEffect[]; /** * @param immuneEffects - An array of {@linkcode StatusEffect}s to prevent application. @@ -3647,7 +3646,7 @@ export class PreSetStatusEffectImmunityAbAttr extends PreSetStatusAbAttr { constructor(...immuneEffects: StatusEffect[]) { super(); - this.immuneEffects = new Uint8Array(immuneEffects); + this.immuneEffects = immuneEffects; } override canApply({ effect, cancelled }: PreSetStatusAbAttrParams): boolean { @@ -3706,7 +3705,7 @@ export interface UserFieldStatusEffectImmunityAbAttrParams extends AbAttrBasePar */ export class UserFieldStatusEffectImmunityAbAttr extends CancelInteractionAbAttr { private declare readonly _: never; - protected readonly immuneEffects: ReadonlyGenericUint8Array; + protected readonly immuneEffects: readonly StatusEffect[]; /** * @param immuneEffects - An array of {@linkcode StatusEffect}s to prevent application. @@ -3715,7 +3714,7 @@ export class UserFieldStatusEffectImmunityAbAttr extends CancelInteractionAbAttr constructor(...immuneEffects: StatusEffect[]) { super(); - this.immuneEffects = new Uint8Array(immuneEffects); + this.immuneEffects = immuneEffects; } override canApply({ effect, cancelled }: UserFieldStatusEffectImmunityAbAttrParams): boolean { @@ -3999,7 +3998,7 @@ export class BlockNonDirectDamageAbAttr extends CancelInteractionAbAttr { * This attribute will block any status damage that you put in the parameter. */ export class BlockStatusDamageAbAttr extends CancelInteractionAbAttr { - private readonly effects: ReadonlyGenericUint8Array; + private readonly effects: readonly StatusEffect[]; /** * @param effects - The status effect(s) that will be blocked from damaging the ability pokemon @@ -4007,7 +4006,7 @@ export class BlockStatusDamageAbAttr extends CancelInteractionAbAttr { constructor(...effects: StatusEffect[]) { super(false); - this.effects = new Uint8Array(effects); + this.effects = effects; } override canApply({ pokemon, cancelled }: AbAttrParamsWithCancel): boolean { @@ -4540,7 +4539,7 @@ export class PostTurnAbAttr extends AbAttr { * @sealed */ export class PostTurnStatusHealAbAttr extends PostTurnAbAttr { - private readonly effects: GenericUint8Array; + private readonly effects: readonly StatusEffect[]; /** * @param effects - The status effect(s) that will qualify healing the ability pokemon @@ -4548,7 +4547,7 @@ export class PostTurnStatusHealAbAttr extends PostTurnAbAttr { constructor(...effects: StatusEffect[]) { super(false); - this.effects = new Uint8Array(effects); + this.effects = effects; } override canApply({ pokemon }: AbAttrBaseParams): boolean { @@ -5809,14 +5808,14 @@ export interface IgnoreTypeStatusEffectImmunityAbAttrParams extends AbAttrParams * @sealed */ export class IgnoreTypeStatusEffectImmunityAbAttr extends AbAttr { - private readonly statusEffect: ReadonlyGenericUint8Array; - private readonly defenderType: ReadonlyGenericInt8Array; + private readonly statusEffect: readonly StatusEffect[]; + private readonly defenderType: readonly PokemonType[]; - constructor(statusEffect: StatusEffect[], defenderType: PokemonType[]) { + constructor(statusEffect: readonly StatusEffect[], defenderType: readonly PokemonType[]) { super(false); - this.statusEffect = new Uint8Array(statusEffect); - this.defenderType = new Int8Array(defenderType); + this.statusEffect = statusEffect; + this.defenderType = defenderType; } override canApply({ statusEffect, defenderType, cancelled }: IgnoreTypeStatusEffectImmunityAbAttrParams): boolean { diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 3d6886abe2b..03d4efd65db 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -87,7 +87,8 @@ import type { AttackMoveResult } from "#types/attack-move-result"; import type { Localizable } from "#types/locales"; import type { ChargingMove, MoveAttrMap, MoveAttrString, MoveClassMap, MoveKindString, MoveMessageFunc } from "#types/move-types"; import type { TurnMove } from "#types/turn-move"; -import { BooleanHolder, type Constructor, NumberHolder, randSeedFloat, randSeedInt, randSeedItem, toDmgValue } from "#utils/common"; +import { BooleanHolder, NumberHolder, randSeedFloat, randSeedInt, randSeedItem, toDmgValue } from "#utils/common"; +import type { Constructor } from "#types/common"; import { coerceArray } from "#utils/array"; import { getEnumValues } from "#utils/enums"; import { toCamelCase, toTitleCase } from "#utils/strings"; @@ -2596,11 +2597,11 @@ export class StatusEffectAttr extends MoveEffectAttr { * Used for {@linkcode Moves.TRI_ATTACK} and {@linkcode Moves.DIRE_CLAW}. */ export class MultiStatusEffectAttr extends StatusEffectAttr { - public readonly effects: ReadonlyGenericUint8Array; + public readonly effects: readonly StatusEffect[]; constructor(effects: StatusEffect[], selfTarget?: boolean) { super(effects[0], selfTarget); - this.effects = new Uint8Array(effects); + this.effects = effects; } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { @@ -2926,7 +2927,7 @@ export class StealEatBerryAttr extends EatBerryAttr { */ export class HealStatusEffectAttr extends MoveEffectAttr { /** List of Status Effects to cure */ - private readonly effects: ReadonlyGenericUint8Array; + private readonly effects: readonly StatusEffect[]; /** * @param selfTarget - Whether this move targets the user @@ -2934,7 +2935,7 @@ export class HealStatusEffectAttr extends MoveEffectAttr { */ constructor(selfTarget: boolean, effects: StatusEffect | StatusEffect[]) { super(selfTarget, { lastHitOnly: true }); - this.effects = new Uint8Array(coerceArray(effects)); + this.effects = coerceArray(effects); } /** diff --git a/src/data/mystery-encounters/mystery-encounter-requirements.ts b/src/data/mystery-encounters/mystery-encounter-requirements.ts index a752ce428df..218a2e7cbed 100644 --- a/src/data/mystery-encounters/mystery-encounter-requirements.ts +++ b/src/data/mystery-encounters/mystery-encounter-requirements.ts @@ -15,7 +15,6 @@ import { WeatherType } from "#enums/weather-type"; import type { PlayerPokemon } from "#field/pokemon"; import { AttackTypeBoosterModifier } from "#modifiers/modifier"; import type { AttackTypeBoosterModifierType } from "#modifiers/modifier-type"; -import type { ReadonlyGenericUint8Array } from "#types/typed-arrays"; import { coerceArray } from "#utils/array"; export interface EncounterRequirement { @@ -697,15 +696,15 @@ export class AbilityRequirement extends EncounterPokemonRequirement { } export class StatusEffectRequirement extends EncounterPokemonRequirement { - requiredStatusEffect: ReadonlyGenericUint8Array; + requiredStatusEffect: readonly StatusEffect[]; minNumberOfPokemon: number; invertQuery: boolean; - constructor(statusEffect: StatusEffect | StatusEffect[], minNumberOfPokemon = 1, invertQuery = false) { + constructor(statusEffect: StatusEffect | readonly StatusEffect[], minNumberOfPokemon = 1, invertQuery = false) { super(); this.minNumberOfPokemon = minNumberOfPokemon; this.invertQuery = invertQuery; - this.requiredStatusEffect = new Uint8Array(coerceArray(statusEffect)); + this.requiredStatusEffect = coerceArray(statusEffect); } override meetsRequirement(): boolean { @@ -718,7 +717,7 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement { return x; } - override queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] { + override queryParty(partyPokemon: readonly PlayerPokemon[]): PlayerPokemon[] { if (!this.invertQuery) { return partyPokemon.filter(pokemon => { return this.requiredStatusEffect.some(statusEffect => { @@ -762,11 +761,11 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement { * If you want to trigger the event based on the form change enabler, use PersistentModifierRequirement. */ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequirement { - requiredFormChangeItem: FormChangeItem[]; + requiredFormChangeItem: readonly FormChangeItem[]; minNumberOfPokemon: number; invertQuery: boolean; - constructor(formChangeItem: FormChangeItem | FormChangeItem[], minNumberOfPokemon = 1, invertQuery = false) { + constructor(formChangeItem: FormChangeItem | readonly FormChangeItem[], minNumberOfPokemon = 1, invertQuery = false) { super(); this.minNumberOfPokemon = minNumberOfPokemon; this.invertQuery = invertQuery; @@ -793,7 +792,7 @@ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequiremen ); } - override queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] { + override queryParty(partyPokemon: readonly PlayerPokemon[]): PlayerPokemon[] { if (!this.invertQuery) { return partyPokemon.filter( pokemon => @@ -878,13 +877,13 @@ export class HeldItemRequirement extends EncounterPokemonRequirement { } export class AttackTypeBoosterHeldItemTypeRequirement extends EncounterPokemonRequirement { - requiredHeldItemTypes: PokemonType[]; + requiredHeldItemTypes: readonly PokemonType[]; minNumberOfPokemon: number; invertQuery: boolean; requireTransferable: boolean; constructor( - heldItemTypes: PokemonType | PokemonType[], + heldItemTypes: PokemonType | readonly PokemonType[], minNumberOfPokemon = 1, invertQuery = false, requireTransferable = true, diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index 0cf90b0fcb1..58dbfc4182a 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -319,7 +319,7 @@ export async function initBattleWithEnemyConfig(partyConfig: EnemyPartyConfig): // Set IVs if (config.ivs) { - enemyPokemon.ivs = new Uint8Array(config.ivs); + enemyPokemon.ivs = config.ivs; } // Set Status diff --git a/src/data/pokemon-forms.ts b/src/data/pokemon-forms.ts index 7eb8ed263d5..1b392f6fb21 100644 --- a/src/data/pokemon-forms.ts +++ b/src/data/pokemon-forms.ts @@ -24,7 +24,8 @@ import { SpeciesFormKey } from "#enums/species-form-key"; import { SpeciesId } from "#enums/species-id"; import { WeatherType } from "#enums/weather-type"; import type { Pokemon } from "#field/pokemon"; -import type { Constructor, nil } from "#utils/common"; +import type { Constructor } from "#types/common"; +import type { nil } from "#utils/common"; export type SpeciesFormChangeConditionPredicate = (p: Pokemon) => boolean; export type SpeciesFormChangeConditionEnforceFunc = (p: Pokemon) => void; diff --git a/src/data/pokemon-forms/form-change-triggers.ts b/src/data/pokemon-forms/form-change-triggers.ts index 6be6385241e..de0deb412a1 100644 --- a/src/data/pokemon-forms/form-change-triggers.ts +++ b/src/data/pokemon-forms/form-change-triggers.ts @@ -11,9 +11,8 @@ import type { TimeOfDay } from "#enums/time-of-day"; import { WeatherType } from "#enums/weather-type"; import type { Pokemon } from "#field/pokemon"; import type { PokemonFormChangeItemModifier } from "#modifiers/modifier"; -import type { ReadonlyGenericUint8Array } from "#types/typed-arrays"; +import type { Constructor } from "#types/common"; import { coerceArray } from "#utils/array"; -import type { Constructor } from "#utils/common"; import { toCamelCase } from "#utils/strings"; import i18next from "i18next"; @@ -123,12 +122,12 @@ export class SpeciesFormChangeActiveTrigger extends SpeciesFormChangeTrigger { } export class SpeciesFormChangeStatusEffectTrigger extends SpeciesFormChangeTrigger { - public readonly statusEffects: ReadonlyGenericUint8Array; + public readonly statusEffects: readonly StatusEffect[]; public invert: boolean; constructor(statusEffects: StatusEffect | StatusEffect[], invert = false) { super(); - this.statusEffects = new Uint8Array(coerceArray(statusEffects)); + this.statusEffects = coerceArray(statusEffects); this.invert = invert; // this.description = i18next.t("pokemonEvolutions:forms.statusEffect"); } diff --git a/src/data/pokemon/pokemon-data.ts b/src/data/pokemon/pokemon-data.ts index ed94a87f2af..4fbb70bccb2 100644 --- a/src/data/pokemon/pokemon-data.ts +++ b/src/data/pokemon/pokemon-data.ts @@ -16,7 +16,6 @@ import type { AttackMoveResult } from "#types/attack-move-result"; import type { IllusionData } from "#types/illusion-data"; import type { TurnMove } from "#types/turn-move"; import type { CoerceNullPropertiesToUndefined } from "#types/type-helpers"; -import { setTypedArray } from "#utils/array"; import { getPokemonSpeciesForm } from "#utils/pokemon-utils"; /** @@ -130,7 +129,7 @@ export class PokemonSummonData { public passiveAbility: AbilityId | undefined; public gender: Gender | undefined; public fusionGender: Gender | undefined; - public stats: Uint32Array = new Uint32Array(6); + 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. @@ -166,11 +165,6 @@ export class PokemonSummonData { continue; } - if (key === "stats") { - setTypedArray(this.stats, source.stats); - continue; - } - if (key === "illusion" && typeof value === "object") { // Make a copy so as not to mutate provided value const illusionData = { @@ -227,10 +221,8 @@ export class PokemonSummonData { // We coerce null to undefined in the type, as the for loop below replaces `null` with `undefined` ...(this as Omit< CoerceNullPropertiesToUndefined, - "speciesForm" | "fusionSpeciesForm" | "illusion" | "stats" + "speciesForm" | "fusionSpeciesForm" | "illusion" >), - // TypedArrays do not serialize to JSON as an array. - stats: Array.from(this.stats), speciesForm: speciesForm == null ? undefined : { id: speciesForm.speciesId, formIdx: speciesForm.formIndex }, fusionSpeciesForm: fusionSpeciesForm == null diff --git a/src/data/positional-tags/load-positional-tag.ts b/src/data/positional-tags/load-positional-tag.ts index ef3609d93e7..90c889db0e9 100644 --- a/src/data/positional-tags/load-positional-tag.ts +++ b/src/data/positional-tags/load-positional-tag.ts @@ -1,7 +1,7 @@ import { DelayedAttackTag, type PositionalTag, WishTag } from "#data/positional-tags/positional-tag"; import { PositionalTagType } from "#enums/positional-tag-type"; +import type { Constructor } from "#types/common"; import type { ObjectValues } from "#types/type-helpers"; -import type { Constructor } from "#utils/common"; /** * Load the attributes of a {@linkcode PositionalTag}. diff --git a/src/field/arena.ts b/src/field/arena.ts index 3e214ff1ea7..d307ce9d0e8 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -35,8 +35,9 @@ import { TagAddedEvent, TagRemovedEvent, TerrainChangedEvent, WeatherChangedEven import type { Pokemon } from "#field/pokemon"; import { FieldEffectModifier } from "#modifiers/modifier"; import type { Move } from "#moves/move"; +import type { Constructor } from "#types/common"; import type { AbstractConstructor } from "#types/type-helpers"; -import { type Constructor, NumberHolder, randSeedInt } from "#utils/common"; +import { NumberHolder, randSeedInt } from "#utils/common"; import { getPokemonSpecies } from "#utils/pokemon-utils"; export class Arena { diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index f223bbfd64f..9f5656aae12 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -141,22 +141,21 @@ import type { PokemonData } from "#system/pokemon-data"; import { RibbonData } from "#system/ribbons/ribbon-data"; import { awardRibbonsToSpeciesLine } from "#system/ribbons/ribbon-methods"; import type { AbAttrMap, AbAttrString, TypeMultiplierAbAttrParams } from "#types/ability-types"; +import type { Constructor } from "#types/common"; import type { getAttackDamageParams, getBaseDamageParams } from "#types/damage-params"; import type { DamageCalculationResult, DamageResult } from "#types/damage-result"; import type { IllusionData } from "#types/illusion-data"; import type { StarterDataEntry, StarterMoveset } from "#types/save-data"; import type { TurnMove } from "#types/turn-move"; -import type { ReadonlyUint8Array } from "#types/typed-arrays"; import { BattleInfo } from "#ui/battle-info"; import { EnemyBattleInfo } from "#ui/enemy-battle-info"; import type { PartyOption } from "#ui/party-ui-handler"; import { PartyUiHandler, PartyUiMode } from "#ui/party-ui-handler"; import { PlayerBattleInfo } from "#ui/player-battle-info"; -import { coerceArray, setTypedArray } from "#utils/array"; +import { coerceArray } from "#utils/array"; import { applyChallenges } from "#utils/challenge-utils"; import { BooleanHolder, - type Constructor, deltaRgb, fixedInt, getIvsFromId, @@ -204,8 +203,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { public levelExp: number; public gender: Gender; public hp: number; - public stats = Uint32Array.of(1, 1, 1, 1, 1, 1); - public ivs = Uint8Array.of(0, 0, 0, 0, 0, 0); + public stats: number[]; + public ivs: number[]; public nature: Nature; public moveset: PokemonMove[]; /** @@ -312,7 +311,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { gender?: Gender, shiny?: boolean, variant?: Variant, - ivs?: ReadonlyUint8Array | number[], + ivs?: number[], nature?: Nature, dataSource?: Pokemon | PokemonData, ) { @@ -346,8 +345,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { if (dataSource) { this.id = dataSource.id; this.hp = dataSource.hp; - setTypedArray(this.stats, dataSource.stats); - setTypedArray(this.ivs, dataSource.ivs ?? getIvsFromId(dataSource.id)); + this.stats = dataSource.stats; + this.ivs = dataSource.ivs; this.passive = !!dataSource.passive; if (this.variant === undefined) { this.variant = 0; @@ -386,7 +385,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { this.stellarTypesBoosted = dataSource.stellarTypesBoosted ?? []; } else { this.id = randSeedInt(4294967296); - setTypedArray(this.ivs, ivs ?? getIvsFromId(this.id)); + this.ivs = ivs || getIvsFromId(this.id); if (this.gender === undefined) { this.gender = this.species.generateGender(); @@ -1320,7 +1319,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { * @param bypassSummonData - Whether to prefer actual stats (`true`) or in-battle overridden stats (`false`); default `true` * @returns The numeric values of this {@linkcode Pokemon}'s stats as an array. */ - getStats(bypassSummonData = true): Uint32Array { + getStats(bypassSummonData = true): number[] { if (!bypassSummonData) { // Only grab summon data stats if nonzero return this.summonData.stats.map((s, i) => s || this.stats[i]); @@ -1552,6 +1551,10 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { } calculateStats(): void { + if (!this.stats) { + this.stats = [0, 0, 0, 0, 0, 0]; + } + // Get and manipulate base stats const baseStats = this.calculateBaseStats(); // Using base stats, calculate and store stats one by one @@ -1584,7 +1587,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { globalScene.applyModifier(PokemonIncrementingStatModifier, this.isPlayer(), this, s, statHolder); } - statHolder.value = Phaser.Math.Clamp(statHolder.value, 1, 0xffffffff); + statHolder.value = Phaser.Math.Clamp(statHolder.value, 1, Number.MAX_SAFE_INTEGER); this.setStat(s, statHolder.value); } @@ -5701,7 +5704,7 @@ export class PlayerPokemon extends Pokemon { gender?: Gender, shiny?: boolean, variant?: Variant, - ivs?: ReadonlyUint8Array | number[], + ivs?: number[], nature?: Nature, dataSource?: Pokemon | PokemonData, ) { @@ -6321,9 +6324,9 @@ export class EnemyPokemon extends Pokemon { if (this.hasTrainer() && globalScene.currentBattle) { const { waveIndex } = globalScene.currentBattle; - const ivs = new Uint8Array(6); - for (let i = 0; i < 6; i++) { - ivs[i] = this.randBattleSeedIntRange(Math.floor(waveIndex / 10), 31); + const ivs: number[] = []; + while (ivs.length < 6) { + ivs.push(randSeedIntRange(Math.floor(waveIndex / 10), 31)); } this.ivs = ivs; } diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 5f89c0a6da8..3ffa7482706 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -1914,7 +1914,7 @@ export class GameData { _unlockSpeciesNature(species.speciesId); } - updateSpeciesDexIvs(speciesId: SpeciesId, ivs: Uint8Array): void { + updateSpeciesDexIvs(speciesId: SpeciesId, ivs: number[]): void { let dexEntry: DexEntry; do { dexEntry = globalScene.gameData.dexData[speciesId]; diff --git a/src/system/pokemon-data.ts b/src/system/pokemon-data.ts index 4aa036f621c..0ddfedeff84 100644 --- a/src/system/pokemon-data.ts +++ b/src/system/pokemon-data.ts @@ -99,8 +99,8 @@ export class PokemonData { this.levelExp = source.levelExp; this.gender = source.gender; this.hp = source.hp; - this.stats = Array.from(source.stats); - this.ivs = Array.from(source.ivs); + 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; @@ -162,7 +162,7 @@ export class PokemonData { this.gender, this.shiny, this.variant, - new Uint8Array(this.ivs.slice(0, 6)), + this.ivs, this.nature, this, playerPokemon => { diff --git a/src/ui/containers/stats-container.ts b/src/ui/containers/stats-container.ts index 55b099b95c5..fa53c8c8fd4 100644 --- a/src/ui/containers/stats-container.ts +++ b/src/ui/containers/stats-container.ts @@ -104,7 +104,7 @@ export class StatsContainer extends Phaser.GameObjects.Container { } } - updateIvs(ivs: Uint8Array | number[], originalIvs?: number[]): void { + updateIvs(ivs: number[], originalIvs?: number[]): void { if (ivs) { const ivChartData = new Array(6) .fill(null) diff --git a/src/ui/handlers/battle-message-ui-handler.ts b/src/ui/handlers/battle-message-ui-handler.ts index 79e7e318915..f845f22a730 100644 --- a/src/ui/handlers/battle-message-ui-handler.ts +++ b/src/ui/handlers/battle-message-ui-handler.ts @@ -195,7 +195,7 @@ export class BattleMessageUiHandler extends MessageUiHandler { super.showDialogue(text, name, delay, callback, callbackDelay, prompt, promptDelay); } - promptLevelUpStats(partyMemberIndex: number, prevStats: ArrayLike, showTotals: boolean): Promise { + promptLevelUpStats(partyMemberIndex: number, prevStats: number[], showTotals: boolean): Promise { return new Promise(resolve => { if (!globalScene.showLevelUpStats) { return resolve(); @@ -219,7 +219,7 @@ export class BattleMessageUiHandler extends MessageUiHandler { }); } - promptIvs(pokemonId: number, ivs: ArrayLike): Promise { + promptIvs(pokemonId: number, ivs: number[]): Promise { return new Promise(resolve => { globalScene.executeWithSeedOffset(() => { let levelUpStatsValuesText = ""; diff --git a/src/ui/handlers/starter-select-ui-handler.ts b/src/ui/handlers/starter-select-ui-handler.ts index 4e7fd837a65..d0bef69aa81 100644 --- a/src/ui/handlers/starter-select-ui-handler.ts +++ b/src/ui/handlers/starter-select-ui-handler.ts @@ -2759,7 +2759,7 @@ export class StarterSelectUiHandler extends MessageUiHandler { pokerus: this.pokerusSpecies.includes(species), nickname: this.starterPreferences[species.speciesId]?.nickname, teraType, - ivs: new Uint8Array(dexEntry.ivs), + ivs: dexEntry.ivs, }; this.starters.push(starter); diff --git a/src/utils/array.ts b/src/utils/array.ts index 75807d2da21..ece0bdd24c5 100644 --- a/src/utils/array.ts +++ b/src/utils/array.ts @@ -12,7 +12,7 @@ import type { * If the input isn't already an array, turns it into one. * @returns An array with the same type as the type of the input */ -export function coerceArray(input: T): T extends any[] ? T : [T]; +export function coerceArray(input: T): T extends readonly any[] ? T : [T]; export function coerceArray(input: T): T | [T] { return Array.isArray(input) ? input : [input]; } diff --git a/src/utils/common.ts b/src/utils/common.ts index 29ab3cf462b..cd6b726938e 100644 --- a/src/utils/common.ts +++ b/src/utils/common.ts @@ -176,15 +176,15 @@ export function getPlayTimeString(totalSeconds: number): string { * @param id 32-bit number * @returns An array of six numbers corresponding to 5-bit chunks from {@linkcode id} */ -export function getIvsFromId(id: number): Uint8Array { - return Uint8Array.of( +export function getIvsFromId(id: number): [number, number, number, number, number, number] { + return [ (id & 0x3e000000) >>> 25, (id & 0x01f00000) >>> 20, (id & 0x000f8000) >>> 15, (id & 0x00007c00) >>> 10, (id & 0x000003e0) >>> 5, id & 0x0000001f, - ); + ]; } export function formatLargeNumber(count: number, threshold: number): string { @@ -292,9 +292,6 @@ export async function localPing(): Promise { } } -/** Alias for the constructor of a class */ -export type Constructor = new (...args: unknown[]) => T; - export class BooleanHolder { public value: boolean; diff --git a/test/abilities/beast-boost.test.ts b/test/abilities/beast-boost.test.ts index 7ec3ac355ff..aeb4d854b1a 100644 --- a/test/abilities/beast-boost.test.ts +++ b/test/abilities/beast-boost.test.ts @@ -38,7 +38,7 @@ describe("Abilities - Beast Boost", () => { const playerPokemon = game.field.getPlayerPokemon(); // Set the pokemon's highest stat to DEF, so it should be picked by Beast Boost - vi.spyOn(playerPokemon, "stats", "get").mockReturnValue(Uint32Array.of(10000, 100, 1000, 200, 100, 100)); + vi.spyOn(playerPokemon, "stats", "get").mockReturnValue([10000, 100, 1000, 200, 100, 100]); console.log(playerPokemon.stats); expect(playerPokemon.getStatStage(Stat.DEF)).toBe(0); @@ -56,7 +56,7 @@ describe("Abilities - Beast Boost", () => { const playerPokemon = game.field.getPlayerPokemon(); // If the opponent uses Guard Split, the pokemon's second highest stat (SPATK) should be chosen - vi.spyOn(playerPokemon, "stats", "get").mockReturnValue(Uint32Array.of(10000, 100, 201, 200, 100, 100)); + vi.spyOn(playerPokemon, "stats", "get").mockReturnValue([10000, 100, 201, 200, 100, 100]); expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(0); @@ -75,7 +75,7 @@ describe("Abilities - Beast Boost", () => { const playerPokemon = game.field.getPlayerPokemon(); // Set up tie between SPATK, SPDEF, and SPD, where SPATK should win - vi.spyOn(playerPokemon, "stats", "get").mockReturnValue(Uint32Array.of(10000, 1, 1, 100, 100, 100)); + vi.spyOn(playerPokemon, "stats", "get").mockReturnValue([10000, 1, 1, 100, 100, 100]); expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(0); diff --git a/test/battle/battle-order.test.ts b/test/battle/battle-order.test.ts index 382604bffcd..de13b22df79 100644 --- a/test/battle/battle-order.test.ts +++ b/test/battle/battle-order.test.ts @@ -39,8 +39,8 @@ describe("Battle order", () => { const enemyPokemon = game.field.getEnemyPokemon(); const enemyStartHp = enemyPokemon.hp; - vi.spyOn(playerPokemon, "stats", "get").mockReturnValue(Uint32Array.of(20, 20, 20, 20, 20, 50)); // set playerPokemon's speed to 50 - vi.spyOn(enemyPokemon, "stats", "get").mockReturnValue(Uint32Array.of(20, 20, 20, 20, 20, 150)); // set enemyPokemon's speed to 150 + vi.spyOn(playerPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 50]); // set playerPokemon's speed to 50 + vi.spyOn(enemyPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150]); // set enemyPokemon's speed to 150 game.move.select(MoveId.TACKLE); await game.phaseInterceptor.to("MoveEndPhase", false); @@ -55,8 +55,8 @@ describe("Battle order", () => { const playerStartHp = playerPokemon.hp; const enemyPokemon = game.field.getEnemyPokemon(); const enemyStartHp = enemyPokemon.hp; - vi.spyOn(playerPokemon, "stats", "get").mockReturnValue(Uint32Array.of(20, 20, 20, 20, 20, 150)); // set playerPokemon's speed to 150 - vi.spyOn(enemyPokemon, "stats", "get").mockReturnValue(Uint32Array.of(20, 20, 20, 20, 20, 50)); // set enemyPokemon's speed to 50 + vi.spyOn(playerPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150]); // set playerPokemon's speed to 150 + vi.spyOn(enemyPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 50]); // set enemyPokemon's speed to 50 game.move.select(MoveId.TACKLE); @@ -74,8 +74,8 @@ describe("Battle order", () => { const enemyPokemon = game.scene.getEnemyField(); const enemyHps = enemyPokemon.map(p => p.hp); - playerPokemon.forEach(p => vi.spyOn(p, "stats", "get").mockReturnValue(Uint32Array.of(20, 20, 20, 20, 20, 50))); // set both playerPokemons' speed to 50 - enemyPokemon.forEach(p => vi.spyOn(p, "stats", "get").mockReturnValue(Uint32Array.of(20, 20, 20, 20, 20, 150))); // set both enemyPokemons' speed to 150 + playerPokemon.forEach(p => vi.spyOn(p, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 50])); // set both playerPokemons' speed to 50 + enemyPokemon.forEach(p => vi.spyOn(p, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150])); // set both enemyPokemons' speed to 150 game.move.select(MoveId.TACKLE); game.move.select(MoveId.TACKLE, 1); @@ -96,9 +96,9 @@ describe("Battle order", () => { const playerPokemon = game.scene.getPlayerField(); const enemyPokemon = game.scene.getEnemyField(); - playerPokemon.forEach(p => vi.spyOn(p, "stats", "get").mockReturnValue(Uint32Array.of(20, 20, 20, 20, 20, 100))); //set both playerPokemons' speed to 100 - vi.spyOn(enemyPokemon[0], "stats", "get").mockReturnValue(Uint32Array.of(20, 20, 20, 20, 20, 100)); // set enemyPokemon's speed to 100 - vi.spyOn(enemyPokemon[1], "stats", "get").mockReturnValue(Uint32Array.of(20, 20, 20, 20, 20, 150)); // set enemyPokemon's speed to 150 + playerPokemon.forEach(p => vi.spyOn(p, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 100])); //set both playerPokemons' speed to 100 + vi.spyOn(enemyPokemon[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 100]); // set enemyPokemon's speed to 100 + vi.spyOn(enemyPokemon[1], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150]); // set enemyPokemon's speed to 150 game.move.select(MoveId.TACKLE); game.move.select(MoveId.TACKLE, 1); @@ -114,10 +114,10 @@ describe("Battle order", () => { const playerPokemon = game.scene.getPlayerField(); const enemyPokemon = game.scene.getEnemyField(); - vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue(Uint32Array.of(20, 20, 20, 20, 20, 100)); // set one playerPokemon's speed to 100 - vi.spyOn(playerPokemon[1], "stats", "get").mockReturnValue(Uint32Array.of(20, 20, 20, 20, 20, 150)); // set other playerPokemon's speed to 150 - vi.spyOn(enemyPokemon[0], "stats", "get").mockReturnValue(Uint32Array.of(20, 20, 20, 20, 20, 100)); // set one enemyPokemon's speed to 100 - vi.spyOn(enemyPokemon[1], "stats", "get").mockReturnValue(Uint32Array.of(20, 20, 20, 20, 20, 150)); // set other enemyPokemon's speed to 150 + vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 100]); // set one playerPokemon's speed to 100 + vi.spyOn(playerPokemon[1], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150]); // set other playerPokemon's speed to 150 + vi.spyOn(enemyPokemon[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 100]); // set one enemyPokemon's speed to 100 + vi.spyOn(enemyPokemon[1], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150]); // set other enemyPokemon's speed to 150 game.move.select(MoveId.TACKLE); game.move.select(MoveId.TACKLE, 1); diff --git a/test/escape-calculations.test.ts b/test/escape-calculations.test.ts index 062a86c6fa2..e1e521f4394 100644 --- a/test/escape-calculations.test.ts +++ b/test/escape-calculations.test.ts @@ -38,7 +38,7 @@ describe("Escape chance calculations", () => { const enemyField = game.scene.getEnemyField(); const enemySpeed = 100; // set enemyPokemon's speed to 100 - vi.spyOn(enemyField[0], "stats", "get").mockReturnValue(Uint32Array.of(20, 20, 20, 20, 20, enemySpeed)); + vi.spyOn(enemyField[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemySpeed]); const commandPhase = game.scene.phaseManager.getCurrentPhase() as CommandPhase; commandPhase.handleCommand(Command.RUN, 0); @@ -81,9 +81,14 @@ describe("Escape chance calculations", () => { // set the number of escape attempts to the required amount game.scene.currentBattle.escapeAttempts = check.escapeAttempts; // set playerPokemon's speed to a multiple of the enemySpeed - vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue( - Uint32Array.of(20, 20, 20, 20, 20, check.pokemonSpeedRatio * enemySpeed), - ); + vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue([ + 20, + 20, + 20, + 20, + 20, + check.pokemonSpeedRatio * enemySpeed, + ]); const chance = phase.calculateEscapeChance(game.scene.currentBattle.escapeAttempts); expect(chance).toBe(check.expectedEscapeChance); } @@ -102,9 +107,9 @@ describe("Escape chance calculations", () => { // this is used to find the ratio of the player's first pokemon const playerASpeedPercentage = 0.4; // set enemyAPokemon's speed to 70 - vi.spyOn(enemyField[0], "stats", "get").mockReturnValue(Uint32Array.of(20, 20, 20, 20, 20, enemyASpeed)); + vi.spyOn(enemyField[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemyASpeed]); // set enemyBPokemon's speed to 30 - vi.spyOn(enemyField[1], "stats", "get").mockReturnValue(Uint32Array.of(20, 20, 20, 20, 20, enemyBSpeed)); + vi.spyOn(enemyField[1], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemyBSpeed]); const commandPhase = game.scene.phaseManager.getCurrentPhase() as CommandPhase; commandPhase.handleCommand(Command.RUN, 0); @@ -146,20 +151,23 @@ describe("Escape chance calculations", () => { // sets the number of escape attempts to the required amount game.scene.currentBattle.escapeAttempts = check.escapeAttempts; // set the first playerPokemon's speed to a multiple of the enemySpeed - vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue( - Uint32Array.of( - 20, - 20, - 20, - 20, - 20, - Math.floor(check.pokemonSpeedRatio * totalEnemySpeed * playerASpeedPercentage), - ), - ); + vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue([ + 20, + 20, + 20, + 20, + 20, + Math.floor(check.pokemonSpeedRatio * totalEnemySpeed * playerASpeedPercentage), + ]); // set the second playerPokemon's speed to the remaining value of speed - vi.spyOn(playerPokemon[1], "stats", "get").mockReturnValue( - Uint32Array.of(20, 20, 20, 20, 20, check.pokemonSpeedRatio * totalEnemySpeed - playerPokemon[0].stats[5]), - ); + vi.spyOn(playerPokemon[1], "stats", "get").mockReturnValue([ + 20, + 20, + 20, + 20, + 20, + check.pokemonSpeedRatio * totalEnemySpeed - playerPokemon[0].stats[5], + ]); const chance = phase.calculateEscapeChance(game.scene.currentBattle.escapeAttempts); // checks to make sure the escape values are the same expect(chance).toBe(check.expectedEscapeChance); @@ -176,7 +184,7 @@ describe("Escape chance calculations", () => { const enemyField = game.scene.getEnemyField()!; const enemySpeed = 100; // set enemyPokemon's speed to 100 - vi.spyOn(enemyField[0], "stats", "get").mockReturnValue(Uint32Array.of(20, 20, 20, 20, 20, enemySpeed)); + vi.spyOn(enemyField[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemySpeed]); const commandPhase = game.scene.phaseManager.getCurrentPhase() as CommandPhase; commandPhase.handleCommand(Command.RUN, 0); @@ -233,9 +241,14 @@ describe("Escape chance calculations", () => { // sets the number of escape attempts to the required amount game.scene.currentBattle.escapeAttempts = check.escapeAttempts; // set playerPokemon's speed to a multiple of the enemySpeed - vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue( - Uint32Array.of(20, 20, 20, 20, 20, check.pokemonSpeedRatio * enemySpeed), - ); + vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue([ + 20, + 20, + 20, + 20, + 20, + check.pokemonSpeedRatio * enemySpeed, + ]); const chance = phase.calculateEscapeChance(game.scene.currentBattle.escapeAttempts); expect(chance).toBe(check.expectedEscapeChance); } @@ -254,9 +267,9 @@ describe("Escape chance calculations", () => { // this is used to find the ratio of the player's first pokemon const playerASpeedPercentage = 0.8; // set enemyAPokemon's speed to 70 - vi.spyOn(enemyField[0], "stats", "get").mockReturnValue(Uint32Array.of(20, 20, 20, 20, 20, enemyASpeed)); + vi.spyOn(enemyField[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemyASpeed]); // set enemyBPokemon's speed to 30 - vi.spyOn(enemyField[1], "stats", "get").mockReturnValue(Uint32Array.of(20, 20, 20, 20, 20, enemyBSpeed)); + vi.spyOn(enemyField[1], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemyBSpeed]); const commandPhase = game.scene.phaseManager.getCurrentPhase() as CommandPhase; commandPhase.handleCommand(Command.RUN, 0); @@ -311,20 +324,23 @@ describe("Escape chance calculations", () => { // sets the number of escape attempts to the required amount game.scene.currentBattle.escapeAttempts = check.escapeAttempts; // set the first playerPokemon's speed to a multiple of the enemySpeed - vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue( - Uint32Array.of( - 20, - 20, - 20, - 20, - 20, - Math.floor(check.pokemonSpeedRatio * totalEnemySpeed * playerASpeedPercentage), - ), - ); + vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue([ + 20, + 20, + 20, + 20, + 20, + Math.floor(check.pokemonSpeedRatio * totalEnemySpeed * playerASpeedPercentage), + ]); // set the second playerPokemon's speed to the remaining value of speed - vi.spyOn(playerPokemon[1], "stats", "get").mockReturnValue( - Uint32Array.of(20, 20, 20, 20, 20, check.pokemonSpeedRatio * totalEnemySpeed - playerPokemon[0].stats[5]), - ); + vi.spyOn(playerPokemon[1], "stats", "get").mockReturnValue([ + 20, + 20, + 20, + 20, + 20, + check.pokemonSpeedRatio * totalEnemySpeed - playerPokemon[0].stats[5], + ]); const chance = phase.calculateEscapeChance(game.scene.currentBattle.escapeAttempts); // checks to make sure the escape values are the same expect(chance).toBe(check.expectedEscapeChance); diff --git a/test/moves/rollout.test.ts b/test/moves/rollout.test.ts index 9e6d8b87e06..0e01725a188 100644 --- a/test/moves/rollout.test.ts +++ b/test/moves/rollout.test.ts @@ -45,10 +45,10 @@ describe("Moves - Rollout", () => { await game.classicMode.startBattle(); const playerPkm = game.field.getPlayerPokemon(); - vi.spyOn(playerPkm, "stats", "get").mockReturnValue(Uint32Array.of(500000, 1, 1, 1, 1, 1)); // HP, ATK, DEF, SPATK, SPDEF, SPD + vi.spyOn(playerPkm, "stats", "get").mockReturnValue([500000, 1, 1, 1, 1, 1]); // HP, ATK, DEF, SPATK, SPDEF, SPD const enemyPkm = game.field.getEnemyPokemon(); - vi.spyOn(enemyPkm, "stats", "get").mockReturnValue(Uint32Array.of(500000, 1, 1, 1, 1, 1)); // HP, ATK, DEF, SPATK, SPDEF, SPD + vi.spyOn(enemyPkm, "stats", "get").mockReturnValue([500000, 1, 1, 1, 1, 1]); // HP, ATK, DEF, SPATK, SPDEF, SPD vi.spyOn(enemyPkm, "getHeldItems").mockReturnValue([]); //no berries enemyPkm.hp = enemyPkm.getMaxHp(); diff --git a/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts b/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts index fb1e5034c9d..bc1a2893627 100644 --- a/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts +++ b/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts @@ -144,7 +144,7 @@ describe("Fun And Games! - Mystery Encounter", () => { expect(game).toBeAtPhase("CommandPhase"); expect(game.field.getEnemyPokemon().species.speciesId).toBe(SpeciesId.WOBBUFFET); - expect(game.field.getEnemyPokemon().ivs).toEqual(Uint8Array.of(0, 0, 0, 0, 0, 0)); + expect(game.field.getEnemyPokemon().ivs).toEqual([0, 0, 0, 0, 0, 0]); expect(game.field.getEnemyPokemon().nature).toBe(Nature.MILD); game.onNextPrompt("MessagePhase", UiMode.MESSAGE, () => { diff --git a/test/mystery-encounter/encounters/part-timer-encounter.test.ts b/test/mystery-encounter/encounters/part-timer-encounter.test.ts index 05af5757b2a..15d2664364c 100644 --- a/test/mystery-encounter/encounters/part-timer-encounter.test.ts +++ b/test/mystery-encounter/encounters/part-timer-encounter.test.ts @@ -122,7 +122,7 @@ describe("Part-Timer - Mystery Encounter", () => { // Override party levels to 50 so stats can be fully reflective scene.getPlayerParty().forEach(p => { p.level = 50; - p.ivs = Uint8Array.of(20, 20, 20, 20, 20, 20); + p.ivs = [20, 20, 20, 20, 20, 20]; p.calculateStats(); }); await runMysteryEncounterToEnd(game, 1, { pokemonNo: 2 }); @@ -168,7 +168,7 @@ describe("Part-Timer - Mystery Encounter", () => { // Override party levels to 50 so stats can be fully reflective scene.getPlayerParty().forEach(p => { p.level = 50; - p.ivs = Uint8Array.of(0, 0, 0, 0, 0, 0); + p.ivs = [0, 0, 0, 0, 0, 0]; p.calculateStats(); }); await runMysteryEncounterToEnd(game, 2, { pokemonNo: 3 }); @@ -188,7 +188,7 @@ describe("Part-Timer - Mystery Encounter", () => { // Override party levels to 50 so stats can be fully reflective scene.getPlayerParty().forEach(p => { p.level = 50; - p.ivs = Uint8Array.of(20, 20, 20, 20, 20, 20); + p.ivs = [20, 20, 20, 20, 20, 20]; p.calculateStats(); }); await runMysteryEncounterToEnd(game, 2, { pokemonNo: 4 });