mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-09-23 15:03:24 +02:00
Merge ac2f09ad33
into 8d5ba221d8
This commit is contained in:
commit
63563766c4
@ -1 +1,6 @@
|
||||
export type ConditionFn = (args?: any[]) => boolean;
|
||||
|
||||
/** Alias for the constructor of a class */
|
||||
export type Constructor<T> = new (...args: unknown[]) => T;
|
||||
|
||||
export type nil = null | undefined;
|
||||
|
@ -7,8 +7,8 @@ import type { TrainerPartyTemplate } from "#trainers/trainer-party-template";
|
||||
|
||||
export type PartyTemplateFunc = () => TrainerPartyTemplate;
|
||||
export type PartyMemberFunc = (level: number, strength: PartyMemberStrength) => EnemyPokemon;
|
||||
export type GenModifiersFunc = (party: EnemyPokemon[]) => PersistentModifier[];
|
||||
export type GenAIFunc = (party: EnemyPokemon[]) => void;
|
||||
export type GenModifiersFunc = (party: readonly EnemyPokemon[]) => PersistentModifier[];
|
||||
export type GenAIFunc = (party: readonly EnemyPokemon[]) => void;
|
||||
|
||||
export interface TrainerTierPools {
|
||||
[key: number]: SpeciesId[];
|
||||
|
524
src/@types/typed-arrays.ts
Normal file
524
src/@types/typed-arrays.ts
Normal file
@ -0,0 +1,524 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Pagefault Games
|
||||
* SPDX-FileContributor: SirzBenjie
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
/**
|
||||
* Collection of utility types for working with
|
||||
* {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray | TypedArray}
|
||||
* with enhanced type safety and usability.
|
||||
* @module
|
||||
*/
|
||||
|
||||
/**
|
||||
* Union type of all {@linkcode https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray | TypedArray}s
|
||||
*/
|
||||
export type TypedArray =
|
||||
| Int8Array
|
||||
| Uint8Array
|
||||
| Uint8ClampedArray
|
||||
| Int16Array
|
||||
| Uint16Array
|
||||
| Int32Array
|
||||
| Uint32Array
|
||||
| Float32Array
|
||||
| Float64Array
|
||||
| BigInt64Array
|
||||
| BigUint64Array;
|
||||
|
||||
/**
|
||||
* A readonly version of {@linkcode https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int8Array | Int8Array}
|
||||
*
|
||||
* @remarks
|
||||
* Is to `Int8Array` what `ReadonlyArray` is to `Array`
|
||||
*/
|
||||
export interface ReadonlyInt8Array<TArrayBuffer extends ArrayBufferLike = ArrayBufferLike>
|
||||
extends Omit<Int8Array, "fill" | "set" | "sort" | "reverse" | "copyWithin" | "subarray"> {
|
||||
subarray(begin?: number, end?: number): ReadonlyInt8Array<TArrayBuffer>;
|
||||
readonly [index: number]: number;
|
||||
}
|
||||
/**
|
||||
* A readonly version of {@linkcode https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array | Uint8Array}
|
||||
*
|
||||
* @remarks
|
||||
* Is to `Uint8Array` what `ReadonlyArray` is to `Array`
|
||||
*/
|
||||
export interface ReadonlyUint8Array<TArrayBuffer extends ArrayBufferLike = ArrayBufferLike>
|
||||
extends Omit<Uint8Array, "fill" | "set" | "sort" | "reverse" | "copyWithin" | "subarray"> {
|
||||
subarray(begin?: number, end?: number): ReadonlyUint8Array<TArrayBuffer>;
|
||||
readonly [index: number]: number;
|
||||
}
|
||||
/**
|
||||
* A readonly version of {@linkcode https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8ClampedArray | Uint8ClampedArray}
|
||||
*
|
||||
* @remarks
|
||||
* Is to `Uint8ClampedArray` what `ReadonlyArray` is to `Array`
|
||||
*/
|
||||
export interface ReadonlyUint8ClampedArray<TArrayBuffer extends ArrayBufferLike = ArrayBufferLike>
|
||||
extends Omit<Uint8ClampedArray<TArrayBuffer>, "fill" | "set" | "sort" | "reverse" | "copyWithin" | "subarray"> {
|
||||
subarray(begin?: number, end?: number): ReadonlyUint8ClampedArray<TArrayBuffer>;
|
||||
readonly [index: number]: number;
|
||||
}
|
||||
/**
|
||||
* A readonly version of {@linkcode https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int16Array | Int16Array}
|
||||
*
|
||||
* @remarks
|
||||
* Is to `Int16Array` what `ReadonlyArray` is to `Array`
|
||||
*/
|
||||
export interface ReadonlyInt16Array<TArrayBuffer extends ArrayBufferLike = ArrayBufferLike>
|
||||
extends Omit<Int16Array<TArrayBuffer>, "fill" | "set" | "sort" | "reverse" | "copyWithin" | "subarray"> {
|
||||
subarray(begin?: number, end?: number): ReadonlyInt16Array<TArrayBuffer>;
|
||||
readonly [index: number]: number;
|
||||
}
|
||||
/**
|
||||
* A readonly version of {@linkcode https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint16Array | Uint16Array}
|
||||
*
|
||||
* @remarks
|
||||
* Is to `Uint16Array` what `ReadonlyArray` is to `Array`
|
||||
*/
|
||||
export interface ReadonlyUint16Array<TArrayBuffer extends ArrayBufferLike = ArrayBufferLike>
|
||||
extends Omit<Uint16Array<TArrayBuffer>, "fill" | "set" | "sort" | "reverse" | "copyWithin" | "subarray"> {
|
||||
subarray(begin?: number, end?: number): ReadonlyUint16Array<TArrayBuffer>;
|
||||
readonly [index: number]: number;
|
||||
}
|
||||
/**
|
||||
* A readonly version of {@linkcode https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int32Array | Int32Array}
|
||||
*
|
||||
* @remarks
|
||||
* Is to `Int32Array` what `ReadonlyArray` is to `Array`
|
||||
*/
|
||||
export interface ReadonlyInt32Array<TArrayBuffer extends ArrayBufferLike = ArrayBufferLike>
|
||||
extends Omit<Int32Array<TArrayBuffer>, "fill" | "set" | "sort" | "reverse" | "copyWithin" | "subarray"> {
|
||||
subarray(begin?: number, end?: number): ReadonlyInt32Array<TArrayBuffer>;
|
||||
readonly [index: number]: number;
|
||||
}
|
||||
/**
|
||||
* A readonly version of {@linkcode https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint32Array | Uint32Array}
|
||||
*
|
||||
* @remarks
|
||||
* Is to `Uint32Array` what `ReadonlyArray` is to `Array`
|
||||
*/
|
||||
export interface ReadonlyUint32Array<TArrayBuffer extends ArrayBufferLike = ArrayBufferLike>
|
||||
extends Omit<Uint32Array<TArrayBuffer>, "fill" | "set" | "sort" | "reverse" | "copyWithin" | "subarray"> {
|
||||
subarray(begin?: number, end?: number): ReadonlyUint32Array<TArrayBuffer>;
|
||||
readonly [index: number]: number;
|
||||
}
|
||||
/**
|
||||
* A readonly version of {@linkcode https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array | Float32Array}
|
||||
*
|
||||
* @remarks
|
||||
* Is to `Float32Array` what `ReadonlyArray` is to `Array`
|
||||
*/
|
||||
export interface ReadonlyFloat32Array<TArrayBuffer extends ArrayBufferLike = ArrayBufferLike>
|
||||
extends Omit<Float32Array<TArrayBuffer>, "fill" | "set" | "sort" | "reverse" | "copyWithin" | "subarray"> {
|
||||
subarray(begin?: number, end?: number): ReadonlyFloat32Array<TArrayBuffer>;
|
||||
readonly [index: number]: number;
|
||||
}
|
||||
/**
|
||||
* A readonly version of {@linkcode https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float64Array | Float64Array}
|
||||
*
|
||||
* @remarks
|
||||
* Is to `Float64Array` what `ReadonlyArray` is to `Array`
|
||||
*/
|
||||
export interface ReadonlyFloat64Array<TArrayBuffer extends ArrayBufferLike = ArrayBufferLike>
|
||||
extends Omit<Float64Array, "fill" | "set" | "sort" | "reverse" | "copyWithin" | "subarray"> {
|
||||
subarray(begin?: number, end?: number): ReadonlyFloat64Array<TArrayBuffer>;
|
||||
readonly [index: number]: number;
|
||||
}
|
||||
/**
|
||||
* A readonly version of {@linkcode https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt64Array | BigInt64Array}
|
||||
*
|
||||
* @remarks
|
||||
* Is to `BigInt64Array` what `ReadonlyArray` is to `Array`
|
||||
*/
|
||||
export interface ReadonlyBigInt64Array<TArrayBuffer extends ArrayBufferLike = ArrayBufferLike>
|
||||
extends Omit<BigInt64Array, "fill" | "set" | "sort" | "reverse" | "copyWithin" | "subarray"> {
|
||||
subarray(begin?: number, end?: number): ReadonlyBigInt64Array<TArrayBuffer>;
|
||||
readonly [index: number]: bigint;
|
||||
}
|
||||
/**
|
||||
* A readonly version of {@linkcode https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigUint64Array | BigUint64Array}
|
||||
*
|
||||
* @remarks
|
||||
* Is to `BigUint64Array` what `ReadonlyArray` is to `Array`
|
||||
*/
|
||||
export interface ReadonlyBigUint64Array<TArrayBuffer extends ArrayBufferLike = ArrayBufferLike>
|
||||
extends Omit<BigUint64Array, "fill" | "set" | "sort" | "reverse" | "copyWithin" | "subarray"> {
|
||||
subarray(begin?: number, end?: number): ReadonlyBigUint64Array<TArrayBuffer>;
|
||||
readonly [index: number]: bigint;
|
||||
}
|
||||
|
||||
export type ReadonlyTypedArray =
|
||||
| ReadonlyInt8Array
|
||||
| ReadonlyUint8Array
|
||||
| ReadonlyUint8ClampedArray
|
||||
| ReadonlyInt16Array
|
||||
| ReadonlyUint16Array
|
||||
| ReadonlyInt32Array
|
||||
| ReadonlyUint32Array
|
||||
| ReadonlyFloat32Array
|
||||
| ReadonlyFloat64Array
|
||||
| ReadonlyBigInt64Array
|
||||
| ReadonlyBigUint64Array;
|
||||
|
||||
/**
|
||||
* Either {@linkcode https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt64Array | BigInt64Array}
|
||||
* or {@linkcode https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt64Array | BigUint64Array}
|
||||
*/
|
||||
export type BigIntArray = BigInt64Array | BigUint64Array;
|
||||
|
||||
export type ReadonlyBigIntArray = ReadonlyBigInt64Array | ReadonlyBigUint64Array;
|
||||
|
||||
/** Any {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray | TypedArray} whose elements are not `bigint`s */
|
||||
export type NumberCompatibleTypedArray = Exclude<TypedArray, BigIntArray>;
|
||||
|
||||
export type ReadonlyNumberCompatibleTypedArray = Exclude<ReadonlyTypedArray, ReadonlyBigIntArray>;
|
||||
|
||||
/**
|
||||
* A partial interface of `Uint8Array` where methods that return the array type have been modified to return a more specific type.
|
||||
*
|
||||
* @privateRemarks
|
||||
* Excludes methods that return a new array (e.g. `subarray`, `slice`, `toReversed`, `toSorted`, `with`) as these cannot be resolved by typescript
|
||||
* @internal
|
||||
*/
|
||||
interface PartialUint8Array<T extends number> extends Uint8Array {
|
||||
at(index: number): T | undefined; // ES2022
|
||||
entries(): ArrayIterator<[number, T]>;
|
||||
fill(value: T, start?: number, end?: number): this;
|
||||
find(predicate: (value: T, index: number, array: this) => boolean, thisArg?: any): T | undefined;
|
||||
findLast(predicate: (value: number, index: number, array: this) => boolean, thisArg?: any): T | undefined;
|
||||
forEach(callbackfn: (value: T, index: number, array: this) => void, thisArg?: any): void;
|
||||
includes(searchElement: T, fromIndex?: number): boolean;
|
||||
indexOf(searchElement: T, fromIndex?: number): number;
|
||||
lastIndexOf(searchElement: T, fromIndex?: number): number;
|
||||
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: this) => T): T;
|
||||
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: this) => T): T;
|
||||
set(array: ArrayLike<T>, offset?: number): void;
|
||||
some(predicate: (value: T, index: number, obj: this) => boolean, thisArg?: any): boolean;
|
||||
sort(compareFn?: (a: T, b: T) => number): this;
|
||||
values(): ArrayIterator<T>;
|
||||
[Symbol.iterator](): ArrayIterator<T>;
|
||||
[index: number]: T;
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Uint8Array` whose elements typescript considers to be of type `T`, allowing for type-safe iteration and access.
|
||||
*
|
||||
* @remarks
|
||||
* Useful to leverage the benefits of `TypedArrays` without losing type information. Typescript will consider the elements to be of type `T` instead of just `number`.
|
||||
* @typeParam T - The specific numeric type of the elements in the array.
|
||||
*/
|
||||
// @ts-expect-error - These methods _will_ error, as we are overriding the return type to be more specific in a way that makes typescript unhappy
|
||||
export interface GenericUint8Array<T extends number> extends PartialUint8Array<T> {
|
||||
// map(callbackfn: (value: T, index: number, array: this) => T, thisArg?: any): GenericUint8Array<T>;
|
||||
// this method does not trigger a typescript error on its own, but if we add in `toReversed` it causes issues....
|
||||
subarray(begin?: number, end?: number): GenericUint8Array<T>;
|
||||
toReversed(): GenericUint8Array<T>;
|
||||
toSorted(compareFn?: (a: T, b: T) => number): GenericUint8Array<T>;
|
||||
filter(predicate: (value: T, index: number, array: this) => any, thisArg?: any): GenericUint8Array<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A partial interface of `Uint16Array` where methods that return the array type have been modified to return a more specific type.
|
||||
*
|
||||
* @privateRemarks
|
||||
* Excludes methods that return a new array (e.g. `subarray`, `slice`, `toReversed`, `toSorted`, `with`) as these cannot be resolved by typescript
|
||||
* @internal
|
||||
*/
|
||||
interface PartialUint16Array<T extends number> extends Uint16Array {
|
||||
at(index: number): T | undefined; // ES2022
|
||||
entries(): ArrayIterator<[number, T]>;
|
||||
fill(value: T, start?: number, end?: number): this;
|
||||
find(predicate: (value: T, index: number, array: this) => boolean, thisArg?: any): T | undefined;
|
||||
findLast(predicate: (value: number, index: number, array: this) => boolean, thisArg?: any): T | undefined;
|
||||
forEach(callbackfn: (value: T, index: number, array: this) => void, thisArg?: any): void;
|
||||
includes(searchElement: T, fromIndex?: number): boolean;
|
||||
indexOf(searchElement: T, fromIndex?: number): number;
|
||||
lastIndexOf(searchElement: T, fromIndex?: number): number;
|
||||
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: this) => T): T;
|
||||
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: this) => T): T;
|
||||
set(array: ArrayLike<T>, offset?: number): void;
|
||||
some(predicate: (value: T, index: number, obj: this) => boolean, thisArg?: any): boolean;
|
||||
sort(compareFn?: (a: T, b: T) => number): this;
|
||||
values(): ArrayIterator<T>;
|
||||
[Symbol.iterator](): ArrayIterator<T>;
|
||||
[index: number]: T;
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Uint16Array` whose elements typescript considers to be of type `T`, allowing for type-safe iteration and access.
|
||||
*
|
||||
* @remarks
|
||||
* Useful to leverage the benefits of `TypedArrays` without losing type information. Typescript will consider the elements to be of type `T` instead of just `number`.
|
||||
* @typeParam T - The specific numeric type of the elements in the array.
|
||||
*/
|
||||
// @ts-expect-error - These methods _will_ error, as we are overriding the return type to be more specific in a way that makes typescript unhappy
|
||||
export interface GenericUint16Array<T extends number> extends PartialUint16Array<T> {
|
||||
// map(callbackfn: (value: T, index: number, array: this) => T, thisArg?: any): GenericUint16Array<T>;
|
||||
// this method does not trigger a typescript error on its own, but if we add in `toReversed` it causes issues....
|
||||
subarray(begin?: number, end?: number): GenericUint16Array<T>;
|
||||
toReversed(): GenericUint16Array<T>;
|
||||
toSorted(compareFn?: (a: T, b: T) => number): GenericUint16Array<T>;
|
||||
filter(predicate: (value: T, index: number, array: this) => any, thisArg?: any): GenericUint16Array<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A partial interface of `Uint32Array` where methods that return the array type have been modified to return a more specific type.
|
||||
*
|
||||
* @privateRemarks
|
||||
* Excludes methods that return a new array (e.g. `subarray`, `slice`, `toReversed`, `toSorted`, `with`) as these cannot be resolved by typescript
|
||||
* @internal
|
||||
*/
|
||||
interface PartialUint32Array<T extends number> extends Uint32Array {
|
||||
at(index: number): T | undefined; // ES2022
|
||||
entries(): ArrayIterator<[number, T]>;
|
||||
fill(value: T, start?: number, end?: number): this;
|
||||
find(predicate: (value: T, index: number, array: this) => boolean, thisArg?: any): T | undefined;
|
||||
findLast(predicate: (value: number, index: number, array: this) => boolean, thisArg?: any): T | undefined;
|
||||
forEach(callbackfn: (value: T, index: number, array: this) => void, thisArg?: any): void;
|
||||
includes(searchElement: T, fromIndex?: number): boolean;
|
||||
indexOf(searchElement: T, fromIndex?: number): number;
|
||||
lastIndexOf(searchElement: T, fromIndex?: number): number;
|
||||
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: this) => T): T;
|
||||
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: this) => T): T;
|
||||
set(array: ArrayLike<T>, offset?: number): void;
|
||||
some(predicate: (value: T, index: number, obj: this) => boolean, thisArg?: any): boolean;
|
||||
sort(compareFn?: (a: T, b: T) => number): this;
|
||||
values(): ArrayIterator<T>;
|
||||
[Symbol.iterator](): ArrayIterator<T>;
|
||||
[index: number]: T;
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Uint32Array` whose elements typescript considers to be of type `T`, allowing for type-safe iteration and access.
|
||||
*
|
||||
* @remarks
|
||||
* Useful to leverage the benefits of `TypedArrays` without losing type information. Typescript will consider the elements to be of type `T` instead of just `number`.
|
||||
* @typeParam T - The specific numeric type of the elements in the array.
|
||||
*/
|
||||
// @ts-expect-error - These methods _will_ error, as we are overriding the return type to be more specific in a way that makes typescript unhappy
|
||||
export interface GenericUint32Array<T extends number> extends PartialUint32Array<T> {
|
||||
// map(callbackfn: (value: T, index: number, array: this) => T, thisArg?: any): GenericUint32Array<T>;
|
||||
// this method does not trigger a typescript error on its own, but if we add in `toReversed` it causes issues....
|
||||
subarray(begin?: number, end?: number): GenericUint32Array<T>;
|
||||
toReversed(): GenericUint32Array<T>;
|
||||
toSorted(compareFn?: (a: T, b: T) => number): GenericUint32Array<T>;
|
||||
filter(predicate: (value: T, index: number, array: this) => any, thisArg?: any): GenericUint32Array<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A partial interface of `Int8Array` where methods that return the array type have been modified to return a more specific type.
|
||||
*
|
||||
* @privateRemarks
|
||||
* Excludes methods that return a new array (e.g. `subarray`, `slice`, `toReversed`, `toSorted`, `with`) as these cannot be resolved by typescript
|
||||
* @internal
|
||||
*/
|
||||
interface PartialInt8Array<T extends number> extends Int8Array {
|
||||
at(index: number): T | undefined; // ES2022
|
||||
entries(): ArrayIterator<[number, T]>;
|
||||
fill(value: T, start?: number, end?: number): this;
|
||||
find(predicate: (value: T, index: number, array: this) => boolean, thisArg?: any): T | undefined;
|
||||
findLast(predicate: (value: number, index: number, array: this) => boolean, thisArg?: any): T | undefined;
|
||||
forEach(callbackfn: (value: T, index: number, array: this) => void, thisArg?: any): void;
|
||||
includes(searchElement: T, fromIndex?: number): boolean;
|
||||
indexOf(searchElement: T, fromIndex?: number): number;
|
||||
lastIndexOf(searchElement: T, fromIndex?: number): number;
|
||||
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: this) => T): T;
|
||||
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: this) => T): T;
|
||||
set(array: ArrayLike<T>, offset?: number): void;
|
||||
some(predicate: (value: T, index: number, obj: this) => boolean, thisArg?: any): boolean;
|
||||
sort(compareFn?: (a: T, b: T) => number): this;
|
||||
values(): ArrayIterator<T>;
|
||||
[Symbol.iterator](): ArrayIterator<T>;
|
||||
[index: number]: T;
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Int8Array` whose elements typescript considers to be of type `T`, allowing for type-safe iteration and access.
|
||||
*
|
||||
* @remarks
|
||||
* Useful to leverage the benefits of `TypedArrays` without losing type information. Typescript will consider the elements to be of type `T` instead of just `number`.
|
||||
* @typeParam T - The specific numeric type of the elements in the array.
|
||||
*/
|
||||
// @ts-expect-error - These methods _will_ error, as we are overriding the return type to be more specific in a way that makes typescript unhappy
|
||||
export interface GenericInt8Array<T extends number> extends PartialInt8Array<T> {
|
||||
// map(callbackfn: (value: T, index: number, array: this) => T, thisArg?: any): GenericInt8Array<T>;
|
||||
// this method does not trigger a typescript error on its own, but if we add in `toReversed` it causes issues....
|
||||
subarray(begin?: number, end?: number): GenericInt8Array<T>;
|
||||
toReversed(): GenericInt8Array<T>;
|
||||
toSorted(compareFn?: (a: T, b: T) => number): GenericInt8Array<T>;
|
||||
filter(predicate: (value: T, index: number, array: this) => any, thisArg?: any): GenericInt8Array<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A partial interface of `Int16Array` where methods that return the array type have been modified to return a more specific type.
|
||||
*
|
||||
* @privateRemarks
|
||||
* Excludes methods that return a new array (e.g. `subarray`, `slice`, `toReversed`, `toSorted`, `with`) as these cannot be resolved by typescript
|
||||
* @internal
|
||||
*/
|
||||
interface PartialInt16Array<T extends number> extends Int16Array {
|
||||
at(index: number): T | undefined; // ES2022
|
||||
entries(): ArrayIterator<[number, T]>;
|
||||
fill(value: T, start?: number, end?: number): this;
|
||||
find(predicate: (value: T, index: number, array: this) => boolean, thisArg?: any): T | undefined;
|
||||
findLast(predicate: (value: number, index: number, array: this) => boolean, thisArg?: any): T | undefined;
|
||||
forEach(callbackfn: (value: T, index: number, array: this) => void, thisArg?: any): void;
|
||||
includes(searchElement: T, fromIndex?: number): boolean;
|
||||
indexOf(searchElement: T, fromIndex?: number): number;
|
||||
lastIndexOf(searchElement: T, fromIndex?: number): number;
|
||||
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: this) => T): T;
|
||||
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: this) => T): T;
|
||||
set(array: ArrayLike<T>, offset?: number): void;
|
||||
some(predicate: (value: T, index: number, obj: this) => boolean, thisArg?: any): boolean;
|
||||
sort(compareFn?: (a: T, b: T) => number): this;
|
||||
values(): ArrayIterator<T>;
|
||||
[Symbol.iterator](): ArrayIterator<T>;
|
||||
[index: number]: T;
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Int16Array` whose elements typescript considers to be of type `T`, allowing for type-safe iteration and access.
|
||||
*
|
||||
* @remarks
|
||||
* Useful to leverage the benefits of `TypedArrays` without losing type information. Typescript will consider the elements to be of type `T` instead of just `number`.
|
||||
* @typeParam T - The specific numeric type of the elements in the array.
|
||||
*/
|
||||
// @ts-expect-error - These methods _will_ error, as we are overriding the return type to be more specific in a way that makes typescript unhappy
|
||||
export interface GenericInt16Array<T extends number> extends PartialInt16Array<T> {
|
||||
// map(callbackfn: (value: T, index: number, array: this) => T, thisArg?: any): GenericInt16Array<T>;
|
||||
// this method does not trigger a typescript error on its own, but if we add in `toReversed` it causes issues....
|
||||
subarray(begin?: number, end?: number): GenericInt16Array<T>;
|
||||
toReversed(): GenericInt16Array<T>;
|
||||
toSorted(compareFn?: (a: T, b: T) => number): GenericInt16Array<T>;
|
||||
filter(predicate: (value: T, index: number, array: this) => any, thisArg?: any): GenericInt16Array<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A partial interface of `Int32Array` where methods that return the array type have been modified to return a more specific type.
|
||||
*
|
||||
* @privateRemarks
|
||||
* Excludes methods that return a new array (e.g. `subarray`, `slice`, `toReversed`, `toSorted`, `with`) as these cannot be resolved by typescript
|
||||
* @internal
|
||||
*/
|
||||
interface PartialInt32Array<T extends number> extends Int32Array {
|
||||
at(index: number): T | undefined; // ES2022
|
||||
entries(): ArrayIterator<[number, T]>;
|
||||
fill(value: T, start?: number, end?: number): this;
|
||||
find(predicate: (value: T, index: number, array: this) => boolean, thisArg?: any): T | undefined;
|
||||
findLast(predicate: (value: number, index: number, array: this) => boolean, thisArg?: any): T | undefined;
|
||||
forEach(callbackfn: (value: T, index: number, array: this) => void, thisArg?: any): void;
|
||||
includes(searchElement: T, fromIndex?: number): boolean;
|
||||
indexOf(searchElement: T, fromIndex?: number): number;
|
||||
lastIndexOf(searchElement: T, fromIndex?: number): number;
|
||||
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: this) => T): T;
|
||||
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: this) => T): T;
|
||||
set(array: ArrayLike<T>, offset?: number): void;
|
||||
some(predicate: (value: T, index: number, obj: this) => boolean, thisArg?: any): boolean;
|
||||
sort(compareFn?: (a: T, b: T) => number): this;
|
||||
values(): ArrayIterator<T>;
|
||||
[Symbol.iterator](): ArrayIterator<T>;
|
||||
[index: number]: T;
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Int32Array` whose elements typescript considers to be of type `T`, allowing for type-safe iteration and access.
|
||||
*
|
||||
* @remarks
|
||||
* Useful to leverage the benefits of `TypedArrays` without losing type information. Typescript will consider the elements to be of type `T` instead of just `number`.
|
||||
* @typeParam T - The specific numeric type of the elements in the array.
|
||||
*/
|
||||
// @ts-expect-error - These methods _will_ error, as we are overriding the return type to be more specific in a way that makes typescript unhappy
|
||||
export interface GenericInt32Array<T extends number> extends PartialInt32Array<T> {
|
||||
// map(callbackfn: (value: T, index: number, array: this) => T, thisArg?: any): GenericInt32Array<T>;
|
||||
// this method does not trigger a typescript error on its own, but if we add in `toReversed` it causes issues....
|
||||
subarray(begin?: number, end?: number): GenericInt32Array<T>;
|
||||
toReversed(): GenericInt32Array<T>;
|
||||
toSorted(compareFn?: (a: T, b: T) => number): GenericInt32Array<T>;
|
||||
filter(predicate: (value: T, index: number, array: this) => any, thisArg?: any): GenericInt32Array<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A readonly version of {@link GenericUint8Array} where elements are of type `T`.
|
||||
* @typeParam T - The specific numeric type of the elements in the array.
|
||||
* @remarks
|
||||
* Is to `GenericUint8Array` what `ReadonlyArray` is to `Array`
|
||||
*/
|
||||
export interface ReadonlyGenericUint8Array<T extends number>
|
||||
extends Omit<GenericUint8Array<T>, "fill" | "set" | "sort" | "reverse" | "copyWithin" | "subarray"> {
|
||||
subarray(begin?: number, end?: number): ReadonlyGenericUint8Array<T>;
|
||||
readonly [index: number]: T;
|
||||
}
|
||||
|
||||
/**
|
||||
* A readonly version of {@link GenericUint16Array} where elements are of type `T`.
|
||||
* @typeParam T - The specific numeric type of the elements in the array.
|
||||
* @remarks
|
||||
* Is to `GenericUint16Array` what `ReadonlyArray` is to `Array`
|
||||
*/
|
||||
export interface ReadonlyGenericUint16Array<T extends number>
|
||||
extends Omit<GenericUint16Array<T>, "fill" | "set" | "sort" | "reverse" | "copyWithin" | "subarray"> {
|
||||
subarray(begin?: number, end?: number): ReadonlyGenericUint16Array<T>;
|
||||
readonly [index: number]: T;
|
||||
}
|
||||
|
||||
/**
|
||||
* A readonly version of {@link GenericUint32Array} where elements are of type `T`.
|
||||
* @typeParam T - The specific numeric type of the elements in the array.
|
||||
* @remarks
|
||||
* Is to `GenericUint32Array` what `ReadonlyArray` is to `Array`
|
||||
*/
|
||||
export interface ReadonlyGenericUint32Array<T extends number>
|
||||
extends Omit<GenericUint32Array<T>, "fill" | "set" | "sort" | "reverse" | "copyWithin" | "subarray"> {
|
||||
subarray(begin?: number, end?: number): ReadonlyGenericUint32Array<T>;
|
||||
readonly [index: number]: T;
|
||||
}
|
||||
|
||||
/**
|
||||
* A readonly version of {@link GenericInt8Array} where elements are of type `T`.
|
||||
* @typeParam T - The specific numeric type of the elements in the array.
|
||||
* @remarks
|
||||
* Is to `GenericInt8Array` what `ReadonlyArray` is to `Array`
|
||||
*/
|
||||
export interface ReadonlyGenericInt8Array<T extends number>
|
||||
extends Omit<GenericInt8Array<T>, "fill" | "set" | "sort" | "reverse" | "copyWithin" | "subarray"> {
|
||||
subarray(begin?: number, end?: number): ReadonlyGenericInt8Array<T>;
|
||||
readonly [index: number]: T;
|
||||
}
|
||||
|
||||
/**
|
||||
* A readonly version of {@link GenericInt16Array} where elements are of type `T`.
|
||||
* @typeParam T - The specific numeric type of the elements in the array.
|
||||
* @remarks
|
||||
* Is to `GenericInt16Array` what `ReadonlyArray` is to `Array`
|
||||
*/
|
||||
export interface ReadonlyGenericInt16Array<T extends number>
|
||||
extends Omit<GenericInt16Array<T>, "fill" | "set" | "sort" | "reverse" | "copyWithin" | "subarray"> {
|
||||
subarray(begin?: number, end?: number): ReadonlyGenericInt16Array<T>;
|
||||
readonly [index: number]: T;
|
||||
}
|
||||
|
||||
/**
|
||||
* A readonly version of {@link GenericInt32Array} where elements are of type `T`.
|
||||
* @typeParam T - The specific numeric type of the elements in the array.
|
||||
* @remarks
|
||||
* Is to `GenericInt32Array` what `ReadonlyArray` is to `Array`
|
||||
*/
|
||||
export interface ReadonlyGenericInt32Array<T extends number>
|
||||
extends Omit<GenericInt32Array<T>, "fill" | "set" | "sort" | "reverse" | "copyWithin" | "subarray"> {
|
||||
subarray(begin?: number, end?: number): ReadonlyGenericInt32Array<T>;
|
||||
readonly [index: number]: T;
|
||||
}
|
||||
|
||||
/**
|
||||
* A union type of all `GenericTypedArray`s
|
||||
*/
|
||||
export type ReadonlyGenericArray<T extends number = number> =
|
||||
| ReadonlyGenericUint8Array<T>
|
||||
| ReadonlyGenericUint16Array<T>
|
||||
| ReadonlyGenericUint32Array<T>
|
||||
| ReadonlyGenericInt8Array<T>
|
||||
| ReadonlyGenericInt16Array<T>
|
||||
| ReadonlyGenericInt32Array<T>;
|
@ -118,6 +118,7 @@ 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 { AbilityBar } from "#ui/ability-bar";
|
||||
@ -132,7 +133,6 @@ import { UI } from "#ui/ui";
|
||||
import { addUiThemeOverrides } from "#ui/ui-theme";
|
||||
import {
|
||||
BooleanHolder,
|
||||
type Constructor,
|
||||
fixedInt,
|
||||
formatMoney,
|
||||
getIvsFromId,
|
||||
|
@ -526,24 +526,25 @@ export class FixedBattleConfig {
|
||||
|
||||
/**
|
||||
* Helper function to generate a random trainer for evil team trainers and the elite 4/champion
|
||||
* @param trainerPool The TrainerType or list of TrainerTypes that can possibly be generated
|
||||
* @param randomGender whether or not to randomly (50%) generate a female trainer (for use with evil team grunts)
|
||||
* @param seedOffset the seed offset to use for the random generation of the trainer
|
||||
* @returns the generated trainer
|
||||
* @param trainerPool - An array of trainer types to choose from. If an entry is an array, a random trainer type will be chosen from that array
|
||||
* @param randomGender - Whether or not to randomly (50%) generate a female trainer (for use with evil team grunts)
|
||||
* @param seedOffset - The seed offset to use for the random generation of the trainer
|
||||
* @returns A function to get a random trainer
|
||||
*/
|
||||
export function getRandomTrainerFunc(
|
||||
trainerPool: (TrainerType | TrainerType[])[],
|
||||
trainerPool: readonly (TrainerType | readonly TrainerType[])[],
|
||||
randomGender = false,
|
||||
seedOffset = 0,
|
||||
): GetTrainerFunc {
|
||||
return () => {
|
||||
const rand = randSeedInt(trainerPool.length);
|
||||
const trainerTypes: TrainerType[] = [];
|
||||
const trainerTypes: TrainerType[] = new Array(trainerPool.length);
|
||||
|
||||
globalScene.executeWithSeedOffset(() => {
|
||||
for (const trainerPoolEntry of trainerPool) {
|
||||
for (let i = 0; i < trainerPool.length; i++) {
|
||||
const trainerPoolEntry = trainerPool[i];
|
||||
const trainerType = Array.isArray(trainerPoolEntry) ? randSeedItem(trainerPoolEntry) : trainerPoolEntry;
|
||||
trainerTypes.push(trainerType);
|
||||
trainerTypes[i] = trainerType;
|
||||
}
|
||||
}, seedOffset);
|
||||
|
||||
|
@ -23,7 +23,7 @@ export const TYPE_BOOST_ITEM_BOOST_PERCENT = 20;
|
||||
/**
|
||||
* The default species that a new player can choose from
|
||||
*/
|
||||
export const defaultStarterSpecies: SpeciesId[] = [
|
||||
export const defaultStarterSpecies: readonly SpeciesId[] = [
|
||||
SpeciesId.BULBASAUR,
|
||||
SpeciesId.CHARMANDER,
|
||||
SpeciesId.SQUIRTLE,
|
||||
|
@ -62,18 +62,11 @@ 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 { Constructor } from "#utils/common";
|
||||
import {
|
||||
BooleanHolder,
|
||||
coerceArray,
|
||||
NumberHolder,
|
||||
randSeedFloat,
|
||||
randSeedInt,
|
||||
randSeedItem,
|
||||
toDmgValue,
|
||||
} from "#utils/common";
|
||||
import { coerceArray } from "#utils/array";
|
||||
import { BooleanHolder, NumberHolder, randSeedFloat, randSeedInt, randSeedItem, toDmgValue } from "#utils/common";
|
||||
import { toCamelCase } from "#utils/strings";
|
||||
import i18next from "i18next";
|
||||
|
||||
@ -1217,7 +1210,7 @@ export class PostDefendTerrainChangeAbAttr extends PostDefendAbAttr {
|
||||
|
||||
export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr {
|
||||
private chance: number;
|
||||
private effects: StatusEffect[];
|
||||
private readonly effects: readonly StatusEffect[];
|
||||
|
||||
constructor(chance: number, ...effects: StatusEffect[]) {
|
||||
super(true);
|
||||
@ -2187,7 +2180,7 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr {
|
||||
export class PostAttackApplyStatusEffectAbAttr extends PostAttackAbAttr {
|
||||
private contactRequired: boolean;
|
||||
private chance: number;
|
||||
private effects: StatusEffect[];
|
||||
private readonly effects: readonly StatusEffect[];
|
||||
|
||||
constructor(contactRequired: boolean, chance: number, ...effects: StatusEffect[]) {
|
||||
super();
|
||||
@ -2947,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 immuneEffects: StatusEffect[];
|
||||
private readonly immuneEffects: readonly StatusEffect[];
|
||||
private statusHealed: StatusEffect;
|
||||
|
||||
/**
|
||||
@ -2960,7 +2953,8 @@ export class PostSummonHealStatusAbAttr extends PostSummonRemoveEffectAbAttr {
|
||||
|
||||
public override canApply({ pokemon }: AbAttrBaseParams): boolean {
|
||||
const status = pokemon.status?.effect;
|
||||
return status != null && (this.immuneEffects.length === 0 || this.immuneEffects.includes(status));
|
||||
const immuneEffects = this.immuneEffects;
|
||||
return status != null && (immuneEffects.length === 0 || immuneEffects.includes(status));
|
||||
}
|
||||
|
||||
public override apply({ pokemon }: AbAttrBaseParams): void {
|
||||
@ -3056,7 +3050,7 @@ export class PostSummonCopyAbilityAbAttr extends PostSummonAbAttr {
|
||||
* Removes supplied status effects from the user's field.
|
||||
*/
|
||||
export class PostSummonUserFieldRemoveStatusEffectAbAttr extends PostSummonAbAttr {
|
||||
private statusEffect: StatusEffect[];
|
||||
private readonly statusEffect: readonly StatusEffect[];
|
||||
|
||||
/**
|
||||
* @param statusEffect - The status effects to be removed from the user's field.
|
||||
@ -3644,7 +3638,7 @@ export class PreSetStatusAbAttr extends AbAttr {
|
||||
* Provides immunity to status effects to specified targets.
|
||||
*/
|
||||
export class PreSetStatusEffectImmunityAbAttr extends PreSetStatusAbAttr {
|
||||
protected immuneEffects: StatusEffect[];
|
||||
protected readonly immuneEffects: readonly StatusEffect[];
|
||||
|
||||
/**
|
||||
* @param immuneEffects - An array of {@linkcode StatusEffect}s to prevent application.
|
||||
@ -3712,7 +3706,7 @@ export interface UserFieldStatusEffectImmunityAbAttrParams extends AbAttrBasePar
|
||||
*/
|
||||
export class UserFieldStatusEffectImmunityAbAttr extends CancelInteractionAbAttr {
|
||||
private declare readonly _: never;
|
||||
protected immuneEffects: StatusEffect[];
|
||||
protected readonly immuneEffects: readonly StatusEffect[];
|
||||
|
||||
/**
|
||||
* @param immuneEffects - An array of {@linkcode StatusEffect}s to prevent application.
|
||||
@ -4005,7 +3999,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 effects: StatusEffect[];
|
||||
private readonly effects: readonly StatusEffect[];
|
||||
|
||||
/**
|
||||
* @param effects - The status effect(s) that will be blocked from damaging the ability pokemon
|
||||
@ -4546,7 +4540,7 @@ export class PostTurnAbAttr extends AbAttr {
|
||||
* @sealed
|
||||
*/
|
||||
export class PostTurnStatusHealAbAttr extends PostTurnAbAttr {
|
||||
private effects: StatusEffect[];
|
||||
private readonly effects: readonly StatusEffect[];
|
||||
|
||||
/**
|
||||
* @param effects - The status effect(s) that will qualify healing the ability pokemon
|
||||
@ -5815,10 +5809,10 @@ export interface IgnoreTypeStatusEffectImmunityAbAttrParams extends AbAttrParams
|
||||
* @sealed
|
||||
*/
|
||||
export class IgnoreTypeStatusEffectImmunityAbAttr extends AbAttr {
|
||||
private statusEffect: StatusEffect[];
|
||||
private defenderType: PokemonType[];
|
||||
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 = statusEffect;
|
||||
@ -6706,7 +6700,7 @@ function getPokemonWithWeatherBasedForms() {
|
||||
|
||||
// biome-ignore format: prevent biome from removing the newlines (e.g. prevent `new Ability(...).attr(...)`)
|
||||
export function initAbilities() {
|
||||
allAbilities.push(
|
||||
(allAbilities as Ability[]).push(
|
||||
new Ability(AbilityId.NONE, 3),
|
||||
new Ability(AbilityId.STENCH, 3)
|
||||
.attr(PostAttackApplyBattlerTagAbAttr, false, (user, target, move) => !move.hasAttr("FlinchAttr") && !move.hitsSubstitute(user, target) ? 10 : 0, BattlerTagType.FLINCHED),
|
||||
|
@ -5,6 +5,7 @@ import { PokemonType } from "#enums/pokemon-type";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { TimeOfDay } from "#enums/time-of-day";
|
||||
import { TrainerType } from "#enums/trainer-type";
|
||||
import type { Mutable } from "#types/type-helpers";
|
||||
import { randSeedInt } from "#utils/common";
|
||||
import { getEnumValues } from "#utils/enums";
|
||||
import { toCamelCase } from "#utils/strings";
|
||||
@ -88,19 +89,19 @@ export enum BiomePoolTier {
|
||||
export const uncatchableSpecies: SpeciesId[] = [];
|
||||
|
||||
interface SpeciesTree {
|
||||
[key: number]: SpeciesId[]
|
||||
readonly [key: number]: SpeciesId[]
|
||||
}
|
||||
|
||||
export interface PokemonPools {
|
||||
[key: number]: (SpeciesId | SpeciesTree)[]
|
||||
readonly [key: number]: (SpeciesId | SpeciesTree)[]
|
||||
}
|
||||
|
||||
interface BiomeTierPokemonPools {
|
||||
[key: number]: PokemonPools
|
||||
readonly [key: number]: PokemonPools
|
||||
}
|
||||
|
||||
interface BiomePokemonPools {
|
||||
[key: number]: BiomeTierPokemonPools
|
||||
readonly [key: number]: BiomeTierPokemonPools
|
||||
}
|
||||
|
||||
export interface BiomeTierTod {
|
||||
@ -110,17 +111,17 @@ export interface BiomeTierTod {
|
||||
}
|
||||
|
||||
export interface CatchableSpecies{
|
||||
[key: number]: BiomeTierTod[]
|
||||
readonly [key: number]: readonly BiomeTierTod[]
|
||||
}
|
||||
|
||||
export const catchableSpecies: CatchableSpecies = {};
|
||||
|
||||
export interface BiomeTierTrainerPools {
|
||||
[key: number]: TrainerType[]
|
||||
readonly [key: number]: readonly TrainerType[]
|
||||
}
|
||||
|
||||
export interface BiomeTrainerPools {
|
||||
[key: number]: BiomeTierTrainerPools
|
||||
readonly [key: number]: BiomeTierTrainerPools
|
||||
}
|
||||
|
||||
export const biomePokemonPools: BiomePokemonPools = {
|
||||
@ -7621,12 +7622,10 @@ export function initBiomes() {
|
||||
? biomeLinks[biome] as (BiomeId | [ BiomeId, number ])[]
|
||||
: [ biomeLinks[biome] as BiomeId ];
|
||||
for (const linkedBiomeEntry of linkedBiomes) {
|
||||
const linkedBiome = !Array.isArray(linkedBiomeEntry)
|
||||
? linkedBiomeEntry as BiomeId
|
||||
: linkedBiomeEntry[0];
|
||||
const biomeChance = !Array.isArray(linkedBiomeEntry)
|
||||
? 1
|
||||
: linkedBiomeEntry[1];
|
||||
const linkedBiome = Array.isArray(linkedBiomeEntry)
|
||||
? linkedBiomeEntry[0] : linkedBiomeEntry as BiomeId;
|
||||
const biomeChance = Array.isArray(linkedBiomeEntry)
|
||||
? linkedBiomeEntry[1] : 1;
|
||||
if (!biomeDepths.hasOwnProperty(linkedBiome) || biomeChance < biomeDepths[linkedBiome][1] || (depth < biomeDepths[linkedBiome][0] && biomeChance === biomeDepths[linkedBiome][1])) {
|
||||
biomeDepths[linkedBiome] = [ depth + 1, biomeChance ];
|
||||
traverseBiome(linkedBiome, depth + 1);
|
||||
@ -7638,15 +7637,15 @@ export function initBiomes() {
|
||||
biomeDepths[BiomeId.END] = [ Object.values(biomeDepths).map(d => d[0]).reduce((max: number, value: number) => Math.max(max, value), 0) + 1, 1 ];
|
||||
|
||||
for (const biome of getEnumValues(BiomeId)) {
|
||||
biomePokemonPools[biome] = {};
|
||||
biomeTrainerPools[biome] = {};
|
||||
(biomePokemonPools[biome] as Mutable<typeof biomePokemonPools[number]>) = {};
|
||||
(biomeTrainerPools[biome] as Mutable<typeof biomeTrainerPools[number]>) = {};
|
||||
|
||||
for (const tier of getEnumValues(BiomePoolTier)) {
|
||||
biomePokemonPools[biome][tier] = {};
|
||||
biomeTrainerPools[biome][tier] = [];
|
||||
(biomePokemonPools[biome][tier] as Mutable<typeof biomePokemonPools[number][number]>) = {};
|
||||
(biomeTrainerPools[biome][tier] as Mutable<typeof biomeTrainerPools[number][number]>) = [];
|
||||
|
||||
for (const tod of getEnumValues(TimeOfDay)) {
|
||||
biomePokemonPools[biome][tier][tod] = [];
|
||||
(biomePokemonPools[biome][tier][tod] as Mutable<typeof biomePokemonPools[number][number][number]>) = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7663,8 +7662,9 @@ export function initBiomes() {
|
||||
uncatchableSpecies.push(speciesId);
|
||||
}
|
||||
|
||||
type mutableSpecies = Mutable<typeof catchableSpecies[SpeciesId]>;
|
||||
// array of biome options for the current species
|
||||
catchableSpecies[speciesId] = [];
|
||||
(catchableSpecies[speciesId] as mutableSpecies) = [];
|
||||
|
||||
for (const b of biomeEntries) {
|
||||
const biome = b[0];
|
||||
@ -7675,7 +7675,7 @@ export function initBiomes() {
|
||||
: [ b[2] ]
|
||||
: [ TimeOfDay.ALL ];
|
||||
|
||||
catchableSpecies[speciesId].push({
|
||||
(catchableSpecies[speciesId] as mutableSpecies).push({
|
||||
biome: biome as BiomeId,
|
||||
tier: tier as BiomePoolTier,
|
||||
tod: timesOfDay as TimeOfDay[]
|
||||
@ -7735,12 +7735,13 @@ export function initBiomes() {
|
||||
};
|
||||
for (let s = 1; s < entry.length; s++) {
|
||||
const speciesId = entry[s];
|
||||
// biome-ignore lint/nursery/noShadow: one-off
|
||||
const prevolution = entry.flatMap((s: string | number) => pokemonEvolutions[s]).find(e => e && e.speciesId === speciesId);
|
||||
const level = prevolution.level - (prevolution.level === 1 ? 1 : 0) + (prevolution.wildDelay * 10) - (tier >= BiomePoolTier.BOSS ? 10 : 0);
|
||||
if (!newEntry.hasOwnProperty(level)) {
|
||||
newEntry[level] = [ speciesId ];
|
||||
} else {
|
||||
if (newEntry.hasOwnProperty(level)) {
|
||||
newEntry[level].push(speciesId);
|
||||
} else {
|
||||
newEntry[level] = [ speciesId ];
|
||||
}
|
||||
}
|
||||
biomeTierTimePool[e] = newEntry;
|
||||
@ -7763,7 +7764,7 @@ export function initBiomes() {
|
||||
}
|
||||
|
||||
const biomeTierPool = biomeTrainerPools[biome][tier];
|
||||
biomeTierPool.push(trainerType);
|
||||
(biomeTierPool as Mutable<typeof biomeTierPool>).push(trainerType);
|
||||
}
|
||||
//outputPools();
|
||||
}
|
||||
|
@ -14,7 +14,8 @@ import { TimeOfDay } from "#enums/time-of-day";
|
||||
import { WeatherType } from "#enums/weather-type";
|
||||
import type { Pokemon } from "#field/pokemon";
|
||||
import type { SpeciesStatBoosterItem, SpeciesStatBoosterModifierType } from "#modifiers/modifier-type";
|
||||
import { coerceArray, randSeedInt } from "#utils/common";
|
||||
import { coerceArray } from "#utils/array";
|
||||
import { randSeedInt } from "#utils/common";
|
||||
import { getPokemonSpecies } from "#utils/pokemon-utils";
|
||||
import { toCamelCase } from "#utils/strings";
|
||||
import i18next from "i18next";
|
||||
|
@ -8,7 +8,7 @@ import { SpeciesId } from "#enums/species-id";
|
||||
|
||||
// biome-ignore format: manually formatted
|
||||
export function initSpecies() {
|
||||
allSpecies.push(
|
||||
(allSpecies as PokemonSpecies[]).push(
|
||||
new PokemonSpecies(SpeciesId.BULBASAUR, 1, false, false, false, "Seed Pokémon", PokemonType.GRASS, PokemonType.POISON, 0.7, 6.9, AbilityId.OVERGROW, AbilityId.NONE, AbilityId.CHLOROPHYLL, 318, 45, 49, 49, 65, 65, 45, 45, 50, 64, GrowthRate.MEDIUM_SLOW, 87.5, false),
|
||||
new PokemonSpecies(SpeciesId.IVYSAUR, 1, false, false, false, "Seed Pokémon", PokemonType.GRASS, PokemonType.POISON, 1, 13, AbilityId.OVERGROW, AbilityId.NONE, AbilityId.CHLOROPHYLL, 405, 60, 62, 63, 80, 80, 60, 45, 50, 142, GrowthRate.MEDIUM_SLOW, 87.5, false),
|
||||
new PokemonSpecies(SpeciesId.VENUSAUR, 1, false, false, false, "Seed Pokémon", PokemonType.GRASS, PokemonType.POISON, 2, 100, AbilityId.OVERGROW, AbilityId.NONE, AbilityId.CHLOROPHYLL, 525, 80, 82, 83, 100, 100, 80, 45, 50, 263, GrowthRate.MEDIUM_SLOW, 87.5, true, true,
|
||||
|
@ -7,7 +7,9 @@ import { AnimBlendType, AnimFocus, AnimFrameTarget, ChargeAnim, CommonAnim } fro
|
||||
import { MoveFlags } from "#enums/move-flags";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import type { Pokemon } from "#field/pokemon";
|
||||
import { coerceArray, getFrameMs, type nil } from "#utils/common";
|
||||
import type { nil } from "#types/common";
|
||||
import { coerceArray } from "#utils/array";
|
||||
import { getFrameMs } from "#utils/common";
|
||||
import { getEnumKeys, getEnumValues } from "#utils/enums";
|
||||
import { toKebabCase } from "#utils/strings";
|
||||
import Phaser from "phaser";
|
||||
|
@ -88,7 +88,8 @@ import type {
|
||||
TypeBoostTagType,
|
||||
} from "#types/battler-tags";
|
||||
import type { Mutable } from "#types/type-helpers";
|
||||
import { BooleanHolder, coerceArray, getFrameMs, NumberHolder, toDmgValue } from "#utils/common";
|
||||
import { coerceArray } from "#utils/array";
|
||||
import { BooleanHolder, getFrameMs, NumberHolder, toDmgValue } from "#utils/common";
|
||||
import { toCamelCase } from "#utils/strings";
|
||||
|
||||
/** Interface containing the serializable fields of BattlerTag */
|
||||
@ -2153,8 +2154,8 @@ export class HighestStatBoostTag extends AbilityBattlerTag {
|
||||
}
|
||||
|
||||
export class WeatherHighestStatBoostTag extends HighestStatBoostTag {
|
||||
#weatherTypes: WeatherType[];
|
||||
public get weatherTypes(): WeatherType[] {
|
||||
readonly #weatherTypes: readonly WeatherType[];
|
||||
public get weatherTypes(): readonly WeatherType[] {
|
||||
return this.#weatherTypes;
|
||||
}
|
||||
|
||||
@ -2165,8 +2166,8 @@ export class WeatherHighestStatBoostTag extends HighestStatBoostTag {
|
||||
}
|
||||
|
||||
export class TerrainHighestStatBoostTag extends HighestStatBoostTag {
|
||||
#terrainTypes: TerrainType[];
|
||||
public get terrainTypes(): TerrainType[] {
|
||||
readonly #terrainTypes: readonly TerrainType[];
|
||||
public get terrainTypes(): readonly TerrainType[] {
|
||||
return this.#terrainTypes;
|
||||
}
|
||||
|
||||
|
@ -3,9 +3,9 @@ import type { PokemonSpecies } from "#data/pokemon-species";
|
||||
import type { ModifierTypes } from "#modifiers/modifier-type";
|
||||
import type { Move } from "#moves/move";
|
||||
|
||||
export const allAbilities: Ability[] = [];
|
||||
export const allMoves: Move[] = [];
|
||||
export const allSpecies: PokemonSpecies[] = [];
|
||||
export const allAbilities: readonly Ability[] = [];
|
||||
export const allMoves: readonly Move[] = [];
|
||||
export const allSpecies: readonly PokemonSpecies[] = [];
|
||||
|
||||
// TODO: Figure out what this is used for and provide an appropriate tsdoc comment
|
||||
export const modifierTypes = {} as ModifierTypes;
|
||||
|
@ -485,14 +485,14 @@ export class Egg {
|
||||
* and being the same each time
|
||||
*/
|
||||
let totalWeight = 0;
|
||||
const speciesWeights: number[] = [];
|
||||
for (const speciesId of speciesPool) {
|
||||
const speciesWeights = new Array<number>(speciesPool.length);
|
||||
for (const [idx, speciesId] of speciesPool.entries()) {
|
||||
// Accounts for species that have starter costs outside of the normal range for their EggTier
|
||||
const speciesCostClamped = Phaser.Math.Clamp(speciesStarterCosts[speciesId], minStarterValue, maxStarterValue);
|
||||
const weight = Math.floor(
|
||||
(((maxStarterValue - speciesCostClamped) / (maxStarterValue - minStarterValue + 1)) * 1.5 + 1) * 100,
|
||||
);
|
||||
speciesWeights.push(totalWeight + weight);
|
||||
speciesWeights[idx] = totalWeight + weight;
|
||||
totalWeight += weight;
|
||||
}
|
||||
|
||||
|
@ -87,13 +87,16 @@ 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, coerceArray, 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";
|
||||
import i18next from "i18next";
|
||||
import { applyChallenges } from "#utils/challenge-utils";
|
||||
import { MovePhaseTimingModifier } from "#enums/move-phase-timing-modifier";
|
||||
import type { AbstractConstructor } from "#types/type-helpers";
|
||||
import type { ReadonlyGenericUint8Array } from "#types/typed-arrays";
|
||||
|
||||
/**
|
||||
* A function used to conditionally determine execution of a given {@linkcode MoveAttr}.
|
||||
@ -2594,7 +2597,7 @@ export class StatusEffectAttr extends MoveEffectAttr {
|
||||
* Used for {@linkcode Moves.TRI_ATTACK} and {@linkcode Moves.DIRE_CLAW}.
|
||||
*/
|
||||
export class MultiStatusEffectAttr extends StatusEffectAttr {
|
||||
public effects: StatusEffect[];
|
||||
public readonly effects: readonly StatusEffect[];
|
||||
|
||||
constructor(effects: StatusEffect[], selfTarget?: boolean) {
|
||||
super(effects[0], selfTarget);
|
||||
@ -2924,7 +2927,7 @@ export class StealEatBerryAttr extends EatBerryAttr {
|
||||
*/
|
||||
export class HealStatusEffectAttr extends MoveEffectAttr {
|
||||
/** List of Status Effects to cure */
|
||||
private effects: StatusEffect[];
|
||||
private readonly effects: readonly StatusEffect[];
|
||||
|
||||
/**
|
||||
* @param selfTarget - Whether this move targets the user
|
||||
@ -2932,7 +2935,7 @@ export class HealStatusEffectAttr extends MoveEffectAttr {
|
||||
*/
|
||||
constructor(selfTarget: boolean, effects: StatusEffect | StatusEffect[]) {
|
||||
super(selfTarget, { lastHitOnly: true });
|
||||
this.effects = coerceArray(effects)
|
||||
this.effects = coerceArray(effects);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -8391,7 +8394,7 @@ const MoveAttrs = Object.freeze({
|
||||
export type MoveAttrConstructorMap = typeof MoveAttrs;
|
||||
|
||||
export function initMoves() {
|
||||
allMoves.push(
|
||||
(allMoves as Move[]).push(
|
||||
new SelfStatusMove(MoveId.NONE, PokemonType.NORMAL, MoveCategory.STATUS, -1, -1, 0, 1),
|
||||
new AttackMove(MoveId.POUND, PokemonType.NORMAL, MoveCategory.PHYSICAL, 40, 100, 35, -1, 0, 1),
|
||||
new AttackMove(MoveId.KARATE_CHOP, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 50, 100, 25, -1, 0, 1)
|
||||
|
@ -78,7 +78,7 @@ const POOL_1_POKEMON = [
|
||||
SpeciesId.RIBOMBEE,
|
||||
SpeciesId.SPIDOPS,
|
||||
SpeciesId.LOKIX,
|
||||
];
|
||||
] as const;
|
||||
|
||||
const POOL_2_POKEMON = [
|
||||
SpeciesId.SCYTHER,
|
||||
@ -104,41 +104,20 @@ const POOL_2_POKEMON = [
|
||||
SpeciesId.CENTISKORCH,
|
||||
SpeciesId.FROSMOTH,
|
||||
SpeciesId.KLEAVOR,
|
||||
];
|
||||
] as const;
|
||||
|
||||
const POOL_3_POKEMON: { species: SpeciesId; formIndex?: number }[] = [
|
||||
{
|
||||
species: SpeciesId.PINSIR,
|
||||
formIndex: 1,
|
||||
},
|
||||
{
|
||||
species: SpeciesId.SCIZOR,
|
||||
formIndex: 1,
|
||||
},
|
||||
{
|
||||
species: SpeciesId.HERACROSS,
|
||||
formIndex: 1,
|
||||
},
|
||||
{
|
||||
species: SpeciesId.ORBEETLE,
|
||||
formIndex: 1,
|
||||
},
|
||||
{
|
||||
species: SpeciesId.CENTISKORCH,
|
||||
formIndex: 1,
|
||||
},
|
||||
{
|
||||
species: SpeciesId.DURANT,
|
||||
},
|
||||
{
|
||||
species: SpeciesId.VOLCARONA,
|
||||
},
|
||||
{
|
||||
species: SpeciesId.GOLISOPOD,
|
||||
},
|
||||
];
|
||||
const POOL_3_POKEMON = [
|
||||
{ species: SpeciesId.PINSIR, formIndex: 1 },
|
||||
{ species: SpeciesId.SCIZOR, formIndex: 1 },
|
||||
{ species: SpeciesId.HERACROSS, formIndex: 1 },
|
||||
{ species: SpeciesId.ORBEETLE, formIndex: 1 },
|
||||
{ species: SpeciesId.CENTISKORCH, formIndex: 1 },
|
||||
{ species: SpeciesId.DURANT } as { species: SpeciesId.DURANT; formIndex: undefined },
|
||||
{ species: SpeciesId.VOLCARONA } as { species: SpeciesId.VOLCARONA; formIndex: undefined },
|
||||
{ species: SpeciesId.GOLISOPOD } as { species: SpeciesId.GOLISOPOD; formIndex: undefined },
|
||||
] as const;
|
||||
|
||||
const POOL_4_POKEMON = [SpeciesId.GENESECT, SpeciesId.SLITHER_WING, SpeciesId.BUZZWOLE, SpeciesId.PHEROMOSA];
|
||||
const POOL_4_POKEMON = [SpeciesId.GENESECT, SpeciesId.SLITHER_WING, SpeciesId.BUZZWOLE, SpeciesId.PHEROMOSA] as const;
|
||||
|
||||
const PHYSICAL_TUTOR_MOVES = [
|
||||
MoveId.MEGAHORN,
|
||||
@ -146,7 +125,7 @@ const PHYSICAL_TUTOR_MOVES = [
|
||||
MoveId.BUG_BITE,
|
||||
MoveId.FIRST_IMPRESSION,
|
||||
MoveId.LUNGE,
|
||||
];
|
||||
] as const;
|
||||
|
||||
const SPECIAL_TUTOR_MOVES = [
|
||||
MoveId.SILVER_WIND,
|
||||
@ -154,7 +133,7 @@ const SPECIAL_TUTOR_MOVES = [
|
||||
MoveId.BUG_BUZZ,
|
||||
MoveId.POLLEN_PUFF,
|
||||
MoveId.STRUGGLE_BUG,
|
||||
];
|
||||
] as const;
|
||||
|
||||
const STATUS_TUTOR_MOVES = [
|
||||
MoveId.STRING_SHOT,
|
||||
@ -162,14 +141,20 @@ const STATUS_TUTOR_MOVES = [
|
||||
MoveId.RAGE_POWDER,
|
||||
MoveId.STICKY_WEB,
|
||||
MoveId.SILK_TRAP,
|
||||
];
|
||||
] as const;
|
||||
|
||||
const MISC_TUTOR_MOVES = [MoveId.LEECH_LIFE, MoveId.U_TURN, MoveId.HEAL_ORDER, MoveId.QUIVER_DANCE, MoveId.INFESTATION];
|
||||
const MISC_TUTOR_MOVES = [
|
||||
MoveId.LEECH_LIFE,
|
||||
MoveId.U_TURN,
|
||||
MoveId.HEAL_ORDER,
|
||||
MoveId.QUIVER_DANCE,
|
||||
MoveId.INFESTATION,
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* Wave breakpoints that determine how strong to make the Bug-Type Superfan's team
|
||||
*/
|
||||
const WAVE_LEVEL_BREAKPOINTS = [30, 50, 70, 100, 120, 140, 160];
|
||||
const WAVE_LEVEL_BREAKPOINTS = [30, 50, 70, 100, 120, 140, 160] as const;
|
||||
|
||||
/**
|
||||
* Bug Type Superfan encounter.
|
||||
@ -517,8 +502,8 @@ function getTrainerConfigForWave(waveIndex: number) {
|
||||
const config = trainerConfigs[TrainerType.BUG_TYPE_SUPERFAN].clone();
|
||||
config.name = i18next.t("trainerNames:bugTypeSuperfan");
|
||||
|
||||
let pool3Copy = POOL_3_POKEMON.slice(0);
|
||||
pool3Copy = randSeedShuffle(pool3Copy);
|
||||
const pool3Copy = randSeedShuffle(POOL_3_POKEMON.slice());
|
||||
// Bang is fine here, as we know pool3Copy has at least 1 entry
|
||||
const pool3Mon = pool3Copy.pop()!;
|
||||
|
||||
if (waveIndex < WAVE_LEVEL_BREAKPOINTS[0]) {
|
||||
@ -579,7 +564,6 @@ function getTrainerConfigForWave(waveIndex: number) {
|
||||
}),
|
||||
);
|
||||
} else if (waveIndex < WAVE_LEVEL_BREAKPOINTS[5]) {
|
||||
pool3Copy = randSeedShuffle(pool3Copy);
|
||||
const pool3Mon2 = pool3Copy.pop()!;
|
||||
config
|
||||
.setPartyTemplates(new TrainerPartyTemplate(5, PartyMemberStrength.AVERAGE))
|
||||
@ -657,7 +641,6 @@ function getTrainerConfigForWave(waveIndex: number) {
|
||||
)
|
||||
.setPartyMemberFunc(4, getRandomPartyMemberFunc(POOL_4_POKEMON, TrainerSlot.TRAINER, true));
|
||||
} else {
|
||||
pool3Copy = randSeedShuffle(pool3Copy);
|
||||
const pool3Mon2 = pool3Copy.pop()!;
|
||||
config
|
||||
.setPartyTemplates(
|
||||
|
@ -220,7 +220,7 @@ async function summonPlayerPokemon() {
|
||||
false,
|
||||
true,
|
||||
);
|
||||
wobbuffet.ivs = [0, 0, 0, 0, 0, 0];
|
||||
wobbuffet.ivs.fill(0);
|
||||
wobbuffet.setNature(Nature.MILD);
|
||||
wobbuffet.setAlpha(0);
|
||||
wobbuffet.setVisible(false);
|
||||
|
@ -15,7 +15,7 @@ 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 { coerceArray } from "#utils/common";
|
||||
import { coerceArray } from "#utils/array";
|
||||
|
||||
export interface EncounterRequirement {
|
||||
meetsRequirement(): boolean; // Boolean to see if a requirement is met
|
||||
@ -696,11 +696,11 @@ export class AbilityRequirement extends EncounterPokemonRequirement {
|
||||
}
|
||||
|
||||
export class StatusEffectRequirement extends EncounterPokemonRequirement {
|
||||
requiredStatusEffect: StatusEffect[];
|
||||
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;
|
||||
@ -717,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 => {
|
||||
@ -761,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;
|
||||
@ -792,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 =>
|
||||
@ -877,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,
|
||||
|
@ -25,7 +25,8 @@ import {
|
||||
StatusEffectRequirement,
|
||||
WaveRangeRequirement,
|
||||
} from "#mystery-encounters/mystery-encounter-requirements";
|
||||
import { coerceArray, randSeedInt } from "#utils/common";
|
||||
import { coerceArray } from "#utils/array";
|
||||
import { randSeedInt } from "#utils/common";
|
||||
import { capitalizeFirstLetter } from "#utils/strings";
|
||||
|
||||
export interface EncounterStartOfBattleEffect {
|
||||
|
@ -3,7 +3,7 @@ import type { MoveId } from "#enums/move-id";
|
||||
import type { PlayerPokemon } from "#field/pokemon";
|
||||
import { PokemonMove } from "#moves/pokemon-move";
|
||||
import { EncounterPokemonRequirement } from "#mystery-encounters/mystery-encounter-requirements";
|
||||
import { coerceArray } from "#utils/common";
|
||||
import { coerceArray } from "#utils/array";
|
||||
|
||||
/**
|
||||
* {@linkcode CanLearnMoveRequirement} options
|
||||
|
@ -49,7 +49,8 @@ import type { HeldModifierConfig } from "#types/held-modifier-config";
|
||||
import type { OptionSelectConfig, OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
|
||||
import type { PartyOption, PokemonSelectFilter } from "#ui/party-ui-handler";
|
||||
import { PartyUiMode } from "#ui/party-ui-handler";
|
||||
import { coerceArray, randomString, randSeedInt, randSeedItem } from "#utils/common";
|
||||
import { coerceArray } from "#utils/array";
|
||||
import { randomString, randSeedInt, randSeedItem } from "#utils/common";
|
||||
import { getPokemonSpecies } from "#utils/pokemon-utils";
|
||||
import i18next from "i18next";
|
||||
|
||||
|
@ -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, nil } from "#types/common";
|
||||
import type { Mutable } from "#types/type-helpers";
|
||||
|
||||
export type SpeciesFormChangeConditionPredicate = (p: Pokemon) => boolean;
|
||||
export type SpeciesFormChangeConditionEnforceFunc = (p: Pokemon) => void;
|
||||
@ -35,7 +36,7 @@ export class SpeciesFormChange {
|
||||
public formKey: string;
|
||||
public trigger: SpeciesFormChangeTrigger;
|
||||
public quiet: boolean;
|
||||
public readonly conditions: SpeciesFormChangeCondition[];
|
||||
public readonly conditions: readonly SpeciesFormChangeCondition[];
|
||||
|
||||
constructor(
|
||||
speciesId: SpeciesId,
|
||||
@ -116,7 +117,7 @@ function getSpeciesDependentFormChangeCondition(species: SpeciesId): SpeciesForm
|
||||
}
|
||||
|
||||
interface PokemonFormChanges {
|
||||
[key: string]: SpeciesFormChange[];
|
||||
[key: string]: readonly SpeciesFormChange[];
|
||||
}
|
||||
|
||||
// biome-ignore format: manually formatted
|
||||
@ -608,6 +609,6 @@ export function initPokemonForms() {
|
||||
);
|
||||
}
|
||||
}
|
||||
formChanges.push(...newFormChanges);
|
||||
(formChanges as Mutable<typeof formChanges>).push(...newFormChanges);
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +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 Constructor, coerceArray } from "#utils/common";
|
||||
import type { Constructor } from "#types/common";
|
||||
import { coerceArray } from "#utils/array";
|
||||
import { toCamelCase } from "#utils/strings";
|
||||
import i18next from "i18next";
|
||||
|
||||
@ -121,7 +122,7 @@ export class SpeciesFormChangeActiveTrigger extends SpeciesFormChangeTrigger {
|
||||
}
|
||||
|
||||
export class SpeciesFormChangeStatusEffectTrigger extends SpeciesFormChangeTrigger {
|
||||
public statusEffects: StatusEffect[];
|
||||
public readonly statusEffects: readonly StatusEffect[];
|
||||
public invert: boolean;
|
||||
|
||||
constructor(statusEffects: StatusEffect | StatusEffect[], invert = false) {
|
||||
@ -239,9 +240,9 @@ export class SpeciesFormChangeWeatherTrigger extends SpeciesFormChangeTrigger {
|
||||
/** The ability that triggers the form change */
|
||||
public ability: AbilityId;
|
||||
/** The list of weathers that trigger the form change */
|
||||
public weathers: WeatherType[];
|
||||
public readonly weathers: readonly WeatherType[];
|
||||
|
||||
constructor(ability: AbilityId, weathers: WeatherType[]) {
|
||||
constructor(ability: AbilityId, weathers: readonly WeatherType[]) {
|
||||
super();
|
||||
this.ability = ability;
|
||||
this.weathers = weathers;
|
||||
@ -277,9 +278,9 @@ export class SpeciesFormChangeRevertWeatherFormTrigger extends SpeciesFormChange
|
||||
/** The ability that triggers the form change*/
|
||||
public ability: AbilityId;
|
||||
/** The list of weathers that will also trigger a form change to original form */
|
||||
public weathers: WeatherType[];
|
||||
public readonly weathers: readonly WeatherType[];
|
||||
|
||||
constructor(ability: AbilityId, weathers: WeatherType[]) {
|
||||
constructor(ability: AbilityId, weathers: readonly WeatherType[]) {
|
||||
super();
|
||||
this.ability = ability;
|
||||
this.weathers = weathers;
|
||||
@ -309,9 +310,10 @@ export class SpeciesFormChangeRevertWeatherFormTrigger extends SpeciesFormChange
|
||||
}
|
||||
|
||||
export function getSpeciesFormChangeMessage(pokemon: Pokemon, formChange: SpeciesFormChange, preName: string): string {
|
||||
const isMega = formChange.formKey.indexOf(SpeciesFormKey.MEGA) > -1;
|
||||
const isGmax = formChange.formKey.indexOf(SpeciesFormKey.GIGANTAMAX) > -1;
|
||||
const isEmax = formChange.formKey.indexOf(SpeciesFormKey.ETERNAMAX) > -1;
|
||||
const formKey = formChange.formKey;
|
||||
const isMega = formKey.indexOf(SpeciesFormKey.MEGA) > -1;
|
||||
const isGmax = formKey.indexOf(SpeciesFormKey.GIGANTAMAX) > -1;
|
||||
const isEmax = formKey.indexOf(SpeciesFormKey.ETERNAMAX) > -1;
|
||||
const isRevert = !isMega && formChange.formKey === pokemon.species.forms[0].formKey;
|
||||
if (isMega) {
|
||||
return i18next.t("battlePokemonForm:megaChange", {
|
||||
|
@ -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}.
|
||||
|
@ -44,7 +44,8 @@ import type {
|
||||
TrainerTierPools,
|
||||
} from "#types/trainer-funcs";
|
||||
import type { Mutable } from "#types/type-helpers";
|
||||
import { coerceArray, randSeedInt, randSeedIntRange, randSeedItem } from "#utils/common";
|
||||
import { coerceArray } from "#utils/array";
|
||||
import { randSeedInt, randSeedIntRange, randSeedItem } from "#utils/common";
|
||||
import { getPokemonSpecies } from "#utils/pokemon-utils";
|
||||
import { toCamelCase, toTitleCase } from "#utils/strings";
|
||||
import i18next from "i18next";
|
||||
@ -999,7 +1000,7 @@ let t = 0;
|
||||
* @param postProcess
|
||||
*/
|
||||
export function getRandomPartyMemberFunc(
|
||||
speciesPool: SpeciesId[],
|
||||
speciesPool: readonly SpeciesId[],
|
||||
trainerSlot: TrainerSlot = TrainerSlot.TRAINER,
|
||||
ignoreEvolution = false,
|
||||
postProcess?: (enemyPokemon: EnemyPokemon) => void,
|
||||
|
@ -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 {
|
||||
@ -587,7 +588,7 @@ export class Arena {
|
||||
overrideTint(): [number, number, number] {
|
||||
switch (Overrides.ARENA_TINT_OVERRIDE) {
|
||||
case TimeOfDay.DUSK:
|
||||
return [98, 48, 73].map(c => Math.round((c + 128) / 2)) as [number, number, number];
|
||||
return [113, 88, 101];
|
||||
case TimeOfDay.NIGHT:
|
||||
return [64, 64, 64];
|
||||
case TimeOfDay.DAWN:
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { Pokemon } from "#field/pokemon";
|
||||
import { coerceArray, fixedInt, randInt } from "#utils/common";
|
||||
import { coerceArray } from "#utils/array";
|
||||
import { fixedInt, randInt } from "#utils/common";
|
||||
|
||||
export class PokemonSpriteSparkleHandler {
|
||||
private sprites: Set<Phaser.GameObjects.Sprite>;
|
||||
|
@ -141,6 +141,7 @@ 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";
|
||||
@ -151,11 +152,10 @@ 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 } from "#utils/array";
|
||||
import { applyChallenges } from "#utils/challenge-utils";
|
||||
import {
|
||||
BooleanHolder,
|
||||
type Constructor,
|
||||
coerceArray,
|
||||
deltaRgb,
|
||||
fixedInt,
|
||||
getIvsFromId,
|
||||
@ -2141,7 +2141,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
* Suppresses an ability and calls its onlose attributes
|
||||
*/
|
||||
public suppressAbility() {
|
||||
[true, false].forEach(passive => applyOnLoseAbAttrs({ pokemon: this, passive }));
|
||||
applyOnLoseAbAttrs({ pokemon: this, passive: true });
|
||||
applyOnLoseAbAttrs({ pokemon: this, passive: false });
|
||||
this.summonData.abilitySuppressed = true;
|
||||
}
|
||||
|
||||
|
@ -642,14 +642,14 @@ export class Trainer extends Phaser.GameObjects.Container {
|
||||
}
|
||||
}
|
||||
|
||||
genModifiers(party: EnemyPokemon[]): PersistentModifier[] {
|
||||
genModifiers(party: readonly EnemyPokemon[]): PersistentModifier[] {
|
||||
if (this.config.genModifiersFunc) {
|
||||
return this.config.genModifiersFunc(party);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
genAI(party: EnemyPokemon[]) {
|
||||
genAI(party: readonly EnemyPokemon[]) {
|
||||
if (this.config.genAIFuncs) {
|
||||
this.config.genAIFuncs.forEach(f => f(party));
|
||||
}
|
||||
|
@ -277,7 +277,7 @@ export class ModifierType {
|
||||
}
|
||||
}
|
||||
|
||||
type ModifierTypeGeneratorFunc = (party: Pokemon[], pregenArgs?: any[]) => ModifierType | null;
|
||||
type ModifierTypeGeneratorFunc = (party: readonly Pokemon[], pregenArgs?: any[]) => ModifierType | null;
|
||||
|
||||
export class ModifierTypeGenerator extends ModifierType {
|
||||
private genTypeFunc: ModifierTypeGeneratorFunc;
|
||||
@ -287,7 +287,7 @@ export class ModifierTypeGenerator extends ModifierType {
|
||||
this.genTypeFunc = genTypeFunc;
|
||||
}
|
||||
|
||||
generateType(party: Pokemon[], pregenArgs?: any[]) {
|
||||
generateType(party: readonly Pokemon[], pregenArgs?: any[]) {
|
||||
const ret = this.genTypeFunc(party, pregenArgs);
|
||||
if (ret) {
|
||||
ret.id = this.id;
|
||||
@ -2360,7 +2360,11 @@ const tierWeights = [768 / 1024, 195 / 1024, 48 / 1024, 12 / 1024, 1 / 1024];
|
||||
*/
|
||||
export const itemPoolChecks: Map<ModifierTypeKeys, boolean | undefined> = new Map();
|
||||
|
||||
export function regenerateModifierPoolThresholds(party: Pokemon[], poolType: ModifierPoolType, rerollCount = 0) {
|
||||
export function regenerateModifierPoolThresholds(
|
||||
party: readonly Pokemon[],
|
||||
poolType: ModifierPoolType,
|
||||
rerollCount = 0,
|
||||
) {
|
||||
const pool = getModifierPoolForType(poolType);
|
||||
itemPoolChecks.forEach((_v, k) => {
|
||||
itemPoolChecks.set(k, false);
|
||||
@ -2906,7 +2910,7 @@ export class ModifierTypeOption {
|
||||
* @param party The player's party.
|
||||
* @returns A number between 0 and 14 based on the party's total luck value, or a random number between 0 and 14 if the player is in Daily Run mode.
|
||||
*/
|
||||
export function getPartyLuckValue(party: Pokemon[]): number {
|
||||
export function getPartyLuckValue(party: readonly Pokemon[]): number {
|
||||
if (globalScene.gameMode.isDaily) {
|
||||
const DailyLuck = new NumberHolder(0);
|
||||
globalScene.executeWithSeedOffset(
|
||||
|
@ -12,7 +12,7 @@ export abstract class BattlePhase extends Phase {
|
||||
const tintSprites = globalScene.currentBattle.trainer.getTintSprites();
|
||||
for (let i = 0; i < sprites.length; i++) {
|
||||
const visible = !trainerSlot || !i === (trainerSlot === TrainerSlot.TRAINER) || sprites.length < 2;
|
||||
[sprites[i], tintSprites[i]].map(sprite => {
|
||||
[sprites[i], tintSprites[i]].forEach(sprite => {
|
||||
if (visible) {
|
||||
sprite.x = trainerSlot || sprites.length < 2 ? 0 : i ? 16 : -16;
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ export class EncounterPhase extends BattlePhase {
|
||||
!!globalScene.getEncounterBossSegments(battle.waveIndex, level, enemySpecies),
|
||||
);
|
||||
if (globalScene.currentBattle.battleSpec === BattleSpec.FINAL_BOSS) {
|
||||
battle.enemyParty[e].ivs = new Array(6).fill(31);
|
||||
battle.enemyParty[e].ivs.fill(31);
|
||||
}
|
||||
globalScene
|
||||
.getPlayerParty()
|
||||
|
@ -37,9 +37,9 @@ import { getMoveTargets, isFieldTargeted } from "#moves/move-utils";
|
||||
import { PokemonMove } from "#moves/pokemon-move";
|
||||
import { PokemonPhase } from "#phases/pokemon-phase";
|
||||
import { DamageAchv } from "#system/achv";
|
||||
import type { nil } from "#types/common";
|
||||
import type { DamageResult } from "#types/damage-result";
|
||||
import type { TurnMove } from "#types/turn-move";
|
||||
import type { nil } from "#utils/common";
|
||||
import { BooleanHolder, NumberHolder } from "#utils/common";
|
||||
import i18next from "i18next";
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { coerceArray } from "#utils/common";
|
||||
import { coerceArray } from "#utils/array";
|
||||
|
||||
let manifest: object;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { coerceArray } from "#utils/common";
|
||||
import { coerceArray } from "#utils/array";
|
||||
|
||||
export const legacyCompatibleImages: string[] = [];
|
||||
|
||||
|
@ -7,8 +7,8 @@ import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { TextStyle } from "#enums/text-style";
|
||||
import { WeatherType } from "#enums/weather-type";
|
||||
import type { nil } from "#types/common";
|
||||
import { addTextObject } from "#ui/text";
|
||||
import type { nil } from "#utils/common";
|
||||
import i18next from "i18next";
|
||||
|
||||
export enum EventType {
|
||||
@ -18,59 +18,59 @@ export enum EventType {
|
||||
}
|
||||
|
||||
interface EventBanner {
|
||||
bannerKey?: string;
|
||||
xOffset?: number;
|
||||
yOffset?: number;
|
||||
scale?: number;
|
||||
availableLangs?: string[];
|
||||
readonly bannerKey?: string;
|
||||
readonly xOffset?: number;
|
||||
readonly yOffset?: number;
|
||||
readonly scale?: number;
|
||||
readonly availableLangs?: readonly string[];
|
||||
}
|
||||
|
||||
interface EventEncounter {
|
||||
species: SpeciesId;
|
||||
blockEvolution?: boolean;
|
||||
formIndex?: number;
|
||||
readonly species: SpeciesId;
|
||||
readonly blockEvolution?: boolean;
|
||||
readonly formIndex?: number;
|
||||
}
|
||||
|
||||
interface EventMysteryEncounterTier {
|
||||
mysteryEncounter: MysteryEncounterType;
|
||||
tier?: MysteryEncounterTier;
|
||||
disable?: boolean;
|
||||
readonly mysteryEncounter: MysteryEncounterType;
|
||||
readonly tier?: MysteryEncounterTier;
|
||||
readonly disable?: boolean;
|
||||
}
|
||||
|
||||
interface EventWaveReward {
|
||||
wave: number;
|
||||
type: string;
|
||||
readonly wave: number;
|
||||
readonly type: string;
|
||||
}
|
||||
|
||||
type EventMusicReplacement = [string, string];
|
||||
type EventMusicReplacement = readonly [string, string];
|
||||
|
||||
interface EventChallenge {
|
||||
challenge: Challenges;
|
||||
value: number;
|
||||
readonly challenge: Challenges;
|
||||
readonly value: number;
|
||||
}
|
||||
|
||||
interface TimedEvent extends EventBanner {
|
||||
name: string;
|
||||
eventType: EventType;
|
||||
shinyMultiplier?: number;
|
||||
classicFriendshipMultiplier?: number;
|
||||
luckBoost?: number;
|
||||
upgradeUnlockedVouchers?: boolean;
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
eventEncounters?: EventEncounter[];
|
||||
delibirdyBuff?: string[];
|
||||
weather?: WeatherPoolEntry[];
|
||||
mysteryEncounterTierChanges?: EventMysteryEncounterTier[];
|
||||
luckBoostedSpecies?: SpeciesId[];
|
||||
boostFusions?: boolean; //MODIFIER REWORK PLEASE
|
||||
classicWaveRewards?: EventWaveReward[]; // Rival battle rewards
|
||||
trainerShinyChance?: number; // Odds over 65536 of trainer mon generating as shiny
|
||||
music?: EventMusicReplacement[];
|
||||
dailyRunChallenges?: EventChallenge[];
|
||||
readonly name: string;
|
||||
readonly eventType: EventType;
|
||||
readonly shinyMultiplier?: number;
|
||||
readonly classicFriendshipMultiplier?: number;
|
||||
readonly luckBoost?: number;
|
||||
readonly upgradeUnlockedVouchers?: boolean;
|
||||
readonly startDate: Date;
|
||||
readonly endDate: Date;
|
||||
readonly eventEncounters?: readonly EventEncounter[];
|
||||
readonly delibirdyBuff?: readonly string[];
|
||||
readonly weather?: readonly WeatherPoolEntry[];
|
||||
readonly mysteryEncounterTierChanges?: readonly EventMysteryEncounterTier[];
|
||||
readonly luckBoostedSpecies?: readonly SpeciesId[];
|
||||
readonly boostFusions?: boolean; //MODIFIER REWORK PLEASE
|
||||
readonly classicWaveRewards?: readonly EventWaveReward[]; // Rival battle rewards
|
||||
readonly trainerShinyChance?: number; // Odds over 65536 of trainer mon generating as shiny
|
||||
readonly music?: readonly EventMusicReplacement[];
|
||||
readonly dailyRunChallenges?: readonly EventChallenge[];
|
||||
}
|
||||
|
||||
const timedEvents: TimedEvent[] = [
|
||||
const timedEvents: readonly TimedEvent[] = [
|
||||
{
|
||||
name: "Winter Holiday Update",
|
||||
eventType: EventType.SHINY,
|
||||
@ -385,7 +385,8 @@ const timedEvents: TimedEvent[] = [
|
||||
|
||||
export class TimedEventManager {
|
||||
isActive(event: TimedEvent) {
|
||||
return event.startDate < new Date() && new Date() < event.endDate;
|
||||
const now = new Date();
|
||||
return event.startDate < now && now < event.endDate;
|
||||
}
|
||||
|
||||
activeEvent(): TimedEvent | undefined {
|
||||
@ -427,19 +428,17 @@ export class TimedEventManager {
|
||||
|
||||
getEventBannerLangs(): string[] {
|
||||
const ret: string[] = [];
|
||||
ret.push(...timedEvents.find(te => this.isActive(te) && te.availableLangs != null)?.availableLangs!);
|
||||
ret.push(...(timedEvents.find(te => this.isActive(te) && te.availableLangs != null)?.availableLangs ?? []));
|
||||
return ret;
|
||||
}
|
||||
|
||||
getEventEncounters(): EventEncounter[] {
|
||||
const ret: EventEncounter[] = [];
|
||||
timedEvents
|
||||
.filter(te => this.isActive(te))
|
||||
.map(te => {
|
||||
if (te.eventEncounters != null) {
|
||||
ret.push(...te.eventEncounters);
|
||||
}
|
||||
});
|
||||
for (const te of timedEvents) {
|
||||
if (this.isActive(te) && te.eventEncounters != null) {
|
||||
ret.push(...te.eventEncounters);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -472,13 +471,11 @@ export class TimedEventManager {
|
||||
*/
|
||||
getDelibirdyBuff(): string[] {
|
||||
const ret: string[] = [];
|
||||
timedEvents
|
||||
.filter(te => this.isActive(te))
|
||||
.map(te => {
|
||||
if (te.delibirdyBuff != null) {
|
||||
ret.push(...te.delibirdyBuff);
|
||||
}
|
||||
});
|
||||
for (const te of timedEvents) {
|
||||
if (this.isActive(te) && te.delibirdyBuff != null) {
|
||||
ret.push(...te.delibirdyBuff);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -488,39 +485,35 @@ export class TimedEventManager {
|
||||
*/
|
||||
getWeather(): WeatherPoolEntry[] {
|
||||
const ret: WeatherPoolEntry[] = [];
|
||||
timedEvents
|
||||
.filter(te => this.isActive(te))
|
||||
.map(te => {
|
||||
if (te.weather != null) {
|
||||
ret.push(...te.weather);
|
||||
}
|
||||
});
|
||||
for (const te of timedEvents) {
|
||||
if (this.isActive(te) && te.weather != null) {
|
||||
ret.push(...te.weather);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
getAllMysteryEncounterChanges(): EventMysteryEncounterTier[] {
|
||||
const ret: EventMysteryEncounterTier[] = [];
|
||||
timedEvents
|
||||
.filter(te => this.isActive(te))
|
||||
.map(te => {
|
||||
if (te.mysteryEncounterTierChanges != null) {
|
||||
ret.push(...te.mysteryEncounterTierChanges);
|
||||
}
|
||||
});
|
||||
for (const te of timedEvents) {
|
||||
if (this.isActive(te) && te.mysteryEncounterTierChanges != null) {
|
||||
ret.push(...te.mysteryEncounterTierChanges);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
getEventMysteryEncountersDisabled(): MysteryEncounterType[] {
|
||||
const ret: MysteryEncounterType[] = [];
|
||||
timedEvents
|
||||
.filter(te => this.isActive(te) && te.mysteryEncounterTierChanges != null)
|
||||
.map(te => {
|
||||
te.mysteryEncounterTierChanges?.map(metc => {
|
||||
for (const te of timedEvents) {
|
||||
if (this.isActive(te) && te.mysteryEncounterTierChanges != null) {
|
||||
for (const metc of te.mysteryEncounterTierChanges) {
|
||||
if (metc.disable) {
|
||||
ret.push(metc.mysteryEncounter);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -529,15 +522,15 @@ export class TimedEventManager {
|
||||
normal: MysteryEncounterTier,
|
||||
): MysteryEncounterTier {
|
||||
let ret = normal;
|
||||
timedEvents
|
||||
.filter(te => this.isActive(te) && te.mysteryEncounterTierChanges != null)
|
||||
.map(te => {
|
||||
te.mysteryEncounterTierChanges?.map(metc => {
|
||||
for (const te of timedEvents) {
|
||||
if (this.isActive(te) && te.mysteryEncounterTierChanges != null) {
|
||||
for (const metc of te.mysteryEncounterTierChanges) {
|
||||
if (metc.mysteryEncounter === encounterType) {
|
||||
ret = metc.tier ?? normal;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -551,15 +544,16 @@ export class TimedEventManager {
|
||||
}
|
||||
|
||||
getEventLuckBoostedSpecies(): SpeciesId[] {
|
||||
const ret: SpeciesId[] = [];
|
||||
timedEvents
|
||||
.filter(te => this.isActive(te))
|
||||
.map(te => {
|
||||
if (te.luckBoostedSpecies != null) {
|
||||
ret.push(...te.luckBoostedSpecies.filter(s => !ret.includes(s)));
|
||||
const ret = new Set<SpeciesId>();
|
||||
|
||||
for (const te of timedEvents) {
|
||||
if (this.isActive(te) && te.luckBoostedSpecies != null) {
|
||||
for (const s of te.luckBoostedSpecies) {
|
||||
ret.add(s);
|
||||
}
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return Array.from(ret);
|
||||
}
|
||||
|
||||
areFusionsBoosted(): boolean {
|
||||
@ -574,45 +568,50 @@ export class TimedEventManager {
|
||||
*/
|
||||
getFixedBattleEventRewards(wave: number): string[] {
|
||||
const ret: string[] = [];
|
||||
timedEvents
|
||||
.filter(te => this.isActive(te) && te.classicWaveRewards != null)
|
||||
.map(te => {
|
||||
ret.push(...te.classicWaveRewards!.filter(cwr => cwr.wave === wave).map(cwr => cwr.type));
|
||||
});
|
||||
for (const te of timedEvents) {
|
||||
if (this.isActive(te) && te.classicWaveRewards != null) {
|
||||
ret.push(...te.classicWaveRewards.filter(cwr => cwr.wave === wave).map(cwr => cwr.type));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Gets the extra shiny chance for trainers due to event (odds/65536)
|
||||
/**
|
||||
* Get the extra shiny chance for trainers due to event
|
||||
*/
|
||||
getClassicTrainerShinyChance(): number {
|
||||
let ret = 0;
|
||||
const tsEvents = timedEvents.filter(te => this.isActive(te) && te.trainerShinyChance != null);
|
||||
tsEvents.map(t => (ret += t.trainerShinyChance!));
|
||||
for (const te of timedEvents) {
|
||||
const shinyChance = te.trainerShinyChance;
|
||||
if (shinyChance && this.isActive(te)) {
|
||||
ret += shinyChance;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
getEventBgmReplacement(bgm: string): string {
|
||||
let ret = bgm;
|
||||
timedEvents.map(te => {
|
||||
for (const te of timedEvents) {
|
||||
if (this.isActive(te) && te.music != null) {
|
||||
te.music.map(mr => {
|
||||
for (const mr of te.music) {
|
||||
if (mr[0] === bgm) {
|
||||
console.log(`it is ${te.name} so instead of ${mr[0]} we play ${mr[1]}`);
|
||||
ret = mr[1];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates any challenges on {@linkcode globalScene.gameMode} for the currently active event
|
||||
* Activate any challenges on {@linkcode globalScene.gameMode} for the currently active event
|
||||
*/
|
||||
startEventChallenges(): void {
|
||||
const challenges = this.activeEvent()?.dailyRunChallenges;
|
||||
challenges?.forEach((eventChal: EventChallenge) =>
|
||||
globalScene.gameMode.setChallengeValue(eventChal.challenge, eventChal.value),
|
||||
);
|
||||
for (const eventChal of this.activeEvent()?.dailyRunChallenges ?? []) {
|
||||
globalScene.gameMode.setChallengeValue(eventChal.challenge, eventChal.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
7
src/typings/phaser/index.d.ts
vendored
7
src/typings/phaser/index.d.ts
vendored
@ -54,6 +54,13 @@ declare module "phaser" {
|
||||
}
|
||||
}
|
||||
|
||||
namespace Math {
|
||||
interface RandomDataGenerator {
|
||||
pick<T>(array: ArrayLike<T>): T;
|
||||
weightedPick<T>(array: ArrayLike<T>): T;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Input {
|
||||
namespace Gamepad {
|
||||
interface GamepadPlugin {
|
||||
|
@ -240,8 +240,8 @@ export class PokedexPageUiHandler extends MessageUiHandler {
|
||||
private passive: AbilityId;
|
||||
private hasPassive: boolean;
|
||||
private hasAbilities: number[];
|
||||
private biomes: BiomeTierTod[];
|
||||
private preBiomes: BiomeTierTod[];
|
||||
private biomes: readonly BiomeTierTod[];
|
||||
private preBiomes: readonly BiomeTierTod[];
|
||||
private baseStats: number[];
|
||||
private baseTotal: number;
|
||||
private evolutions: SpeciesFormEvolution[];
|
||||
@ -893,7 +893,7 @@ export class PokedexPageUiHandler extends MessageUiHandler {
|
||||
}
|
||||
|
||||
// Function to ensure that forms appear in the appropriate biome and tod
|
||||
sanitizeBiomes(biomes: BiomeTierTod[], speciesId: number): BiomeTierTod[] {
|
||||
sanitizeBiomes(biomes: readonly BiomeTierTod[], speciesId: number): readonly BiomeTierTod[] {
|
||||
if (speciesId === SpeciesId.BURMY || speciesId === SpeciesId.WORMADAM) {
|
||||
return biomes.filter(b => {
|
||||
const formIndex = (() => {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { coerceArray, fixedInt } from "#utils/common";
|
||||
import { coerceArray } from "#utils/array";
|
||||
import { fixedInt } from "#utils/common";
|
||||
|
||||
export enum PokemonIconAnimMode {
|
||||
NONE,
|
||||
|
101
src/utils/array.ts
Normal file
101
src/utils/array.ts
Normal file
@ -0,0 +1,101 @@
|
||||
import type {
|
||||
ReadonlyBigInt64Array,
|
||||
ReadonlyBigIntArray,
|
||||
ReadonlyBigUint64Array,
|
||||
ReadonlyGenericArray,
|
||||
ReadonlyNumberCompatibleTypedArray,
|
||||
ReadonlyTypedArray,
|
||||
TypedArray,
|
||||
} from "#types/typed-arrays";
|
||||
|
||||
/**
|
||||
* 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<T>(input: T): T extends readonly any[] ? T : [T];
|
||||
export function coerceArray<T>(input: T): T | [T] {
|
||||
return Array.isArray(input) ? input : [input];
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard to check if an input is a typed array, defined as an `ArrayBufferView` that is not a `DataView`.
|
||||
*/
|
||||
export function isTypedArray(input: unknown): input is TypedArray {
|
||||
return ArrayBuffer.isView(input) && !(input instanceof DataView);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a subarray view of the first `n` elements of an array-like object, to use
|
||||
* with constructing a new one, with minimal allocaitons.
|
||||
*
|
||||
* @remarks
|
||||
* This is primarily useful for setting elements of a `TypedArray` using its `set` method.
|
||||
*
|
||||
* @privateRemarks
|
||||
* Note that if this is used with a tuple type, typescript will improperly set
|
||||
* the return type to be a tuple of the same length as input.
|
||||
*
|
||||
* @param arr - The array-like object to take elements from
|
||||
* @param n - The maximum number of elements to take. If
|
||||
* @returns An array-like object whose `length` property is guaranteed to be <= `n`
|
||||
*
|
||||
* @typeParam T - The element type of the array-like object
|
||||
* @typeParam A - The type of the array-like object
|
||||
*
|
||||
* @example
|
||||
* ```
|
||||
* const arr = new Uint8Array(3);
|
||||
* const other = [1, 2, 3, 4, 5];
|
||||
* // Using arr.set(other) would throw, as other.length > arr.length
|
||||
* arr.set(subArray(other, arr.length));
|
||||
*
|
||||
* @throws {TypeError}
|
||||
* If `arr` is not an array or typed array (though typescript should prevent this)
|
||||
*/
|
||||
export function subArray<const A extends ReadonlyTypedArray | readonly unknown[]>(arr: A, n: number): typeof arr {
|
||||
if (arr.length <= n) {
|
||||
return arr;
|
||||
}
|
||||
|
||||
const len = Math.min(arr.length, n);
|
||||
|
||||
if (Array.isArray(arr)) {
|
||||
// The only path with a new allocation
|
||||
return arr.slice(0, len) as typeof arr;
|
||||
}
|
||||
if (isTypedArray(arr)) {
|
||||
return arr.subarray(0, len) as typeof arr;
|
||||
}
|
||||
throw new TypeError("Expecting an array or typed array");
|
||||
}
|
||||
|
||||
/**
|
||||
* Store multiple values in the typed array, from input values from a specified array, without
|
||||
* the possibility of a `RangeError` being thrown.
|
||||
*
|
||||
* @remarks
|
||||
* Almost equivalent to calling `target.set(source, offset)`, though ensures that
|
||||
* `RangeError` can never be thrown by clamping the number of elements taken from `source`
|
||||
* to the available space in `target` starting at `offset`.
|
||||
*
|
||||
* @param target - The typed array to set values in
|
||||
* @param source - The array-like object to take values from
|
||||
* @param offset - The offset in `target` to start writing values at, default `0`
|
||||
*
|
||||
* @see {@linkcode https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/set | TypedArray#set}
|
||||
*/
|
||||
export function setTypedArray<const T extends ReadonlyTypedArray | ReadonlyGenericArray>(
|
||||
target: T,
|
||||
source: T extends ReadonlyBigInt64Array | ReadonlyBigUint64Array
|
||||
? ReadonlyBigIntArray | readonly bigint[]
|
||||
: ReadonlyNumberCompatibleTypedArray | readonly number[],
|
||||
offset = 0,
|
||||
): void {
|
||||
if (offset < 0 || offset >= target.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// @ts-expect-error - TS can't link the conditional type of source to the conditional type of target
|
||||
// in the body here, despite the fact that the function signature guarantees it.
|
||||
target.set(subArray(source, target.length - offset), offset);
|
||||
}
|
@ -3,8 +3,6 @@ import { MoneyFormat } from "#enums/money-format";
|
||||
import type { Variant } from "#sprites/variant";
|
||||
import i18next from "i18next";
|
||||
|
||||
export type nil = null | undefined;
|
||||
|
||||
export const MissingTextureKey = "__MISSING";
|
||||
|
||||
// TODO: Draft tests for these utility functions
|
||||
@ -125,29 +123,25 @@ export function randSeedFloat(): number {
|
||||
return Phaser.Math.RND.frac();
|
||||
}
|
||||
|
||||
export function randItem<T>(items: T[]): T {
|
||||
export function randItem<T>(items: ArrayLike<T>): T {
|
||||
return items.length === 1 ? items[0] : items[randInt(items.length)];
|
||||
}
|
||||
|
||||
export function randSeedItem<T>(items: T[]): T {
|
||||
export function randSeedItem<T>(items: ArrayLike<T>): T {
|
||||
return items.length === 1 ? items[0] : Phaser.Math.RND.pick(items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuffle a list using the seeded rng. Utilises the Fisher-Yates algorithm.
|
||||
* Shuffle a list in place using the seeded rng and the Fisher-Yates algorithm.
|
||||
* @param items An array of items.
|
||||
* @returns A new shuffled array of items.
|
||||
* @returns `items` shuffled in place.
|
||||
*/
|
||||
export function randSeedShuffle<T>(items: T[]): T[] {
|
||||
if (items.length <= 1) {
|
||||
return items;
|
||||
}
|
||||
const newArray = items.slice(0);
|
||||
for (let i = items.length - 1; i > 0; i--) {
|
||||
const j = Phaser.Math.RND.integerInRange(0, i);
|
||||
[newArray[i], newArray[j]] = [newArray[j], newArray[i]];
|
||||
[items[i], items[j]] = [items[j], items[i]];
|
||||
}
|
||||
return newArray;
|
||||
return items;
|
||||
}
|
||||
|
||||
export function getFrameMs(frameCount: number): number {
|
||||
@ -176,7 +170,7 @@ 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): number[] {
|
||||
export function getIvsFromId(id: number): [number, number, number, number, number, number] {
|
||||
return [
|
||||
(id & 0x3e000000) >>> 25,
|
||||
(id & 0x01f00000) >>> 20,
|
||||
@ -292,9 +286,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 {
|
||||
public value: boolean;
|
||||
|
||||
@ -343,7 +334,7 @@ export function rgbToHsv(r: number, g: number, b: number) {
|
||||
* @param rgb1 First RGB color in array
|
||||
* @param rgb2 Second RGB color in array
|
||||
*/
|
||||
export function deltaRgb(rgb1: number[], rgb2: number[]): number {
|
||||
export function deltaRgb(rgb1: readonly number[], rgb2: readonly number[]): number {
|
||||
const [r1, g1, b1] = rgb1;
|
||||
const [r2, g2, b2] = rgb2;
|
||||
const drp2 = Math.pow(r1 - r2, 2);
|
||||
@ -367,7 +358,7 @@ export function rgbHexToRgba(hex: string) {
|
||||
};
|
||||
}
|
||||
|
||||
export function rgbaToInt(rgba: number[]): number {
|
||||
export function rgbaToInt(rgba: readonly number[]): number {
|
||||
return (rgba[0] << 24) + (rgba[1] << 16) + (rgba[2] << 8) + rgba[3];
|
||||
}
|
||||
|
||||
@ -506,12 +497,3 @@ export function getShinyDescriptor(variant: Variant): string {
|
||||
return i18next.t("common:commonShiny");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<T>(input: T): T extends any[] ? T : [T];
|
||||
export function coerceArray<T>(input: T): T | [T] {
|
||||
return Array.isArray(input) ? input : [input];
|
||||
}
|
||||
|
@ -67,6 +67,7 @@ export function isBareObject(obj: any): boolean {
|
||||
if (typeof obj !== "object") {
|
||||
return false;
|
||||
}
|
||||
// biome-ignore lint/suspicious/useGuardForIn: Checking a bare object should include prototype chain
|
||||
for (const _ in obj) {
|
||||
return false;
|
||||
}
|
||||
|
@ -16,22 +16,23 @@ interface hasPokemon {
|
||||
* @returns The sorted array of {@linkcode Pokemon}
|
||||
*/
|
||||
export function sortInSpeedOrder<T extends Pokemon | hasPokemon>(pokemonList: T[], shuffleFirst = true): T[] {
|
||||
pokemonList = shuffleFirst ? shufflePokemonList(pokemonList) : pokemonList;
|
||||
if (shuffleFirst) {
|
||||
shufflePokemonList(pokemonList);
|
||||
}
|
||||
sortBySpeed(pokemonList);
|
||||
return pokemonList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuffle the list of pokemon *in place*
|
||||
* @param pokemonList - The array of Pokemon or objects containing Pokemon
|
||||
* @returns The shuffled array
|
||||
* @returns The same array instance that was passed in, shuffled.
|
||||
*/
|
||||
function shufflePokemonList<T extends Pokemon | hasPokemon>(pokemonList: T[]): T[] {
|
||||
// This is seeded with the current turn to prevent an inconsistency where it
|
||||
// was varying based on how long since you last reloaded
|
||||
globalScene.executeWithSeedOffset(
|
||||
() => {
|
||||
pokemonList = randSeedShuffle(pokemonList);
|
||||
},
|
||||
() => randSeedShuffle(pokemonList),
|
||||
globalScene.currentBattle.turn * 1000 + pokemonList.length,
|
||||
globalScene.waveSeed,
|
||||
);
|
||||
|
@ -166,8 +166,8 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
||||
|
||||
// Mock stats by replacing entries in copy with desired values for specific stats
|
||||
const stats = {
|
||||
enemy: [[...enemyParty[0].stats], [...enemyParty[1].stats]],
|
||||
player: [[...party[0].stats], [...party[1].stats]],
|
||||
enemy: [enemyParty[0].stats.slice(), enemyParty[1].stats.slice()],
|
||||
player: [party[0].stats.slice(), party[1].stats.slice()],
|
||||
};
|
||||
|
||||
// Ensure survival by reducing enemy Sp. Atk and boosting party Sp. Def
|
||||
@ -220,8 +220,8 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
||||
|
||||
// Mock stats by replacing entries in copy with desired values for specific stats
|
||||
const stats = {
|
||||
enemy: [[...enemyParty[0].stats], [...enemyParty[1].stats]],
|
||||
player: [[...party[0].stats], [...party[1].stats]],
|
||||
enemy: [enemyParty[0].stats.slice(), enemyParty[1].stats.slice()],
|
||||
player: [party[0].stats.slice(), party[1].stats.slice()],
|
||||
};
|
||||
|
||||
// Ensure survival by reducing enemy Sp. Atk and boosting party Sp. Def
|
||||
|
@ -12,7 +12,7 @@ import type { CommandPhase } from "#phases/command-phase";
|
||||
import type { EnemyCommandPhase } from "#phases/enemy-command-phase";
|
||||
import { MoveEffectPhase } from "#phases/move-effect-phase";
|
||||
import { GameManagerHelper } from "#test/test-utils/helpers/game-manager-helper";
|
||||
import { coerceArray } from "#utils/common";
|
||||
import { coerceArray } from "#utils/array";
|
||||
import { toTitleCase } from "#utils/strings";
|
||||
import type { MockInstance } from "vitest";
|
||||
import { expect, vi } from "vitest";
|
||||
|
@ -20,7 +20,8 @@ import { WeatherType } from "#enums/weather-type";
|
||||
import type { ModifierOverride } from "#modifiers/modifier-type";
|
||||
import type { Variant } from "#sprites/variant";
|
||||
import { GameManagerHelper } from "#test/test-utils/helpers/game-manager-helper";
|
||||
import { coerceArray, shiftCharCodes } from "#utils/common";
|
||||
import { coerceArray } from "#utils/array";
|
||||
import { shiftCharCodes } from "#utils/common";
|
||||
import chalk from "chalk";
|
||||
import { vi } from "vitest";
|
||||
|
||||
|
@ -7,7 +7,7 @@ import Overrides from "#app/overrides";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { getEnumStr } from "#test/test-utils/string-utils";
|
||||
import { isPokemonInstance, receivedStr } from "#test/test-utils/test-utils";
|
||||
import { coerceArray } from "#utils/common";
|
||||
import { coerceArray } from "#utils/array";
|
||||
import type { MatcherState, SyncExpectationResult } from "@vitest/expect";
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { DEBUG_COLOR, NEW_TURN_COLOR, TRACE_COLOR, UI_MSG_COLOR } from "#app/constants/colors";
|
||||
import { inferColorFormat } from "#test/test-utils/mocks/mock-console/infer-color";
|
||||
import { coerceArray } from "#utils/common";
|
||||
import { coerceArray } from "#utils/array";
|
||||
import { type InspectOptions, inspect } from "node:util";
|
||||
import chalk, { type ChalkInstance } from "chalk";
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { MockGameObject } from "#test/test-utils/mocks/mock-game-object";
|
||||
import type { MockTextureManager } from "#test/test-utils/mocks/mock-texture-manager";
|
||||
import { coerceArray } from "#utils/common";
|
||||
import { coerceArray } from "#utils/array";
|
||||
|
||||
export class MockContainer implements MockGameObject {
|
||||
protected x: number;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { MockGameObject } from "#test/test-utils/mocks/mock-game-object";
|
||||
import { coerceArray } from "#utils/common";
|
||||
import { coerceArray } from "#utils/array";
|
||||
|
||||
export class MockRectangle implements MockGameObject {
|
||||
private fillColor;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { MockGameObject } from "#test/test-utils/mocks/mock-game-object";
|
||||
import { coerceArray } from "#utils/common";
|
||||
import { coerceArray } from "#utils/array";
|
||||
import Phaser from "phaser";
|
||||
|
||||
type Frame = Phaser.Textures.Frame;
|
||||
|
279
test/utils/array.test.ts
Normal file
279
test/utils/array.test.ts
Normal file
@ -0,0 +1,279 @@
|
||||
import type { ReadonlyUint8Array } from "#types/typed-arrays";
|
||||
import { coerceArray, isTypedArray, setTypedArray, subArray } from "#utils/array";
|
||||
import { describe, expect, expectTypeOf, it } from "vitest";
|
||||
|
||||
/**
|
||||
* Unit tests for the utility methods in `src/utils/array.ts`
|
||||
* @module
|
||||
*/
|
||||
|
||||
describe("Utils - Array", () => {
|
||||
describe("subArray", () => {
|
||||
it("returns the same array if length <= n (plain array)", () => {
|
||||
const arr = [1, 2, 3];
|
||||
const result = subArray(arr, 5);
|
||||
expect(result).toBe(arr);
|
||||
expect(result).toEqual([1, 2, 3]);
|
||||
});
|
||||
|
||||
it("returns a sliced array if length > n (plain array)", () => {
|
||||
const arr = [1, 2, 3, 4, 5];
|
||||
const result = subArray(arr, 3);
|
||||
expect(result).not.toBe(arr);
|
||||
expect(result).toEqual([1, 2, 3]);
|
||||
});
|
||||
|
||||
it("returns the same typed array if length <= n", () => {
|
||||
const arr = new Uint8Array([1, 2, 3]);
|
||||
const result = subArray(arr, 5);
|
||||
expect(result).toBe(arr);
|
||||
expect(Array.from(result)).toEqual([1, 2, 3]);
|
||||
});
|
||||
|
||||
it("returns a subarray if length > n (typed array)", () => {
|
||||
const arr = new Uint8Array([1, 2, 3, 4, 5]);
|
||||
const result = subArray(arr, 2);
|
||||
expect(result).not.toBe(arr);
|
||||
expect(Array.from(result)).toEqual([1, 2]);
|
||||
});
|
||||
|
||||
it("returns empty array if n is 0 (plain array)", () => {
|
||||
const arr = [1, 2, 3];
|
||||
const result = subArray(arr, 0);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it("returns empty typed array if n is 0", () => {
|
||||
const arr = new Uint8Array([1, 2, 3]);
|
||||
const result = subArray(arr, 0);
|
||||
expect(Array.from(result)).toEqual([]);
|
||||
});
|
||||
|
||||
it("throws TypeError for non-array-like input", () => {
|
||||
// @ts-expect-error
|
||||
expect(() => subArray({ length: 4 }, 2)).toThrow(TypeError);
|
||||
});
|
||||
|
||||
describe("output type inference", () => {
|
||||
it("plain array input", () => {
|
||||
const arr = [1, 2, 3, 4];
|
||||
const result = subArray(arr, 2);
|
||||
expectTypeOf(result).toEqualTypeOf<number[]>();
|
||||
});
|
||||
|
||||
it("typed array input", () => {
|
||||
const arr = new Uint8Array([1, 2, 3, 4]);
|
||||
const result = subArray(arr, 2);
|
||||
// @ts-expect-error We get a questionable error about Uint8Array not being assignable to Uint8Array...
|
||||
expectTypeOf(result).toEqualTypeOf<Uint8Array>();
|
||||
});
|
||||
|
||||
it("readonly array input", () => {
|
||||
const arr: readonly number[] = [1, 2, 3, 4];
|
||||
const result = subArray(arr, 2);
|
||||
expectTypeOf(result).toEqualTypeOf<readonly number[]>();
|
||||
});
|
||||
|
||||
it("readonly typed array input", () => {
|
||||
const arr = new Uint8Array([1, 2, 3, 4]) as ReadonlyUint8Array;
|
||||
const result = subArray(arr, 2);
|
||||
expectTypeOf(result).toEqualTypeOf<ReadonlyUint8Array>();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("setTypedArray", () => {
|
||||
it("sets values from source to target with no offset (fits exactly)", () => {
|
||||
const target = new Uint8Array(3);
|
||||
const source = [1, 2, 3];
|
||||
setTypedArray(target, source);
|
||||
expect(Array.from(target)).toEqual([1, 2, 3]);
|
||||
});
|
||||
|
||||
it("sets values from source to target with offset", () => {
|
||||
const target = new Uint8Array([0, 0, 0, 0, 0]);
|
||||
const source = [9, 8];
|
||||
setTypedArray(target, source, 2);
|
||||
expect(Array.from(target)).toEqual([0, 0, 9, 8, 0]);
|
||||
});
|
||||
|
||||
it("clamps source if it would overflow target", () => {
|
||||
const target = new Uint8Array(4);
|
||||
const source = [1, 2, 3, 4, 5, 6];
|
||||
setTypedArray(target, source, 2);
|
||||
expect(Array.from(target)).toEqual([0, 0, 1, 2]);
|
||||
});
|
||||
|
||||
it("does nothing if offset < 0", () => {
|
||||
const target = new Uint8Array([1, 2, 3]);
|
||||
const source = [4, 5, 6];
|
||||
setTypedArray(target, source, -1);
|
||||
expect(Array.from(target)).toEqual([1, 2, 3]);
|
||||
});
|
||||
|
||||
it("does nothing if offset >= target.length", () => {
|
||||
const target = new Uint8Array([1, 2, 3]);
|
||||
const source = [4, 5, 6];
|
||||
setTypedArray(target, source, 3);
|
||||
expect(Array.from(target)).toEqual([1, 2, 3]);
|
||||
});
|
||||
|
||||
it("does nothing if source is empty", () => {
|
||||
const target = new Uint8Array([1, 2, 3]);
|
||||
const source: number[] = [];
|
||||
setTypedArray(target, source, 1);
|
||||
expect(Array.from(target)).toEqual([1, 2, 3]);
|
||||
});
|
||||
|
||||
it("works with typed array as source", () => {
|
||||
const target = new Uint8Array(4);
|
||||
const source = new Uint8Array([7, 8, 9]);
|
||||
setTypedArray(target, source, 1);
|
||||
expect(Array.from(target)).toEqual([0, 7, 8, 9]);
|
||||
});
|
||||
|
||||
it("clamps source typed array if it would overflow target", () => {
|
||||
const target = new Uint8Array(3);
|
||||
const source = new Uint8Array([1, 2, 3, 4, 5]);
|
||||
setTypedArray(target, source, 1);
|
||||
expect(Array.from(target)).toEqual([0, 1, 2]);
|
||||
});
|
||||
|
||||
it("works with BigUint64Array and bigint[]", () => {
|
||||
if (typeof BigUint64Array !== "undefined") {
|
||||
const target = new BigUint64Array(3);
|
||||
const source = [1n, 2n, 3n, 4n];
|
||||
|
||||
setTypedArray(target, source, 1);
|
||||
expect(Array.from(target)).toEqual([0n, 1n, 2n]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("coerceArray", () => {
|
||||
it("returns the same array if input is already an array", () => {
|
||||
const arr = [1, 2, 3];
|
||||
const result = coerceArray(arr);
|
||||
expect(result).toBe(arr);
|
||||
expect(result).toEqual([1, 2, 3]);
|
||||
});
|
||||
|
||||
it("wraps a non-array input in an array", () => {
|
||||
const input = 42;
|
||||
const result = coerceArray(input);
|
||||
expect(result).toEqual([42]);
|
||||
});
|
||||
|
||||
it("wraps an object in an array", () => {
|
||||
const obj = { a: 1 };
|
||||
const result = coerceArray(obj);
|
||||
expect(result).toEqual([{ a: 1 }]);
|
||||
});
|
||||
|
||||
it("wraps a string in an array", () => {
|
||||
const str = "hello";
|
||||
const result = coerceArray(str);
|
||||
expect(result).toEqual(["hello"]);
|
||||
});
|
||||
|
||||
it("wraps null in an array", () => {
|
||||
const result = coerceArray(null);
|
||||
expect(result).toEqual([null]);
|
||||
});
|
||||
|
||||
it("wraps undefined in an array", () => {
|
||||
const result = coerceArray(undefined);
|
||||
expect(result).toEqual([undefined]);
|
||||
});
|
||||
|
||||
it("returns the same array for empty array input", () => {
|
||||
const arr: number[] = [];
|
||||
const result = coerceArray(arr);
|
||||
expect(result).toBe(arr);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
describe("typing", () => {
|
||||
it("infers correct type for array input", () => {
|
||||
const arr = [1, 2, 3];
|
||||
const result = coerceArray(arr);
|
||||
expectTypeOf(result).toEqualTypeOf<number[]>();
|
||||
});
|
||||
|
||||
it("infers correct type for non-array input", () => {
|
||||
const input = "test";
|
||||
const result = coerceArray(input);
|
||||
expectTypeOf(result).toEqualTypeOf<[string]>();
|
||||
});
|
||||
|
||||
it("infers correct type for object input", () => {
|
||||
const obj = { key: "value" };
|
||||
const result = coerceArray(obj);
|
||||
expectTypeOf(result).toEqualTypeOf<[{ key: string }]>();
|
||||
});
|
||||
|
||||
it("infers correct type for null input", () => {
|
||||
const result = coerceArray(null);
|
||||
expectTypeOf(result).toEqualTypeOf<[null]>();
|
||||
});
|
||||
|
||||
it("infers correct type for undefined input", () => {
|
||||
const result = coerceArray(undefined);
|
||||
expectTypeOf(result).toEqualTypeOf<[undefined]>();
|
||||
});
|
||||
|
||||
it("infers correct type for empty array input", () => {
|
||||
const arr: number[] = [];
|
||||
const result = coerceArray(arr);
|
||||
expectTypeOf(result).toEqualTypeOf<number[]>();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("isTypedArray", () => {
|
||||
it.each([
|
||||
["Int8Array", Int8Array],
|
||||
["Uint8ClampedArray", Uint8ClampedArray],
|
||||
["Uint16Array", Uint16Array],
|
||||
["Uint32Array", Uint32Array],
|
||||
["Float64Array", Float64Array],
|
||||
["Float32Array", Float32Array],
|
||||
])("returns true for %s", (_, ArrayType) => {
|
||||
if (typeof ArrayType !== "undefined") {
|
||||
const arr = new ArrayType([1, 2, 3]);
|
||||
expect(isTypedArray(arr)).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it.each([
|
||||
["BigUint64Array", BigUint64Array],
|
||||
["BigInt64Array", BigInt64Array],
|
||||
])("returns true for %s", (_, ArrayType) => {
|
||||
const arr = new ArrayType([1n, 2n, 3n]);
|
||||
expect(isTypedArray(arr)).toBe(true);
|
||||
});
|
||||
|
||||
it.each([
|
||||
["strings", "hello"],
|
||||
["null", null],
|
||||
["undefined", undefined],
|
||||
["a number", 123],
|
||||
["an object", { a: 1 }],
|
||||
["a plain array", [1, 2, 3]],
|
||||
["a DataView", new DataView(new ArrayBuffer(8))],
|
||||
["an ArrayLike", { length: 2, 0: 1, 1: 2 }],
|
||||
])("returns false for %s", (_, input) => {
|
||||
expect(isTypedArray(input)).toBe(false);
|
||||
});
|
||||
|
||||
it("returns true for an object that extends a typed array", () => {
|
||||
class MyUint8Array extends Uint8Array {
|
||||
customMethod() {
|
||||
return "custom";
|
||||
}
|
||||
}
|
||||
const arr = new MyUint8Array([1, 2, 3]);
|
||||
expect(isTypedArray(arr)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user