Revert use of typed arrays

This commit is contained in:
Sirz Benjie 2025-09-22 12:41:40 -05:00
parent 9921b57ef3
commit ba7be773dc
No known key found for this signature in database
GPG Key ID: 4A524B4D196C759E
26 changed files with 164 additions and 154 deletions

View File

@ -1 +1,4 @@
export type ConditionFn = (args?: any[]) => boolean; export type ConditionFn = (args?: any[]) => boolean;
/** Alias for the constructor of a class */
export type Constructor<T> = new (...args: unknown[]) => T;

View File

@ -123,7 +123,7 @@ export interface Starter {
pokerus: boolean; pokerus: boolean;
nickname?: string; nickname?: string;
teraType?: PokemonType; teraType?: PokemonType;
ivs: Uint8Array; ivs: number[];
} }
export type RunHistoryData = Record<number, RunEntry>; export type RunHistoryData = Record<number, RunEntry>;

View File

@ -118,9 +118,9 @@ import type { TrainerData } from "#system/trainer-data";
import type { Voucher } from "#system/voucher"; import type { Voucher } from "#system/voucher";
import { vouchers } from "#system/voucher"; import { vouchers } from "#system/voucher";
import { trainerConfigs } from "#trainers/trainer-config"; import { trainerConfigs } from "#trainers/trainer-config";
import type { Constructor } from "#types/common";
import type { HeldModifierConfig } from "#types/held-modifier-config"; import type { HeldModifierConfig } from "#types/held-modifier-config";
import type { Localizable } from "#types/locales"; import type { Localizable } from "#types/locales";
import type { ReadonlyUint8Array } from "#types/typed-arrays";
import { AbilityBar } from "#ui/ability-bar"; import { AbilityBar } from "#ui/ability-bar";
import { ArenaFlyout } from "#ui/arena-flyout"; import { ArenaFlyout } from "#ui/arena-flyout";
import { CandyBar } from "#ui/candy-bar"; import { CandyBar } from "#ui/candy-bar";
@ -133,7 +133,6 @@ import { UI } from "#ui/ui";
import { addUiThemeOverrides } from "#ui/ui-theme"; import { addUiThemeOverrides } from "#ui/ui-theme";
import { import {
BooleanHolder, BooleanHolder,
type Constructor,
fixedInt, fixedInt,
formatMoney, formatMoney,
getIvsFromId, getIvsFromId,
@ -867,7 +866,7 @@ export class BattleScene extends SceneBase {
gender?: Gender, gender?: Gender,
shiny?: boolean, shiny?: boolean,
variant?: Variant, variant?: Variant,
ivs?: ReadonlyUint8Array | number[], ivs?: number[],
nature?: Nature, nature?: Nature,
dataSource?: Pokemon | PokemonData, dataSource?: Pokemon | PokemonData,
postProcess?: (playerPokemon: PlayerPokemon) => void, postProcess?: (playerPokemon: PlayerPokemon) => void,
@ -898,12 +897,12 @@ export class BattleScene extends SceneBase {
if (Overrides.IVS_OVERRIDE.some(value => !isBetween(value, 0, 31))) { 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!"); 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 { } else {
if (!isBetween(Overrides.IVS_OVERRIDE, 0, 31)) { if (!isBetween(Overrides.IVS_OVERRIDE, 0, 31)) {
throw new Error("The Player IV override must be a value between 0 and 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) { 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))) { 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!"); 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 { } else {
if (!isBetween(Overrides.ENEMY_IVS_OVERRIDE, 0, 31)) { if (!isBetween(Overrides.ENEMY_IVS_OVERRIDE, 0, 31)) {
throw new Error("The Enemy IV override must be a value between 0 and 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) { if (Overrides.ENEMY_NATURE_OVERRIDE !== null) {

View File

@ -62,11 +62,10 @@ import type {
PokemonDefendCondition, PokemonDefendCondition,
PokemonStatStageChangeCondition, PokemonStatStageChangeCondition,
} from "#types/ability-types"; } from "#types/ability-types";
import type { Constructor } from "#types/common";
import type { Localizable } from "#types/locales"; import type { Localizable } from "#types/locales";
import type { Closed, Exact } from "#types/type-helpers"; import type { Closed, Exact } from "#types/type-helpers";
import type { GenericUint8Array, ReadonlyGenericInt8Array, ReadonlyGenericUint8Array } from "#types/typed-arrays";
import { coerceArray } from "#utils/array"; import { coerceArray } from "#utils/array";
import type { Constructor } from "#utils/common";
import { BooleanHolder, NumberHolder, randSeedFloat, randSeedInt, randSeedItem, toDmgValue } from "#utils/common"; import { BooleanHolder, NumberHolder, randSeedFloat, randSeedInt, randSeedItem, toDmgValue } from "#utils/common";
import { toCamelCase } from "#utils/strings"; import { toCamelCase } from "#utils/strings";
import i18next from "i18next"; import i18next from "i18next";
@ -1211,13 +1210,13 @@ export class PostDefendTerrainChangeAbAttr extends PostDefendAbAttr {
export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr { export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr {
private chance: number; private chance: number;
private readonly effects: ReadonlyGenericUint8Array<StatusEffect>; private readonly effects: readonly StatusEffect[];
constructor(chance: number, ...effects: StatusEffect[]) { constructor(chance: number, ...effects: StatusEffect[]) {
super(true); super(true);
this.chance = chance; this.chance = chance;
this.effects = new Uint8Array(effects); this.effects = effects;
} }
override canApply({ pokemon, move, opponent: attacker }: PostMoveInteractionAbAttrParams): boolean { override canApply({ pokemon, move, opponent: attacker }: PostMoveInteractionAbAttrParams): boolean {
@ -2181,14 +2180,14 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr {
export class PostAttackApplyStatusEffectAbAttr extends PostAttackAbAttr { export class PostAttackApplyStatusEffectAbAttr extends PostAttackAbAttr {
private contactRequired: boolean; private contactRequired: boolean;
private chance: number; private chance: number;
private readonly effects: ReadonlyGenericUint8Array<StatusEffect>; private readonly effects: readonly StatusEffect[];
constructor(contactRequired: boolean, chance: number, ...effects: StatusEffect[]) { constructor(contactRequired: boolean, chance: number, ...effects: StatusEffect[]) {
super(); super();
this.contactRequired = contactRequired; this.contactRequired = contactRequired;
this.chance = chance; this.chance = chance;
this.effects = new Uint8Array(effects); this.effects = effects;
} }
override canApply(params: PostMoveInteractionAbAttrParams): boolean { 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) * Heals a status effect if the Pokemon is afflicted with it upon switch in (or gain)
*/ */
export class PostSummonHealStatusAbAttr extends PostSummonRemoveEffectAbAttr { export class PostSummonHealStatusAbAttr extends PostSummonRemoveEffectAbAttr {
private readonly immuneEffects: ReadonlyGenericUint8Array<StatusEffect>; private readonly immuneEffects: readonly StatusEffect[];
private statusHealed: StatusEffect; private statusHealed: StatusEffect;
/** /**
@ -2949,7 +2948,7 @@ export class PostSummonHealStatusAbAttr extends PostSummonRemoveEffectAbAttr {
*/ */
constructor(...immuneEffects: StatusEffect[]) { constructor(...immuneEffects: StatusEffect[]) {
super(); super();
this.immuneEffects = new Uint8Array(immuneEffects); this.immuneEffects = immuneEffects;
} }
public override canApply({ pokemon }: AbAttrBaseParams): boolean { public override canApply({ pokemon }: AbAttrBaseParams): boolean {
@ -3050,7 +3049,7 @@ export class PostSummonCopyAbilityAbAttr extends PostSummonAbAttr {
* Removes supplied status effects from the user's field. * Removes supplied status effects from the user's field.
*/ */
export class PostSummonUserFieldRemoveStatusEffectAbAttr extends PostSummonAbAttr { export class PostSummonUserFieldRemoveStatusEffectAbAttr extends PostSummonAbAttr {
private readonly statusEffect: ReadonlyGenericUint8Array<StatusEffect>; private readonly statusEffect: readonly StatusEffect[];
/** /**
* @param statusEffect - The status effects to be removed from the user's field. * @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[]) { constructor(...statusEffect: StatusEffect[]) {
super(false); super(false);
this.statusEffect = new Uint8Array(statusEffect); this.statusEffect = statusEffect;
} }
override canApply({ pokemon }: AbAttrBaseParams): boolean { override canApply({ pokemon }: AbAttrBaseParams): boolean {
@ -3638,7 +3637,7 @@ export class PreSetStatusAbAttr extends AbAttr {
* Provides immunity to status effects to specified targets. * Provides immunity to status effects to specified targets.
*/ */
export class PreSetStatusEffectImmunityAbAttr extends PreSetStatusAbAttr { export class PreSetStatusEffectImmunityAbAttr extends PreSetStatusAbAttr {
protected readonly immuneEffects: ReadonlyGenericUint8Array<StatusEffect>; protected readonly immuneEffects: readonly StatusEffect[];
/** /**
* @param immuneEffects - An array of {@linkcode StatusEffect}s to prevent application. * @param immuneEffects - An array of {@linkcode StatusEffect}s to prevent application.
@ -3647,7 +3646,7 @@ export class PreSetStatusEffectImmunityAbAttr extends PreSetStatusAbAttr {
constructor(...immuneEffects: StatusEffect[]) { constructor(...immuneEffects: StatusEffect[]) {
super(); super();
this.immuneEffects = new Uint8Array(immuneEffects); this.immuneEffects = immuneEffects;
} }
override canApply({ effect, cancelled }: PreSetStatusAbAttrParams): boolean { override canApply({ effect, cancelled }: PreSetStatusAbAttrParams): boolean {
@ -3706,7 +3705,7 @@ export interface UserFieldStatusEffectImmunityAbAttrParams extends AbAttrBasePar
*/ */
export class UserFieldStatusEffectImmunityAbAttr extends CancelInteractionAbAttr { export class UserFieldStatusEffectImmunityAbAttr extends CancelInteractionAbAttr {
private declare readonly _: never; private declare readonly _: never;
protected readonly immuneEffects: ReadonlyGenericUint8Array<StatusEffect>; protected readonly immuneEffects: readonly StatusEffect[];
/** /**
* @param immuneEffects - An array of {@linkcode StatusEffect}s to prevent application. * @param immuneEffects - An array of {@linkcode StatusEffect}s to prevent application.
@ -3715,7 +3714,7 @@ export class UserFieldStatusEffectImmunityAbAttr extends CancelInteractionAbAttr
constructor(...immuneEffects: StatusEffect[]) { constructor(...immuneEffects: StatusEffect[]) {
super(); super();
this.immuneEffects = new Uint8Array(immuneEffects); this.immuneEffects = immuneEffects;
} }
override canApply({ effect, cancelled }: UserFieldStatusEffectImmunityAbAttrParams): boolean { 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. * This attribute will block any status damage that you put in the parameter.
*/ */
export class BlockStatusDamageAbAttr extends CancelInteractionAbAttr { export class BlockStatusDamageAbAttr extends CancelInteractionAbAttr {
private readonly effects: ReadonlyGenericUint8Array<StatusEffect>; private readonly effects: readonly StatusEffect[];
/** /**
* @param effects - The status effect(s) that will be blocked from damaging the ability pokemon * @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[]) { constructor(...effects: StatusEffect[]) {
super(false); super(false);
this.effects = new Uint8Array(effects); this.effects = effects;
} }
override canApply({ pokemon, cancelled }: AbAttrParamsWithCancel): boolean { override canApply({ pokemon, cancelled }: AbAttrParamsWithCancel): boolean {
@ -4540,7 +4539,7 @@ export class PostTurnAbAttr extends AbAttr {
* @sealed * @sealed
*/ */
export class PostTurnStatusHealAbAttr extends PostTurnAbAttr { export class PostTurnStatusHealAbAttr extends PostTurnAbAttr {
private readonly effects: GenericUint8Array<StatusEffect>; private readonly effects: readonly StatusEffect[];
/** /**
* @param effects - The status effect(s) that will qualify healing the ability pokemon * @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[]) { constructor(...effects: StatusEffect[]) {
super(false); super(false);
this.effects = new Uint8Array(effects); this.effects = effects;
} }
override canApply({ pokemon }: AbAttrBaseParams): boolean { override canApply({ pokemon }: AbAttrBaseParams): boolean {
@ -5809,14 +5808,14 @@ export interface IgnoreTypeStatusEffectImmunityAbAttrParams extends AbAttrParams
* @sealed * @sealed
*/ */
export class IgnoreTypeStatusEffectImmunityAbAttr extends AbAttr { export class IgnoreTypeStatusEffectImmunityAbAttr extends AbAttr {
private readonly statusEffect: ReadonlyGenericUint8Array<StatusEffect>; private readonly statusEffect: readonly StatusEffect[];
private readonly defenderType: ReadonlyGenericInt8Array<PokemonType>; private readonly defenderType: readonly PokemonType[];
constructor(statusEffect: StatusEffect[], defenderType: PokemonType[]) { constructor(statusEffect: readonly StatusEffect[], defenderType: readonly PokemonType[]) {
super(false); super(false);
this.statusEffect = new Uint8Array(statusEffect); this.statusEffect = statusEffect;
this.defenderType = new Int8Array(defenderType); this.defenderType = defenderType;
} }
override canApply({ statusEffect, defenderType, cancelled }: IgnoreTypeStatusEffectImmunityAbAttrParams): boolean { override canApply({ statusEffect, defenderType, cancelled }: IgnoreTypeStatusEffectImmunityAbAttrParams): boolean {

View File

@ -87,7 +87,8 @@ import type { AttackMoveResult } from "#types/attack-move-result";
import type { Localizable } from "#types/locales"; import type { Localizable } from "#types/locales";
import type { ChargingMove, MoveAttrMap, MoveAttrString, MoveClassMap, MoveKindString, MoveMessageFunc } from "#types/move-types"; import type { ChargingMove, MoveAttrMap, MoveAttrString, MoveClassMap, MoveKindString, MoveMessageFunc } from "#types/move-types";
import type { TurnMove } from "#types/turn-move"; 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 { coerceArray } from "#utils/array";
import { getEnumValues } from "#utils/enums"; import { getEnumValues } from "#utils/enums";
import { toCamelCase, toTitleCase } from "#utils/strings"; 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}. * Used for {@linkcode Moves.TRI_ATTACK} and {@linkcode Moves.DIRE_CLAW}.
*/ */
export class MultiStatusEffectAttr extends StatusEffectAttr { export class MultiStatusEffectAttr extends StatusEffectAttr {
public readonly effects: ReadonlyGenericUint8Array<StatusEffect>; public readonly effects: readonly StatusEffect[];
constructor(effects: StatusEffect[], selfTarget?: boolean) { constructor(effects: StatusEffect[], selfTarget?: boolean) {
super(effects[0], selfTarget); super(effects[0], selfTarget);
this.effects = new Uint8Array(effects); this.effects = effects;
} }
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
@ -2926,7 +2927,7 @@ export class StealEatBerryAttr extends EatBerryAttr {
*/ */
export class HealStatusEffectAttr extends MoveEffectAttr { export class HealStatusEffectAttr extends MoveEffectAttr {
/** List of Status Effects to cure */ /** List of Status Effects to cure */
private readonly effects: ReadonlyGenericUint8Array<StatusEffect>; private readonly effects: readonly StatusEffect[];
/** /**
* @param selfTarget - Whether this move targets the user * @param selfTarget - Whether this move targets the user
@ -2934,7 +2935,7 @@ export class HealStatusEffectAttr extends MoveEffectAttr {
*/ */
constructor(selfTarget: boolean, effects: StatusEffect | StatusEffect[]) { constructor(selfTarget: boolean, effects: StatusEffect | StatusEffect[]) {
super(selfTarget, { lastHitOnly: true }); super(selfTarget, { lastHitOnly: true });
this.effects = new Uint8Array(coerceArray(effects)); this.effects = coerceArray(effects);
} }
/** /**

View File

@ -15,7 +15,6 @@ import { WeatherType } from "#enums/weather-type";
import type { PlayerPokemon } from "#field/pokemon"; import type { PlayerPokemon } from "#field/pokemon";
import { AttackTypeBoosterModifier } from "#modifiers/modifier"; import { AttackTypeBoosterModifier } from "#modifiers/modifier";
import type { AttackTypeBoosterModifierType } from "#modifiers/modifier-type"; import type { AttackTypeBoosterModifierType } from "#modifiers/modifier-type";
import type { ReadonlyGenericUint8Array } from "#types/typed-arrays";
import { coerceArray } from "#utils/array"; import { coerceArray } from "#utils/array";
export interface EncounterRequirement { export interface EncounterRequirement {
@ -697,15 +696,15 @@ export class AbilityRequirement extends EncounterPokemonRequirement {
} }
export class StatusEffectRequirement extends EncounterPokemonRequirement { export class StatusEffectRequirement extends EncounterPokemonRequirement {
requiredStatusEffect: ReadonlyGenericUint8Array<StatusEffect>; requiredStatusEffect: readonly StatusEffect[];
minNumberOfPokemon: number; minNumberOfPokemon: number;
invertQuery: boolean; invertQuery: boolean;
constructor(statusEffect: StatusEffect | StatusEffect[], minNumberOfPokemon = 1, invertQuery = false) { constructor(statusEffect: StatusEffect | readonly StatusEffect[], minNumberOfPokemon = 1, invertQuery = false) {
super(); super();
this.minNumberOfPokemon = minNumberOfPokemon; this.minNumberOfPokemon = minNumberOfPokemon;
this.invertQuery = invertQuery; this.invertQuery = invertQuery;
this.requiredStatusEffect = new Uint8Array(coerceArray(statusEffect)); this.requiredStatusEffect = coerceArray(statusEffect);
} }
override meetsRequirement(): boolean { override meetsRequirement(): boolean {
@ -718,7 +717,7 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement {
return x; return x;
} }
override queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] { override queryParty(partyPokemon: readonly PlayerPokemon[]): PlayerPokemon[] {
if (!this.invertQuery) { if (!this.invertQuery) {
return partyPokemon.filter(pokemon => { return partyPokemon.filter(pokemon => {
return this.requiredStatusEffect.some(statusEffect => { 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. * If you want to trigger the event based on the form change enabler, use PersistentModifierRequirement.
*/ */
export class CanFormChangeWithItemRequirement extends EncounterPokemonRequirement { export class CanFormChangeWithItemRequirement extends EncounterPokemonRequirement {
requiredFormChangeItem: FormChangeItem[]; requiredFormChangeItem: readonly FormChangeItem[];
minNumberOfPokemon: number; minNumberOfPokemon: number;
invertQuery: boolean; invertQuery: boolean;
constructor(formChangeItem: FormChangeItem | FormChangeItem[], minNumberOfPokemon = 1, invertQuery = false) { constructor(formChangeItem: FormChangeItem | readonly FormChangeItem[], minNumberOfPokemon = 1, invertQuery = false) {
super(); super();
this.minNumberOfPokemon = minNumberOfPokemon; this.minNumberOfPokemon = minNumberOfPokemon;
this.invertQuery = invertQuery; 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) { if (!this.invertQuery) {
return partyPokemon.filter( return partyPokemon.filter(
pokemon => pokemon =>
@ -878,13 +877,13 @@ export class HeldItemRequirement extends EncounterPokemonRequirement {
} }
export class AttackTypeBoosterHeldItemTypeRequirement extends EncounterPokemonRequirement { export class AttackTypeBoosterHeldItemTypeRequirement extends EncounterPokemonRequirement {
requiredHeldItemTypes: PokemonType[]; requiredHeldItemTypes: readonly PokemonType[];
minNumberOfPokemon: number; minNumberOfPokemon: number;
invertQuery: boolean; invertQuery: boolean;
requireTransferable: boolean; requireTransferable: boolean;
constructor( constructor(
heldItemTypes: PokemonType | PokemonType[], heldItemTypes: PokemonType | readonly PokemonType[],
minNumberOfPokemon = 1, minNumberOfPokemon = 1,
invertQuery = false, invertQuery = false,
requireTransferable = true, requireTransferable = true,

View File

@ -319,7 +319,7 @@ export async function initBattleWithEnemyConfig(partyConfig: EnemyPartyConfig):
// Set IVs // Set IVs
if (config.ivs) { if (config.ivs) {
enemyPokemon.ivs = new Uint8Array(config.ivs); enemyPokemon.ivs = config.ivs;
} }
// Set Status // Set Status

View File

@ -24,7 +24,8 @@ import { SpeciesFormKey } from "#enums/species-form-key";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
import { WeatherType } from "#enums/weather-type"; import { WeatherType } from "#enums/weather-type";
import type { Pokemon } from "#field/pokemon"; 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 SpeciesFormChangeConditionPredicate = (p: Pokemon) => boolean;
export type SpeciesFormChangeConditionEnforceFunc = (p: Pokemon) => void; export type SpeciesFormChangeConditionEnforceFunc = (p: Pokemon) => void;

View File

@ -11,9 +11,8 @@ import type { TimeOfDay } from "#enums/time-of-day";
import { WeatherType } from "#enums/weather-type"; import { WeatherType } from "#enums/weather-type";
import type { Pokemon } from "#field/pokemon"; import type { Pokemon } from "#field/pokemon";
import type { PokemonFormChangeItemModifier } from "#modifiers/modifier"; 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 { coerceArray } from "#utils/array";
import type { Constructor } from "#utils/common";
import { toCamelCase } from "#utils/strings"; import { toCamelCase } from "#utils/strings";
import i18next from "i18next"; import i18next from "i18next";
@ -123,12 +122,12 @@ export class SpeciesFormChangeActiveTrigger extends SpeciesFormChangeTrigger {
} }
export class SpeciesFormChangeStatusEffectTrigger extends SpeciesFormChangeTrigger { export class SpeciesFormChangeStatusEffectTrigger extends SpeciesFormChangeTrigger {
public readonly statusEffects: ReadonlyGenericUint8Array<StatusEffect>; public readonly statusEffects: readonly StatusEffect[];
public invert: boolean; public invert: boolean;
constructor(statusEffects: StatusEffect | StatusEffect[], invert = false) { constructor(statusEffects: StatusEffect | StatusEffect[], invert = false) {
super(); super();
this.statusEffects = new Uint8Array(coerceArray(statusEffects)); this.statusEffects = coerceArray(statusEffects);
this.invert = invert; this.invert = invert;
// this.description = i18next.t("pokemonEvolutions:forms.statusEffect"); // this.description = i18next.t("pokemonEvolutions:forms.statusEffect");
} }

View File

@ -16,7 +16,6 @@ import type { AttackMoveResult } from "#types/attack-move-result";
import type { IllusionData } from "#types/illusion-data"; import type { IllusionData } from "#types/illusion-data";
import type { TurnMove } from "#types/turn-move"; import type { TurnMove } from "#types/turn-move";
import type { CoerceNullPropertiesToUndefined } from "#types/type-helpers"; import type { CoerceNullPropertiesToUndefined } from "#types/type-helpers";
import { setTypedArray } from "#utils/array";
import { getPokemonSpeciesForm } from "#utils/pokemon-utils"; import { getPokemonSpeciesForm } from "#utils/pokemon-utils";
/** /**
@ -130,7 +129,7 @@ export class PokemonSummonData {
public passiveAbility: AbilityId | undefined; public passiveAbility: AbilityId | undefined;
public gender: Gender | undefined; public gender: Gender | undefined;
public fusionGender: 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; public moveset: PokemonMove[] | null;
// If not initialized this value will not be populated from save data. // If not initialized this value will not be populated from save data.
@ -166,11 +165,6 @@ export class PokemonSummonData {
continue; continue;
} }
if (key === "stats") {
setTypedArray(this.stats, source.stats);
continue;
}
if (key === "illusion" && typeof value === "object") { if (key === "illusion" && typeof value === "object") {
// Make a copy so as not to mutate provided value // Make a copy so as not to mutate provided value
const illusionData = { 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` // We coerce null to undefined in the type, as the for loop below replaces `null` with `undefined`
...(this as Omit< ...(this as Omit<
CoerceNullPropertiesToUndefined<PokemonSummonData>, CoerceNullPropertiesToUndefined<PokemonSummonData>,
"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 }, speciesForm: speciesForm == null ? undefined : { id: speciesForm.speciesId, formIdx: speciesForm.formIndex },
fusionSpeciesForm: fusionSpeciesForm:
fusionSpeciesForm == null fusionSpeciesForm == null

View File

@ -1,7 +1,7 @@
import { DelayedAttackTag, type PositionalTag, WishTag } from "#data/positional-tags/positional-tag"; import { DelayedAttackTag, type PositionalTag, WishTag } from "#data/positional-tags/positional-tag";
import { PositionalTagType } from "#enums/positional-tag-type"; import { PositionalTagType } from "#enums/positional-tag-type";
import type { Constructor } from "#types/common";
import type { ObjectValues } from "#types/type-helpers"; import type { ObjectValues } from "#types/type-helpers";
import type { Constructor } from "#utils/common";
/** /**
* Load the attributes of a {@linkcode PositionalTag}. * Load the attributes of a {@linkcode PositionalTag}.

View File

@ -35,8 +35,9 @@ import { TagAddedEvent, TagRemovedEvent, TerrainChangedEvent, WeatherChangedEven
import type { Pokemon } from "#field/pokemon"; import type { Pokemon } from "#field/pokemon";
import { FieldEffectModifier } from "#modifiers/modifier"; import { FieldEffectModifier } from "#modifiers/modifier";
import type { Move } from "#moves/move"; import type { Move } from "#moves/move";
import type { Constructor } from "#types/common";
import type { AbstractConstructor } from "#types/type-helpers"; 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"; import { getPokemonSpecies } from "#utils/pokemon-utils";
export class Arena { export class Arena {

View File

@ -141,22 +141,21 @@ import type { PokemonData } from "#system/pokemon-data";
import { RibbonData } from "#system/ribbons/ribbon-data"; import { RibbonData } from "#system/ribbons/ribbon-data";
import { awardRibbonsToSpeciesLine } from "#system/ribbons/ribbon-methods"; import { awardRibbonsToSpeciesLine } from "#system/ribbons/ribbon-methods";
import type { AbAttrMap, AbAttrString, TypeMultiplierAbAttrParams } from "#types/ability-types"; 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 { getAttackDamageParams, getBaseDamageParams } from "#types/damage-params";
import type { DamageCalculationResult, DamageResult } from "#types/damage-result"; import type { DamageCalculationResult, DamageResult } from "#types/damage-result";
import type { IllusionData } from "#types/illusion-data"; import type { IllusionData } from "#types/illusion-data";
import type { StarterDataEntry, StarterMoveset } from "#types/save-data"; import type { StarterDataEntry, StarterMoveset } from "#types/save-data";
import type { TurnMove } from "#types/turn-move"; import type { TurnMove } from "#types/turn-move";
import type { ReadonlyUint8Array } from "#types/typed-arrays";
import { BattleInfo } from "#ui/battle-info"; import { BattleInfo } from "#ui/battle-info";
import { EnemyBattleInfo } from "#ui/enemy-battle-info"; import { EnemyBattleInfo } from "#ui/enemy-battle-info";
import type { PartyOption } from "#ui/party-ui-handler"; import type { PartyOption } from "#ui/party-ui-handler";
import { PartyUiHandler, PartyUiMode } from "#ui/party-ui-handler"; import { PartyUiHandler, PartyUiMode } from "#ui/party-ui-handler";
import { PlayerBattleInfo } from "#ui/player-battle-info"; 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 { applyChallenges } from "#utils/challenge-utils";
import { import {
BooleanHolder, BooleanHolder,
type Constructor,
deltaRgb, deltaRgb,
fixedInt, fixedInt,
getIvsFromId, getIvsFromId,
@ -204,8 +203,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
public levelExp: number; public levelExp: number;
public gender: Gender; public gender: Gender;
public hp: number; public hp: number;
public stats = Uint32Array.of(1, 1, 1, 1, 1, 1); public stats: number[];
public ivs = Uint8Array.of(0, 0, 0, 0, 0, 0); public ivs: number[];
public nature: Nature; public nature: Nature;
public moveset: PokemonMove[]; public moveset: PokemonMove[];
/** /**
@ -312,7 +311,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
gender?: Gender, gender?: Gender,
shiny?: boolean, shiny?: boolean,
variant?: Variant, variant?: Variant,
ivs?: ReadonlyUint8Array | number[], ivs?: number[],
nature?: Nature, nature?: Nature,
dataSource?: Pokemon | PokemonData, dataSource?: Pokemon | PokemonData,
) { ) {
@ -346,8 +345,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
if (dataSource) { if (dataSource) {
this.id = dataSource.id; this.id = dataSource.id;
this.hp = dataSource.hp; this.hp = dataSource.hp;
setTypedArray(this.stats, dataSource.stats); this.stats = dataSource.stats;
setTypedArray(this.ivs, dataSource.ivs ?? getIvsFromId(dataSource.id)); this.ivs = dataSource.ivs;
this.passive = !!dataSource.passive; this.passive = !!dataSource.passive;
if (this.variant === undefined) { if (this.variant === undefined) {
this.variant = 0; this.variant = 0;
@ -386,7 +385,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
this.stellarTypesBoosted = dataSource.stellarTypesBoosted ?? []; this.stellarTypesBoosted = dataSource.stellarTypesBoosted ?? [];
} else { } else {
this.id = randSeedInt(4294967296); this.id = randSeedInt(4294967296);
setTypedArray(this.ivs, ivs ?? getIvsFromId(this.id)); this.ivs = ivs || getIvsFromId(this.id);
if (this.gender === undefined) { if (this.gender === undefined) {
this.gender = this.species.generateGender(); 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` * @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. * @returns The numeric values of this {@linkcode Pokemon}'s stats as an array.
*/ */
getStats(bypassSummonData = true): Uint32Array { getStats(bypassSummonData = true): number[] {
if (!bypassSummonData) { if (!bypassSummonData) {
// Only grab summon data stats if nonzero // Only grab summon data stats if nonzero
return this.summonData.stats.map((s, i) => s || this.stats[i]); 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 { calculateStats(): void {
if (!this.stats) {
this.stats = [0, 0, 0, 0, 0, 0];
}
// Get and manipulate base stats // Get and manipulate base stats
const baseStats = this.calculateBaseStats(); const baseStats = this.calculateBaseStats();
// Using base stats, calculate and store stats one by one // 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); 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); this.setStat(s, statHolder.value);
} }
@ -5701,7 +5704,7 @@ export class PlayerPokemon extends Pokemon {
gender?: Gender, gender?: Gender,
shiny?: boolean, shiny?: boolean,
variant?: Variant, variant?: Variant,
ivs?: ReadonlyUint8Array | number[], ivs?: number[],
nature?: Nature, nature?: Nature,
dataSource?: Pokemon | PokemonData, dataSource?: Pokemon | PokemonData,
) { ) {
@ -6321,9 +6324,9 @@ export class EnemyPokemon extends Pokemon {
if (this.hasTrainer() && globalScene.currentBattle) { if (this.hasTrainer() && globalScene.currentBattle) {
const { waveIndex } = globalScene.currentBattle; const { waveIndex } = globalScene.currentBattle;
const ivs = new Uint8Array(6); const ivs: number[] = [];
for (let i = 0; i < 6; i++) { while (ivs.length < 6) {
ivs[i] = this.randBattleSeedIntRange(Math.floor(waveIndex / 10), 31); ivs.push(randSeedIntRange(Math.floor(waveIndex / 10), 31));
} }
this.ivs = ivs; this.ivs = ivs;
} }

View File

@ -1914,7 +1914,7 @@ export class GameData {
_unlockSpeciesNature(species.speciesId); _unlockSpeciesNature(species.speciesId);
} }
updateSpeciesDexIvs(speciesId: SpeciesId, ivs: Uint8Array): void { updateSpeciesDexIvs(speciesId: SpeciesId, ivs: number[]): void {
let dexEntry: DexEntry; let dexEntry: DexEntry;
do { do {
dexEntry = globalScene.gameData.dexData[speciesId]; dexEntry = globalScene.gameData.dexData[speciesId];

View File

@ -99,8 +99,8 @@ export class PokemonData {
this.levelExp = source.levelExp; this.levelExp = source.levelExp;
this.gender = source.gender; this.gender = source.gender;
this.hp = source.hp; this.hp = source.hp;
this.stats = Array.from(source.stats); this.stats = source.stats;
this.ivs = Array.from(source.ivs); this.ivs = source.ivs;
// TODO: Can't we move some of this verification stuff to an upgrade script? // TODO: Can't we move some of this verification stuff to an upgrade script?
this.nature = source.nature ?? Nature.HARDY; this.nature = source.nature ?? Nature.HARDY;
@ -162,7 +162,7 @@ export class PokemonData {
this.gender, this.gender,
this.shiny, this.shiny,
this.variant, this.variant,
new Uint8Array(this.ivs.slice(0, 6)), this.ivs,
this.nature, this.nature,
this, this,
playerPokemon => { playerPokemon => {

View File

@ -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) { if (ivs) {
const ivChartData = new Array(6) const ivChartData = new Array(6)
.fill(null) .fill(null)

View File

@ -195,7 +195,7 @@ export class BattleMessageUiHandler extends MessageUiHandler {
super.showDialogue(text, name, delay, callback, callbackDelay, prompt, promptDelay); super.showDialogue(text, name, delay, callback, callbackDelay, prompt, promptDelay);
} }
promptLevelUpStats(partyMemberIndex: number, prevStats: ArrayLike<number>, showTotals: boolean): Promise<void> { promptLevelUpStats(partyMemberIndex: number, prevStats: number[], showTotals: boolean): Promise<void> {
return new Promise(resolve => { return new Promise(resolve => {
if (!globalScene.showLevelUpStats) { if (!globalScene.showLevelUpStats) {
return resolve(); return resolve();
@ -219,7 +219,7 @@ export class BattleMessageUiHandler extends MessageUiHandler {
}); });
} }
promptIvs(pokemonId: number, ivs: ArrayLike<number>): Promise<void> { promptIvs(pokemonId: number, ivs: number[]): Promise<void> {
return new Promise(resolve => { return new Promise(resolve => {
globalScene.executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
let levelUpStatsValuesText = ""; let levelUpStatsValuesText = "";

View File

@ -2759,7 +2759,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
pokerus: this.pokerusSpecies.includes(species), pokerus: this.pokerusSpecies.includes(species),
nickname: this.starterPreferences[species.speciesId]?.nickname, nickname: this.starterPreferences[species.speciesId]?.nickname,
teraType, teraType,
ivs: new Uint8Array(dexEntry.ivs), ivs: dexEntry.ivs,
}; };
this.starters.push(starter); this.starters.push(starter);

View File

@ -12,7 +12,7 @@ import type {
* If the input isn't already an array, turns it into one. * 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 * @returns An array with the same type as the type of the input
*/ */
export function coerceArray<T>(input: T): T extends any[] ? T : [T]; export function coerceArray<T>(input: T): T extends readonly any[] ? T : [T];
export function coerceArray<T>(input: T): T | [T] { export function coerceArray<T>(input: T): T | [T] {
return Array.isArray(input) ? input : [input]; return Array.isArray(input) ? input : [input];
} }

View File

@ -176,15 +176,15 @@ export function getPlayTimeString(totalSeconds: number): string {
* @param id 32-bit number * @param id 32-bit number
* @returns An array of six numbers corresponding to 5-bit chunks from {@linkcode id} * @returns An array of six numbers corresponding to 5-bit chunks from {@linkcode id}
*/ */
export function getIvsFromId(id: number): Uint8Array { export function getIvsFromId(id: number): [number, number, number, number, number, number] {
return Uint8Array.of( return [
(id & 0x3e000000) >>> 25, (id & 0x3e000000) >>> 25,
(id & 0x01f00000) >>> 20, (id & 0x01f00000) >>> 20,
(id & 0x000f8000) >>> 15, (id & 0x000f8000) >>> 15,
(id & 0x00007c00) >>> 10, (id & 0x00007c00) >>> 10,
(id & 0x000003e0) >>> 5, (id & 0x000003e0) >>> 5,
id & 0x0000001f, id & 0x0000001f,
); ];
} }
export function formatLargeNumber(count: number, threshold: number): string { export function formatLargeNumber(count: number, threshold: number): string {
@ -292,9 +292,6 @@ export async function localPing(): Promise<void> {
} }
} }
/** Alias for the constructor of a class */
export type Constructor<T> = new (...args: unknown[]) => T;
export class BooleanHolder { export class BooleanHolder {
public value: boolean; public value: boolean;

View File

@ -38,7 +38,7 @@ describe("Abilities - Beast Boost", () => {
const playerPokemon = game.field.getPlayerPokemon(); const playerPokemon = game.field.getPlayerPokemon();
// Set the pokemon's highest stat to DEF, so it should be picked by Beast Boost // 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); console.log(playerPokemon.stats);
expect(playerPokemon.getStatStage(Stat.DEF)).toBe(0); expect(playerPokemon.getStatStage(Stat.DEF)).toBe(0);
@ -56,7 +56,7 @@ describe("Abilities - Beast Boost", () => {
const playerPokemon = game.field.getPlayerPokemon(); const playerPokemon = game.field.getPlayerPokemon();
// If the opponent uses Guard Split, the pokemon's second highest stat (SPATK) should be chosen // 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); expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(0);
@ -75,7 +75,7 @@ describe("Abilities - Beast Boost", () => {
const playerPokemon = game.field.getPlayerPokemon(); const playerPokemon = game.field.getPlayerPokemon();
// Set up tie between SPATK, SPDEF, and SPD, where SPATK should win // 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); expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(0);

View File

@ -39,8 +39,8 @@ describe("Battle order", () => {
const enemyPokemon = game.field.getEnemyPokemon(); const enemyPokemon = game.field.getEnemyPokemon();
const enemyStartHp = enemyPokemon.hp; 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(playerPokemon, "stats", "get").mockReturnValue([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(enemyPokemon, "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);
await game.phaseInterceptor.to("MoveEndPhase", false); await game.phaseInterceptor.to("MoveEndPhase", false);
@ -55,8 +55,8 @@ describe("Battle order", () => {
const playerStartHp = playerPokemon.hp; const playerStartHp = playerPokemon.hp;
const enemyPokemon = game.field.getEnemyPokemon(); const enemyPokemon = game.field.getEnemyPokemon();
const enemyStartHp = enemyPokemon.hp; 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(playerPokemon, "stats", "get").mockReturnValue([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(enemyPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 50]); // set enemyPokemon's speed to 50
game.move.select(MoveId.TACKLE); game.move.select(MoveId.TACKLE);
@ -74,8 +74,8 @@ describe("Battle order", () => {
const enemyPokemon = game.scene.getEnemyField(); const enemyPokemon = game.scene.getEnemyField();
const enemyHps = enemyPokemon.map(p => p.hp); 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 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(Uint32Array.of(20, 20, 20, 20, 20, 150))); // set both enemyPokemons' speed to 150 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);
game.move.select(MoveId.TACKLE, 1); game.move.select(MoveId.TACKLE, 1);
@ -96,9 +96,9 @@ describe("Battle order", () => {
const playerPokemon = game.scene.getPlayerField(); const playerPokemon = game.scene.getPlayerField();
const enemyPokemon = game.scene.getEnemyField(); 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 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(Uint32Array.of(20, 20, 20, 20, 20, 100)); // set enemyPokemon's 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(Uint32Array.of(20, 20, 20, 20, 20, 150)); // set enemyPokemon's speed to 150 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);
game.move.select(MoveId.TACKLE, 1); game.move.select(MoveId.TACKLE, 1);
@ -114,10 +114,10 @@ describe("Battle order", () => {
const playerPokemon = game.scene.getPlayerField(); const playerPokemon = game.scene.getPlayerField();
const enemyPokemon = game.scene.getEnemyField(); 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[0], "stats", "get").mockReturnValue([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(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(Uint32Array.of(20, 20, 20, 20, 20, 100)); // set one enemyPokemon's speed to 100 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(Uint32Array.of(20, 20, 20, 20, 20, 150)); // set other enemyPokemon's speed to 150 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);
game.move.select(MoveId.TACKLE, 1); game.move.select(MoveId.TACKLE, 1);

View File

@ -38,7 +38,7 @@ describe("Escape chance calculations", () => {
const enemyField = game.scene.getEnemyField(); const enemyField = game.scene.getEnemyField();
const enemySpeed = 100; const enemySpeed = 100;
// set enemyPokemon's speed to 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; const commandPhase = game.scene.phaseManager.getCurrentPhase() as CommandPhase;
commandPhase.handleCommand(Command.RUN, 0); commandPhase.handleCommand(Command.RUN, 0);
@ -81,9 +81,14 @@ describe("Escape chance calculations", () => {
// set the number of escape attempts to the required amount // set the number of escape attempts to the required amount
game.scene.currentBattle.escapeAttempts = check.escapeAttempts; game.scene.currentBattle.escapeAttempts = check.escapeAttempts;
// set playerPokemon's speed to a multiple of the enemySpeed // set playerPokemon's speed to a multiple of the enemySpeed
vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue( vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue([
Uint32Array.of(20, 20, 20, 20, 20, check.pokemonSpeedRatio * enemySpeed), 20,
); 20,
20,
20,
20,
check.pokemonSpeedRatio * enemySpeed,
]);
const chance = phase.calculateEscapeChance(game.scene.currentBattle.escapeAttempts); const chance = phase.calculateEscapeChance(game.scene.currentBattle.escapeAttempts);
expect(chance).toBe(check.expectedEscapeChance); 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 // this is used to find the ratio of the player's first pokemon
const playerASpeedPercentage = 0.4; const playerASpeedPercentage = 0.4;
// set enemyAPokemon's speed to 70 // 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 // 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; const commandPhase = game.scene.phaseManager.getCurrentPhase() as CommandPhase;
commandPhase.handleCommand(Command.RUN, 0); commandPhase.handleCommand(Command.RUN, 0);
@ -146,20 +151,23 @@ describe("Escape chance calculations", () => {
// sets the number of escape attempts to the required amount // sets the number of escape attempts to the required amount
game.scene.currentBattle.escapeAttempts = check.escapeAttempts; game.scene.currentBattle.escapeAttempts = check.escapeAttempts;
// set the first playerPokemon's speed to a multiple of the enemySpeed // set the first playerPokemon's speed to a multiple of the enemySpeed
vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue( vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue([
Uint32Array.of( 20,
20, 20,
20, 20,
20, 20,
20, 20,
20, Math.floor(check.pokemonSpeedRatio * totalEnemySpeed * playerASpeedPercentage),
Math.floor(check.pokemonSpeedRatio * totalEnemySpeed * playerASpeedPercentage), ]);
),
);
// set the second playerPokemon's speed to the remaining value of speed // set the second playerPokemon's speed to the remaining value of speed
vi.spyOn(playerPokemon[1], "stats", "get").mockReturnValue( vi.spyOn(playerPokemon[1], "stats", "get").mockReturnValue([
Uint32Array.of(20, 20, 20, 20, 20, check.pokemonSpeedRatio * totalEnemySpeed - playerPokemon[0].stats[5]), 20,
); 20,
20,
20,
20,
check.pokemonSpeedRatio * totalEnemySpeed - playerPokemon[0].stats[5],
]);
const chance = phase.calculateEscapeChance(game.scene.currentBattle.escapeAttempts); const chance = phase.calculateEscapeChance(game.scene.currentBattle.escapeAttempts);
// checks to make sure the escape values are the same // checks to make sure the escape values are the same
expect(chance).toBe(check.expectedEscapeChance); expect(chance).toBe(check.expectedEscapeChance);
@ -176,7 +184,7 @@ describe("Escape chance calculations", () => {
const enemyField = game.scene.getEnemyField()!; const enemyField = game.scene.getEnemyField()!;
const enemySpeed = 100; const enemySpeed = 100;
// set enemyPokemon's speed to 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; const commandPhase = game.scene.phaseManager.getCurrentPhase() as CommandPhase;
commandPhase.handleCommand(Command.RUN, 0); commandPhase.handleCommand(Command.RUN, 0);
@ -233,9 +241,14 @@ describe("Escape chance calculations", () => {
// sets the number of escape attempts to the required amount // sets the number of escape attempts to the required amount
game.scene.currentBattle.escapeAttempts = check.escapeAttempts; game.scene.currentBattle.escapeAttempts = check.escapeAttempts;
// set playerPokemon's speed to a multiple of the enemySpeed // set playerPokemon's speed to a multiple of the enemySpeed
vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue( vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue([
Uint32Array.of(20, 20, 20, 20, 20, check.pokemonSpeedRatio * enemySpeed), 20,
); 20,
20,
20,
20,
check.pokemonSpeedRatio * enemySpeed,
]);
const chance = phase.calculateEscapeChance(game.scene.currentBattle.escapeAttempts); const chance = phase.calculateEscapeChance(game.scene.currentBattle.escapeAttempts);
expect(chance).toBe(check.expectedEscapeChance); 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 // this is used to find the ratio of the player's first pokemon
const playerASpeedPercentage = 0.8; const playerASpeedPercentage = 0.8;
// set enemyAPokemon's speed to 70 // 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 // 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; const commandPhase = game.scene.phaseManager.getCurrentPhase() as CommandPhase;
commandPhase.handleCommand(Command.RUN, 0); commandPhase.handleCommand(Command.RUN, 0);
@ -311,20 +324,23 @@ describe("Escape chance calculations", () => {
// sets the number of escape attempts to the required amount // sets the number of escape attempts to the required amount
game.scene.currentBattle.escapeAttempts = check.escapeAttempts; game.scene.currentBattle.escapeAttempts = check.escapeAttempts;
// set the first playerPokemon's speed to a multiple of the enemySpeed // set the first playerPokemon's speed to a multiple of the enemySpeed
vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue( vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue([
Uint32Array.of( 20,
20, 20,
20, 20,
20, 20,
20, 20,
20, Math.floor(check.pokemonSpeedRatio * totalEnemySpeed * playerASpeedPercentage),
Math.floor(check.pokemonSpeedRatio * totalEnemySpeed * playerASpeedPercentage), ]);
),
);
// set the second playerPokemon's speed to the remaining value of speed // set the second playerPokemon's speed to the remaining value of speed
vi.spyOn(playerPokemon[1], "stats", "get").mockReturnValue( vi.spyOn(playerPokemon[1], "stats", "get").mockReturnValue([
Uint32Array.of(20, 20, 20, 20, 20, check.pokemonSpeedRatio * totalEnemySpeed - playerPokemon[0].stats[5]), 20,
); 20,
20,
20,
20,
check.pokemonSpeedRatio * totalEnemySpeed - playerPokemon[0].stats[5],
]);
const chance = phase.calculateEscapeChance(game.scene.currentBattle.escapeAttempts); const chance = phase.calculateEscapeChance(game.scene.currentBattle.escapeAttempts);
// checks to make sure the escape values are the same // checks to make sure the escape values are the same
expect(chance).toBe(check.expectedEscapeChance); expect(chance).toBe(check.expectedEscapeChance);

View File

@ -45,10 +45,10 @@ describe("Moves - Rollout", () => {
await game.classicMode.startBattle(); await game.classicMode.startBattle();
const playerPkm = game.field.getPlayerPokemon(); 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(); 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 vi.spyOn(enemyPkm, "getHeldItems").mockReturnValue([]); //no berries
enemyPkm.hp = enemyPkm.getMaxHp(); enemyPkm.hp = enemyPkm.getMaxHp();

View File

@ -144,7 +144,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
expect(game).toBeAtPhase("CommandPhase"); expect(game).toBeAtPhase("CommandPhase");
expect(game.field.getEnemyPokemon().species.speciesId).toBe(SpeciesId.WOBBUFFET); 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); expect(game.field.getEnemyPokemon().nature).toBe(Nature.MILD);
game.onNextPrompt("MessagePhase", UiMode.MESSAGE, () => { game.onNextPrompt("MessagePhase", UiMode.MESSAGE, () => {

View File

@ -122,7 +122,7 @@ describe("Part-Timer - Mystery Encounter", () => {
// Override party levels to 50 so stats can be fully reflective // Override party levels to 50 so stats can be fully reflective
scene.getPlayerParty().forEach(p => { scene.getPlayerParty().forEach(p => {
p.level = 50; p.level = 50;
p.ivs = Uint8Array.of(20, 20, 20, 20, 20, 20); p.ivs = [20, 20, 20, 20, 20, 20];
p.calculateStats(); p.calculateStats();
}); });
await runMysteryEncounterToEnd(game, 1, { pokemonNo: 2 }); 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 // Override party levels to 50 so stats can be fully reflective
scene.getPlayerParty().forEach(p => { scene.getPlayerParty().forEach(p => {
p.level = 50; p.level = 50;
p.ivs = Uint8Array.of(0, 0, 0, 0, 0, 0); p.ivs = [0, 0, 0, 0, 0, 0];
p.calculateStats(); p.calculateStats();
}); });
await runMysteryEncounterToEnd(game, 2, { pokemonNo: 3 }); 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 // Override party levels to 50 so stats can be fully reflective
scene.getPlayerParty().forEach(p => { scene.getPlayerParty().forEach(p => {
p.level = 50; p.level = 50;
p.ivs = Uint8Array.of(20, 20, 20, 20, 20, 20); p.ivs = [20, 20, 20, 20, 20, 20];
p.calculateStats(); p.calculateStats();
}); });
await runMysteryEncounterToEnd(game, 2, { pokemonNo: 4 }); await runMysteryEncounterToEnd(game, 2, { pokemonNo: 4 });