Miscellaneous code cleanup:

- Update `overrides.ts` imports

- Add missing comment and `;` in `eslint.config.js`

- `field/pokemon.ts`:

Rename `generateVariant` to `generateShinyVariant`

Mark some functions as `public`/etc

Update various tsdocs/comments, add tsdocs to `isOfType`

Turn an unreadable 450+ character line into a
readable block of code

- `utils.ts`:

Remove unused function `randSeedEasedWeightedItem`

Mark `IntegerHolder` and `FixedInt` as deprecated

Update some tsdocs/comments
This commit is contained in:
NightKev 2024-10-06 02:54:09 -07:00
parent f9691b872b
commit 2a47c32e88
4 changed files with 136 additions and 124 deletions

View File

@ -1,5 +1,5 @@
import tseslint from '@typescript-eslint/eslint-plugin'; import tseslint from '@typescript-eslint/eslint-plugin';
import stylisticTs from '@stylistic/eslint-plugin-ts' import stylisticTs from '@stylistic/eslint-plugin-ts';
import parser from '@typescript-eslint/parser'; import parser from '@typescript-eslint/parser';
import importX from 'eslint-plugin-import-x'; import importX from 'eslint-plugin-import-x';
@ -16,11 +16,11 @@ export default [
'@typescript-eslint': tseslint '@typescript-eslint': tseslint
}, },
rules: { rules: {
"eqeqeq": ["error", "always"], // Enforces the use of === and !== instead of == and != "eqeqeq": ["error", "always"], // Enforces the use of `===` and `!==` instead of `==` and `!=`
"indent": ["error", 2], // Enforces a 2-space indentation "indent": ["error", 2], // Enforces a 2-space indentation
"quotes": ["error", "double"], // Enforces the use of double quotes for strings "quotes": ["error", "double"], // Enforces the use of double quotes for strings
"no-var": "error", // Disallows the use of var, enforcing let or const instead "no-var": "error", // Disallows the use of `var`, enforcing `let` or `const` instead
"prefer-const": "error", // Prefers the use of const for variables that are never reassigned "prefer-const": "error", // Prefers the use of `const` for variables that are never reassigned
"no-undef": "off", // Disables the rule that disallows the use of undeclared variables (TypeScript handles this) "no-undef": "off", // Disables the rule that disallows the use of undeclared variables (TypeScript handles this)
"@typescript-eslint/no-unused-vars": [ "error", { "@typescript-eslint/no-unused-vars": [ "error", {
"args": "none", // Allows unused function parameters. Useful for functions with specific signatures where not all parameters are always used. "args": "none", // Allows unused function parameters. Useful for functions with specific signatures where not all parameters are always used.
@ -32,7 +32,7 @@ export default [
"no-extra-semi": ["error"], // Disallows unnecessary semicolons for TypeScript-specific syntax "no-extra-semi": ["error"], // Disallows unnecessary semicolons for TypeScript-specific syntax
"brace-style": "off", // Note: you must disable the base rule as it can report incorrect errors "brace-style": "off", // Note: you must disable the base rule as it can report incorrect errors
"curly": ["error", "all"], // Enforces the use of curly braces for all control statements "curly": ["error", "all"], // Enforces the use of curly braces for all control statements
"@stylistic/ts/brace-style": ["error", "1tbs"], "@stylistic/ts/brace-style": ["error", "1tbs"], // Enforces `else` inline with braces
"no-trailing-spaces": ["error", { // Disallows trailing whitespace at the end of lines "no-trailing-spaces": ["error", { // Disallows trailing whitespace at the end of lines
"skipBlankLines": false, // Enforces the rule even on blank lines "skipBlankLines": false, // Enforces the rule even on blank lines
"ignoreComments": false // Enforces the rule on lines containing comments "ignoreComments": false // Enforces the rule on lines containing comments

View File

@ -232,7 +232,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
if (this.variant === undefined) { if (this.variant === undefined) {
this.variant = this.shiny ? this.generateVariant() : 0; this.variant = this.shiny ? this.generateShinyVariant() : 0;
} }
this.mysteryEncounterPokemonData = new MysteryEncounterPokemonData(); this.mysteryEncounterPokemonData = new MysteryEncounterPokemonData();
@ -1152,7 +1152,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* @returns an array of {@linkcode Moves}, the length of which is determined * @returns an array of {@linkcode Moves}, the length of which is determined
* by how many learnable moves there are for the {@linkcode Pokemon}. * by how many learnable moves there are for the {@linkcode Pokemon}.
*/ */
getLearnableLevelMoves(): Moves[] { public getLearnableLevelMoves(): Moves[] {
let levelMoves = this.getLevelMoves(1, true, false, true).map(lm => lm[1]); let levelMoves = this.getLevelMoves(1, true, false, true).map(lm => lm[1]);
if (this.metBiome === -1 && !this.scene.gameMode.isFreshStartChallenge() && !this.scene.gameMode.isDaily) { if (this.metBiome === -1 && !this.scene.gameMode.isFreshStartChallenge() && !this.scene.gameMode.isDaily) {
levelMoves = this.getUnlockedEggMoves().concat(levelMoves); levelMoves = this.getUnlockedEggMoves().concat(levelMoves);
@ -1166,13 +1166,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
/** /**
* Gets the types of a pokemon * Gets the types of a pokemon
* @param includeTeraType boolean to include tera-formed type, default false * @param includeTeraType - `true` to include tera-formed type; Default: `false`
* @param forDefend boolean if the pokemon is defending from an attack * @param forDefend - `true` if the pokemon is defending from an attack; Default: `false`
* @param ignoreOverride boolean if true, ignore ability changing effects * @param ignoreOverride - If `true`, ignore ability changing effects; Default: `false`
* @returns array of {@linkcode Type} * @returns array of {@linkcode Type}
*/ */
getTypes(includeTeraType = false, forDefend: boolean = false, ignoreOverride?: boolean): Type[] { public getTypes(includeTeraType = false, forDefend: boolean = false, ignoreOverride: boolean = false): Type[] {
const types : Type[] = []; const types: Type[] = [];
if (includeTeraType) { if (includeTeraType) {
const teraType = this.getTeraType(); const teraType = this.getTeraType();
@ -1237,14 +1237,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
} }
// this.scene potentially can be undefined for a fainted pokemon in doubles // become UNKNOWN if no types are present
// use optional chaining to avoid runtime errors if (!types.length) {
if (!types.length) { // become UNKNOWN if no types are present
types.push(Type.UNKNOWN); types.push(Type.UNKNOWN);
} }
if (types.length > 1 && types.includes(Type.UNKNOWN)) { // remove UNKNOWN if other types are present // remove UNKNOWN if other types are present
if (types.length > 1 && types.includes(Type.UNKNOWN)) {
const index = types.indexOf(Type.UNKNOWN); const index = types.indexOf(Type.UNKNOWN);
if (index !== -1) { if (index !== -1) {
types.splice(index, 1); types.splice(index, 1);
@ -1254,19 +1253,27 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return types; return types;
} }
isOfType(type: Type, includeTeraType: boolean = true, forDefend: boolean = false, ignoreOverride?: boolean): boolean { /**
return !!this.getTypes(includeTeraType, forDefend, ignoreOverride).some(t => t === type); * Checks if the pokemon's typing includes the specified type
* @param type - {@linkcode Type} to check
* @param includeTeraType - `true` to include tera-formed type; Default: `true`
* @param forDefend - `true` if the pokemon is defending from an attack; Default: `false`
* @param ignoreOverride - If `true`, ignore ability changing effects; Default: `false`
* @returns `true` if the Pokemon's type matches
*/
public isOfType(type: Type, includeTeraType: boolean = true, forDefend: boolean = false, ignoreOverride: boolean = false): boolean {
return this.getTypes(includeTeraType, forDefend, ignoreOverride).some((t) => t === type);
} }
/** /**
* Gets the non-passive ability of the pokemon. This accounts for fusions and ability changing effects. * Gets the non-passive ability of the pokemon. This accounts for fusions and ability changing effects.
* This should rarely be called, most of the time {@link hasAbility} or {@link hasAbilityWithAttr} are better used as * This should rarely be called, most of the time {@linkcode hasAbility} or {@linkcode hasAbilityWithAttr} are better used as
* those check both the passive and non-passive abilities and account for ability suppression. * those check both the passive and non-passive abilities and account for ability suppression.
* @see {@link hasAbility} {@link hasAbilityWithAttr} Intended ways to check abilities in most cases * @see {@linkcode hasAbility} {@linkcode hasAbilityWithAttr} Intended ways to check abilities in most cases
* @param {boolean} ignoreOverride If true, ignore ability changing effects * @param ignoreOverride - If `true`, ignore ability changing effects; Default: `false`
* @returns {Ability} The non-passive ability of the pokemon * @returns The non-passive {@linkcode Ability} of the pokemon
*/ */
getAbility(ignoreOverride?: boolean): Ability { public getAbility(ignoreOverride: boolean = false): Ability {
if (!ignoreOverride && this.summonData?.ability) { if (!ignoreOverride && this.summonData?.ability) {
return allAbilities[this.summonData.ability]; return allAbilities[this.summonData.ability];
} }
@ -1295,12 +1302,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
/** /**
* Gets the passive ability of the pokemon. This should rarely be called, most of the time * Gets the passive ability of the pokemon. This should rarely be called, most of the time
* {@link hasAbility} or {@link hasAbilityWithAttr} are better used as those check both the passive and * {@linkcode hasAbility} or {@linkcode hasAbilityWithAttr} are better used as those check both the passive and
* non-passive abilities and account for ability suppression. * non-passive abilities and account for ability suppression.
* @see {@link hasAbility} {@link hasAbilityWithAttr} Intended ways to check abilities in most cases * @see {@linkcode hasAbility} {@linkcode hasAbilityWithAttr} Intended ways to check abilities in most cases
* @returns {Ability} The passive ability of the pokemon * @returns The passive {@linkcode Ability} of the pokemon
*/ */
getPassiveAbility(): Ability { public getPassiveAbility(): Ability {
if (Overrides.PASSIVE_ABILITY_OVERRIDE && this.isPlayer()) { if (Overrides.PASSIVE_ABILITY_OVERRIDE && this.isPlayer()) {
return allAbilities[Overrides.PASSIVE_ABILITY_OVERRIDE]; return allAbilities[Overrides.PASSIVE_ABILITY_OVERRIDE];
} }
@ -1322,12 +1329,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* Gets a list of all instances of a given ability attribute among abilities this pokemon has. * Gets a list of all instances of a given ability attribute among abilities this pokemon has.
* Accounts for all the various effects which can affect whether an ability will be present or * Accounts for all the various effects which can affect whether an ability will be present or
* in effect, and both passive and non-passive. * in effect, and both passive and non-passive.
* @param attrType {@linkcode AbAttr} The ability attribute to check for. * @param attrType - {@linkcode AbAttr} The ability attribute to check for.
* @param canApply {@linkcode Boolean} If false, it doesn't check whether the ability is currently active * @param canApply - If `false`, it doesn't check whether the ability is currently active; Default `true`
* @param ignoreOverride {@linkcode Boolean} If true, it ignores ability changing effects * @param ignoreOverride - If `true`, it ignores ability changing effects; Default `false`
* @returns A list of all the ability attributes on this ability. * @returns An array of all the ability attributes on this ability.
*/ */
getAbilityAttrs<T extends AbAttr = AbAttr>(attrType: { new(...args: any[]): T }, canApply: boolean = true, ignoreOverride?: boolean): T[] { public getAbilityAttrs<T extends AbAttr = AbAttr>(attrType: { new(...args: any[]): T }, canApply: boolean = true, ignoreOverride: boolean = false): T[] {
const abilityAttrs: T[] = []; const abilityAttrs: T[] = [];
if (!canApply || this.canApplyAbility()) { if (!canApply || this.canApplyAbility()) {
@ -1346,12 +1353,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* - bought with starter candy * - bought with starter candy
* - set by override * - set by override
* - is a boss pokemon * - is a boss pokemon
* @returns whether or not a pokemon should have a passive * @returns `true` if the Pokemon has a passive
*/ */
hasPassive(): boolean { public hasPassive(): boolean {
// returns override if valid for current case // returns override if valid for current case
if ((Overrides.PASSIVE_ABILITY_OVERRIDE !== Abilities.NONE && this.isPlayer()) || if ((Overrides.PASSIVE_ABILITY_OVERRIDE !== Abilities.NONE && this.isPlayer())
(Overrides.OPP_PASSIVE_ABILITY_OVERRIDE !== Abilities.NONE && !this.isPlayer())) { || (Overrides.OPP_PASSIVE_ABILITY_OVERRIDE !== Abilities.NONE && !this.isPlayer())) {
return true; return true;
} }
@ -1370,12 +1377,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
/** /**
* Checks whether an ability of a pokemon can be currently applied. This should rarely be * Checks whether an ability of a pokemon can be currently applied. This should rarely be
* directly called, as {@link hasAbility} and {@link hasAbilityWithAttr} already call this. * directly called, as {@linkcode hasAbility} and {@linkcode hasAbilityWithAttr} already call this.
* @see {@link hasAbility} {@link hasAbilityWithAttr} Intended ways to check abilities in most cases * @see {@linkcode hasAbility} {@linkcode hasAbilityWithAttr} Intended ways to check abilities in most cases
* @param {boolean} passive If true, check if passive can be applied instead of non-passive * @param passive If true, check if passive can be applied instead of non-passive
* @returns {Ability} The passive ability of the pokemon * @returns `true` if the ability can be applied
*/ */
canApplyAbility(passive: boolean = false): boolean { public canApplyAbility(passive: boolean = false): boolean {
if (passive && !this.hasPassive()) { if (passive && !this.hasPassive()) {
return false; return false;
} }
@ -1404,7 +1411,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return false; return false;
} }
} }
return (!!this.hp || ability.isBypassFaint) && !ability.conditions.find(condition => !condition(this)); return (this.hp > 0 || ability.isBypassFaint) && !ability.conditions.find(condition => !condition(this));
} }
/** /**
@ -1416,7 +1423,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* @param {boolean} ignoreOverride If true, it ignores ability changing effects * @param {boolean} ignoreOverride If true, it ignores ability changing effects
* @returns {boolean} Whether the ability is present and active * @returns {boolean} Whether the ability is present and active
*/ */
hasAbility(ability: Abilities, canApply: boolean = true, ignoreOverride?: boolean): boolean { public hasAbility(ability: Abilities, canApply: boolean = true, ignoreOverride?: boolean): boolean {
if ((!canApply || this.canApplyAbility()) && this.getAbility(ignoreOverride).id === ability) { if ((!canApply || this.canApplyAbility()) && this.getAbility(ignoreOverride).id === ability) {
return true; return true;
} }
@ -1436,7 +1443,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* @param {boolean} ignoreOverride If true, it ignores ability changing effects * @param {boolean} ignoreOverride If true, it ignores ability changing effects
* @returns {boolean} Whether an ability with that attribute is present and active * @returns {boolean} Whether an ability with that attribute is present and active
*/ */
hasAbilityWithAttr(attrType: Constructor<AbAttr>, canApply: boolean = true, ignoreOverride?: boolean): boolean { public hasAbilityWithAttr(attrType: Constructor<AbAttr>, canApply: boolean = true, ignoreOverride?: boolean): boolean {
if ((!canApply || this.canApplyAbility()) && this.getAbility(ignoreOverride).hasAttr(attrType)) { if ((!canApply || this.canApplyAbility()) && this.getAbility(ignoreOverride).hasAttr(attrType)) {
return true; return true;
} }
@ -1451,7 +1458,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* and then multiplicative modifiers happening after (Heavy Metal and Light Metal) * and then multiplicative modifiers happening after (Heavy Metal and Light Metal)
* @returns the kg of the Pokemon (minimum of 0.1) * @returns the kg of the Pokemon (minimum of 0.1)
*/ */
getWeight(): number { public getWeight(): number {
const autotomizedTag = this.getTag(AutotomizedTag); const autotomizedTag = this.getTag(AutotomizedTag);
let weightRemoved = 0; let weightRemoved = 0;
if (!Utils.isNullOrUndefined(autotomizedTag)) { if (!Utils.isNullOrUndefined(autotomizedTag)) {
@ -1466,10 +1473,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
/** /**
* Gets the tera-formed type of the pokemon, or UNKNOWN if not present * @returns The tera-formed type of the pokemon, or {@linkcode Type.UNKNOWN} if not present
* @returns the {@linkcode Type}
*/ */
getTeraType(): Type { public getTeraType(): Type {
// this.scene can be undefined for a fainted mon in doubles // this.scene can be undefined for a fainted mon in doubles
if (this.scene !== undefined) { if (this.scene !== undefined) {
const teraModifier = this.scene.findModifier(m => m instanceof TerastallizeModifier const teraModifier = this.scene.findModifier(m => m instanceof TerastallizeModifier
@ -1483,23 +1489,23 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return Type.UNKNOWN; return Type.UNKNOWN;
} }
isTerastallized(): boolean { public isTerastallized(): boolean {
return this.getTeraType() !== Type.UNKNOWN; return this.getTeraType() !== Type.UNKNOWN;
} }
isGrounded(): boolean { public isGrounded(): boolean {
return !!this.getTag(GroundedTag) || (!this.isOfType(Type.FLYING, true, true) && !this.hasAbility(Abilities.LEVITATE) && !this.getTag(BattlerTagType.MAGNET_RISEN) && !this.getTag(SemiInvulnerableTag)); return !!this.getTag(GroundedTag) || (!this.isOfType(Type.FLYING, true, true) && !this.hasAbility(Abilities.LEVITATE) && !this.getTag(BattlerTagType.MAGNET_RISEN) && !this.getTag(SemiInvulnerableTag));
} }
/** /**
* Determines whether this Pokemon is prevented from running or switching due * Determines whether this Pokemon is prevented from running or switching due
* to effects from moves and/or abilities. * to effects from moves and/or abilities.
* @param trappedAbMessages `string[]` If defined, ability trigger messages * @param trappedAbMessages - If defined, ability trigger messages
* (e.g. from Shadow Tag) are forwarded through this array. * (e.g. from Shadow Tag) are forwarded through this array.
* @param simulated `boolean` if `true`, applies abilities via simulated calls. * @param simulated - If `true`, applies abilities via simulated calls.
* @returns * @returns `true` if the pokemon is trapped
*/ */
isTrapped(trappedAbMessages: string[] = [], simulated: boolean = true): boolean { public isTrapped(trappedAbMessages: string[] = [], simulated: boolean = true): boolean {
if (this.isOfType(Type.GHOST)) { if (this.isOfType(Type.GHOST)) {
return false; return false;
} }
@ -1507,7 +1513,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const trappedByAbility = new Utils.BooleanHolder(false); const trappedByAbility = new Utils.BooleanHolder(false);
const opposingField = this.isPlayer() ? this.scene.getEnemyField() : this.scene.getPlayerField(); const opposingField = this.isPlayer() ? this.scene.getEnemyField() : this.scene.getPlayerField();
opposingField.forEach(opponent => opposingField.forEach((opponent) =>
applyCheckTrappedAbAttrs(CheckTrappedAbAttr, opponent, trappedByAbility, this, trappedAbMessages, simulated) applyCheckTrappedAbAttrs(CheckTrappedAbAttr, opponent, trappedByAbility, this, trappedAbMessages, simulated)
); );
@ -1517,11 +1523,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
/** /**
* Calculates the type of a move when used by this Pokemon after * Calculates the type of a move when used by this Pokemon after
* type-changing move and ability attributes have applied. * type-changing move and ability attributes have applied.
* @param move {@linkcode Move} The move being used. * @param move - {@linkcode Move} The move being used.
* @param simulated If `true`, prevents showing abilities applied in this calculation. * @param simulated - If `true`, prevents showing abilities applied in this calculation.
* @returns the {@linkcode Type} of the move after attributes are applied * @returns The {@linkcode Type} of the move after attributes are applied
*/ */
getMoveType(move: Move, simulated: boolean = true): Type { public getMoveType(move: Move, simulated: boolean = true): Type {
const moveTypeHolder = new Utils.NumberHolder(move.type); const moveTypeHolder = new Utils.NumberHolder(move.type);
applyMoveAttrs(VariableMoveTypeAttr, this, null, move, moveTypeHolder); applyMoveAttrs(VariableMoveTypeAttr, this, null, move, moveTypeHolder);
@ -1886,13 +1892,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* Function that tries to set a Pokemon shiny based on seed. * Function that tries to set a Pokemon shiny based on seed.
* For manual use only, usually to roll a Pokemon's shiny chance a second time. * For manual use only, usually to roll a Pokemon's shiny chance a second time.
* *
* The base shiny odds are {@linkcode BASE_SHINY_CHANCE} / 65536 * The base shiny odds are {@linkcode BASE_SHINY_CHANCE} / `65536`
* @param thresholdOverride number that is divided by 2^16 (65536) to get the shiny chance, overrides {@linkcode shinyThreshold} if set (bypassing shiny rate modifiers such as Shiny Charm) * @param thresholdOverride number that is divided by `2^16` (`65536`) to get the shiny chance, overrides {@linkcode shinyThreshold} if set (bypassing shiny rate modifiers such as Shiny Charm)
* @param applyModifiersToOverride If {@linkcode thresholdOverride} is set and this is true, will apply Shiny Charm and event modifiers to {@linkcode thresholdOverride} * @param applyModifiersToOverride If {@linkcode thresholdOverride} is set and this is true, will apply Shiny Charm and event modifiers to {@linkcode thresholdOverride}
* @returns true if the Pokemon has been set as a shiny, false otherwise * @returns `true` if the Pokemon has been set as a shiny, `false` otherwise
*/ */
trySetShinySeed(thresholdOverride?: integer, applyModifiersToOverride?: boolean): boolean { public trySetShinySeed(thresholdOverride?: number, applyModifiersToOverride?: boolean): boolean {
const shinyThreshold = new Utils.IntegerHolder(BASE_SHINY_CHANCE); const shinyThreshold = new Utils.NumberHolder(BASE_SHINY_CHANCE);
if (thresholdOverride === undefined || applyModifiersToOverride) { if (thresholdOverride === undefined || applyModifiersToOverride) {
if (thresholdOverride !== undefined && applyModifiersToOverride) { if (thresholdOverride !== undefined && applyModifiersToOverride) {
shinyThreshold.value = thresholdOverride; shinyThreshold.value = thresholdOverride;
@ -1917,13 +1923,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
/** /**
* Generates a variant * Generates a shiny variant
* Has a 10% of returning 2 (epic variant) * @returns `0-2`, with the following probabilities:
* And a 30% of returning 1 (rare variant) * - Has a 10% chance of returning `2` (epic variant)
* Returns 0 (basic shiny) if there is no variant or 60% of the time otherwise * - Has a 30% chance of returning `1` (rare variant)
* @returns the shiny variant * - Has a 60% chance of returning `0` (basic shiny)
*/ */
generateVariant(): Variant { protected generateShinyVariant(): Variant {
const formIndex: number = this.formIndex; const formIndex: number = this.formIndex;
let variantDataIndex: string | number = this.species.speciesId; let variantDataIndex: string | number = this.species.speciesId;
if (this.species.forms.length > 0) { if (this.species.forms.length > 0) {
@ -1949,8 +1955,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
} }
generateFusionSpecies(forStarter?: boolean): void { public generateFusionSpecies(forStarter?: boolean): void {
const hiddenAbilityChance = new Utils.IntegerHolder(BASE_HIDDEN_ABILITY_CHANCE); const hiddenAbilityChance = new Utils.NumberHolder(BASE_HIDDEN_ABILITY_CHANCE);
if (!this.hasTrainer()) { if (!this.hasTrainer()) {
this.scene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance); this.scene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
} }
@ -1999,7 +2005,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.generateName(); this.generateName();
} }
clearFusionSpecies(): void { public clearFusionSpecies(): void {
this.fusionSpecies = null; this.fusionSpecies = null;
this.fusionFormIndex = 0; this.fusionFormIndex = 0;
this.fusionAbilityIndex = 0; this.fusionAbilityIndex = 0;
@ -2013,12 +2019,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.calculateStats(); this.calculateStats();
} }
generateAndPopulateMoveset(): void { /** Generates a semi-random moveset for a Pokemon */
public generateAndPopulateMoveset(): void {
this.moveset = []; this.moveset = [];
let movePool: [Moves, number][] = []; let movePool: [Moves, number][] = [];
const allLevelMoves = this.getLevelMoves(1, true, true); const allLevelMoves = this.getLevelMoves(1, true, true);
if (!allLevelMoves) { if (!allLevelMoves) {
console.log(this.species.speciesId, "ERROR"); console.warn("Error encountered trying to generate moveset for:", this.species.name);
return; return;
} }
@ -2028,15 +2035,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
break; break;
} }
let weight = levelMove[0]; let weight = levelMove[0];
if (weight === 0) { // Evo Moves // Evolution Moves
if (weight === 0) {
weight = 50; weight = 50;
} }
if (weight === 1 && allMoves[levelMove[1]].power >= 80) { // Assume level 1 moves with 80+ BP are "move reminder" moves and bump their weight // Assume level 1 moves with 80+ BP are "move reminder" moves and bump their weight
if (weight === 1 && allMoves[levelMove[1]].power >= 80) {
weight = 40; weight = 40;
} }
if (allMoves[levelMove[1]].name.endsWith(" (N)")) {
weight /= 100;
} // Unimplemented level up moves are possible to generate, but 1% of their normal chance.
if (!movePool.some(m => m[0] === levelMove[1])) { if (!movePool.some(m => m[0] === levelMove[1])) {
movePool.push([ levelMove[1], weight ]); movePool.push([ levelMove[1], weight ]);
} }
@ -2069,7 +2075,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
} }
if (this.level >= 60) { // No egg moves below level 60 // No egg moves below level 60
if (this.level >= 60) {
for (let i = 0; i < 3; i++) { for (let i = 0; i < 3; i++) {
const moveId = speciesEggMoves[this.species.getRootSpeciesId()][i]; const moveId = speciesEggMoves[this.species.getRootSpeciesId()][i];
if (!movePool.some(m => m[0] === moveId) && !allMoves[moveId].name.endsWith(" (N)")) { if (!movePool.some(m => m[0] === moveId) && !allMoves[moveId].name.endsWith(" (N)")) {
@ -2077,7 +2084,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
} }
const moveId = speciesEggMoves[this.species.getRootSpeciesId()][3]; const moveId = speciesEggMoves[this.species.getRootSpeciesId()][3];
if (this.level >= 170 && !movePool.some(m => m[0] === moveId) && !allMoves[moveId].name.endsWith(" (N)") && !this.isBoss()) { // No rare egg moves before e4 // No rare egg moves before e4
if (this.level >= 170 && !movePool.some(m => m[0] === moveId) && !allMoves[moveId].name.endsWith(" (N)") && !this.isBoss()) {
movePool.push([ moveId, 30 ]); movePool.push([ moveId, 30 ]);
} }
if (this.fusionSpecies) { if (this.fusionSpecies) {
@ -2088,14 +2096,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
} }
const moveId = speciesEggMoves[this.fusionSpecies.getRootSpeciesId()][3]; const moveId = speciesEggMoves[this.fusionSpecies.getRootSpeciesId()][3];
if (this.level >= 170 && !movePool.some(m => m[0] === moveId) && !allMoves[moveId].name.endsWith(" (N)") && !this.isBoss()) {// No rare egg moves before e4 // No rare egg moves before e4
if (this.level >= 170 && !movePool.some(m => m[0] === moveId) && !allMoves[moveId].name.endsWith(" (N)") && !this.isBoss()) {
movePool.push([ moveId, 30 ]); movePool.push([ moveId, 30 ]);
} }
} }
} }
} }
if (this.isBoss()) { // Bosses never get self ko moves // Bosses never get self ko moves
if (this.isBoss()) {
movePool = movePool.filter(m => !allMoves[m[0]].hasAttr(SacrificialAttr)); movePool = movePool.filter(m => !allMoves[m[0]].hasAttr(SacrificialAttr));
} }
movePool = movePool.filter(m => !allMoves[m[0]].hasAttr(SacrificialAttrOnHit)); movePool = movePool.filter(m => !allMoves[m[0]].hasAttr(SacrificialAttrOnHit));
@ -2124,7 +2134,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const statRatio = worseCategory === MoveCategory.PHYSICAL ? atk / spAtk : spAtk / atk; const statRatio = worseCategory === MoveCategory.PHYSICAL ? atk / spAtk : spAtk / atk;
movePool = movePool.map(m => [ m[0], m[1] * (allMoves[m[0]].category === worseCategory ? statRatio : 1) ]); movePool = movePool.map(m => [ m[0], m[1] * (allMoves[m[0]].category === worseCategory ? statRatio : 1) ]);
let weightMultiplier = 0.9; // The higher this is the more the game weights towards higher level moves. At 0 all moves are equal weight. /** The higher this is the more the game weights towards higher level moves. At `0` all moves are equal weight. */
let weightMultiplier = 0.9;
if (this.hasTrainer()) { if (this.hasTrainer()) {
weightMultiplier += 0.7; weightMultiplier += 0.7;
} }
@ -2133,7 +2144,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
const baseWeights: [Moves, number][] = movePool.map(m => [ m[0], Math.ceil(Math.pow(m[1], weightMultiplier) * 100) ]); const baseWeights: [Moves, number][] = movePool.map(m => [ m[0], Math.ceil(Math.pow(m[1], weightMultiplier) * 100) ]);
if (this.hasTrainer() || this.isBoss()) { // Trainers and bosses always force a stab move // Trainers and bosses always force a stab move
if (this.hasTrainer() || this.isBoss()) {
const stabMovePool = baseWeights.filter(m => allMoves[m[0]].category !== MoveCategory.STATUS && this.isOfType(allMoves[m[0]].type)); const stabMovePool = baseWeights.filter(m => allMoves[m[0]].category !== MoveCategory.STATUS && this.isOfType(allMoves[m[0]].type));
if (stabMovePool.length) { if (stabMovePool.length) {
@ -2163,8 +2175,20 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
// Sqrt the weight of any damaging moves with overlapping types. This is about a 0.05 - 0.1 multiplier. // Sqrt the weight of any damaging moves with overlapping types. This is about a 0.05 - 0.1 multiplier.
// Other damaging moves 2x weight if 0-1 damaging moves, 0.5x if 2, 0.125x if 3. These weights double if STAB. // Other damaging moves 2x weight if 0-1 damaging moves, 0.5x if 2, 0.125x if 3. These weights double if STAB.
// Status moves remain unchanged on weight, this encourages 1-2 // Status moves remain unchanged on weight, this encourages 1-2
movePool = baseWeights.filter(m => !this.moveset.some(mo => m[0] === mo?.moveId)).map(m => [ m[0], this.moveset.some(mo => mo?.getMove().category !== MoveCategory.STATUS && mo?.getMove().type === allMoves[m[0]].type) ? Math.ceil(Math.sqrt(m[1])) : allMoves[m[0]].category !== MoveCategory.STATUS ? Math.ceil(m[1] / Math.max(Math.pow(4, this.moveset.filter(mo => (mo?.getMove().power!) > 1).length) / 8, 0.5) * (this.isOfType(allMoves[m[0]].type) ? 2 : 1)) : m[1] ]); // TODO: is this bang correct? const movesetFilter = (m: [Moves, number]) => {
} else { // Non-trainer pokemon just use normal weights let ret: number;
if (this.moveset.some(mo => mo?.getMove().category !== MoveCategory.STATUS && mo?.getMove().type === allMoves[m[0]].type)) {
ret = Math.ceil(Math.sqrt(m[1]));
} else if (allMoves[m[0]].category !== MoveCategory.STATUS) {
ret = Math.ceil(m[1] / Math.max(Math.pow(4, this.moveset.filter(mo => (mo?.getMove().power ?? 0) > 1).length) / 8, 0.5) * (this.isOfType(allMoves[m[0]].type) ? 2 : 1));
} else {
ret = m[1];
}
return ret;
};
movePool = baseWeights.filter(m => !this.moveset.some(mo => m[0] === mo?.moveId)).map(m => [ m[0], movesetFilter(m) ]);
} else {
// Non-trainer pokemon just use normal weights
movePool = baseWeights.filter(m => !this.moveset.some(mo => m[0] === mo?.moveId)); movePool = baseWeights.filter(m => !this.moveset.some(mo => m[0] === mo?.moveId));
} }
const totalWeight = movePool.reduce((v, m) => v + m[1], 0); const totalWeight = movePool.reduce((v, m) => v + m[1], 0);
@ -2179,11 +2203,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.scene.triggerPokemonFormChange(this, SpeciesFormChangeMoveLearnedTrigger); this.scene.triggerPokemonFormChange(this, SpeciesFormChangeMoveLearnedTrigger);
} }
trySelectMove(moveIndex: integer, ignorePp?: boolean): boolean { public trySelectMove(moveIndex: integer, ignorePp?: boolean): boolean {
const move = this.getMoveset().length > moveIndex const move = this.getMoveset().length > moveIndex
? this.getMoveset()[moveIndex] ? this.getMoveset()[moveIndex]
: null; : null;
return move?.isUsable(this, ignorePp)!; // TODO: is this bang correct? return move?.isUsable(this, ignorePp) ?? false;
} }
showInfo(): void { showInfo(): void {
@ -4432,7 +4456,7 @@ export class EnemyPokemon extends Pokemon {
this.initShinySparkle(); this.initShinySparkle();
} }
if (this.shiny) { if (this.shiny) {
this.variant = this.generateVariant(); this.variant = this.generateShinyVariant();
if (Overrides.OPP_VARIANT_OVERRIDE) { if (Overrides.OPP_VARIANT_OVERRIDE) {
this.variant = Overrides.OPP_VARIANT_OVERRIDE; this.variant = Overrides.OPP_VARIANT_OVERRIDE;
} }

View File

@ -1,20 +1,20 @@
import { type PokeballCounts } from "#app/battle-scene";
import { Gender } from "#app/data/gender";
import { Variant } from "#app/data/variant";
import { type ModifierOverride } from "#app/modifier/modifier-type";
import { Unlockables } from "#app/system/unlockables";
import { Abilities } from "#enums/abilities"; import { Abilities } from "#enums/abilities";
import { Biome } from "#enums/biome"; import { Biome } from "#enums/biome";
import { EggTier } from "#enums/egg-type"; import { EggTier } from "#enums/egg-type";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { PokeballType } from "#enums/pokeball"; import { PokeballType } from "#enums/pokeball";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { StatusEffect } from "#enums/status-effect"; import { StatusEffect } from "#enums/status-effect";
import { TimeOfDay } from "#enums/time-of-day"; import { TimeOfDay } from "#enums/time-of-day";
import { VariantTier } from "#enums/variant-tier"; import { VariantTier } from "#enums/variant-tier";
import { WeatherType } from "#enums/weather-type"; import { WeatherType } from "#enums/weather-type";
import { type PokeballCounts } from "./battle-scene";
import { Gender } from "./data/gender";
import { Variant } from "./data/variant";
import { type ModifierOverride } from "./modifier/modifier-type";
import { Unlockables } from "./system/unlockables";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
/** /**
* Overrides that are using when testing different in game situations * Overrides that are using when testing different in game situations

View File

@ -125,18 +125,6 @@ export function randSeedWeightedItem<T>(items: T[]): T {
: Phaser.Math.RND.weightedPick(items); : Phaser.Math.RND.weightedPick(items);
} }
export function randSeedEasedWeightedItem<T>(items: T[], easingFunction: string = "Sine.easeIn"): T | null {
if (!items.length) {
return null;
}
if (items.length === 1) {
return items[0];
}
const value = Phaser.Math.RND.realInRange(0, 1);
const easedValue = Phaser.Tweens.Builders.GetEaseFunction(easingFunction)(value);
return items[Math.floor(easedValue * items.length)];
}
/** /**
* Shuffle a list using the seeded rng. Utilises the Fisher-Yates algorithm. * Shuffle a list using the seeded rng. Utilises the Fisher-Yates algorithm.
* @param {Array} items An array of items. * @param {Array} items An array of items.
@ -384,18 +372,21 @@ export class NumberHolder {
} }
} }
/** @deprecated Use {@linkcode NumberHolder} */
export class IntegerHolder extends NumberHolder { export class IntegerHolder extends NumberHolder {
constructor(value: integer) { constructor(value: integer) {
super(value); super(value);
} }
} }
/** @deprecated Use {@linkcode NumberHolder}*/
export class FixedInt extends IntegerHolder { export class FixedInt extends IntegerHolder {
constructor(value: integer) { constructor(value: integer) {
super(value); super(value);
} }
} }
/** @deprecated */
export function fixedInt(value: integer): integer { export function fixedInt(value: integer): integer {
return new FixedInt(value) as unknown as integer; return new FixedInt(value) as unknown as integer;
} }
@ -478,14 +469,16 @@ export function hslToHex(h: number, s: number, l: number): string {
return `#${f(0)}${f(8)}${f(4)}`; return `#${f(0)}${f(8)}${f(4)}`;
} }
/*This function returns true if the current lang is available for some functions /**
If the lang is not in the function, it usually means that lang is going to use the default english version * This function returns `true` if the current lang is available for some functions.
This function is used in: * If the lang is not in the function, it usually means that lang is going to use the default english version
- summary-ui-handler.ts: If the lang is not available, it'll use types.json (english) *
English itself counts as not available * This function is used in:
* - `summary-ui-handler.ts`: If the lang is not available, it'll use `types.json` (english).
* English itself counts as not available
*/ */
export function verifyLang(lang?: string): boolean { export function verifyLang(lang?: string): boolean {
//IMPORTANT - ONLY ADD YOUR LANG HERE IF YOU'VE ALREADY ADDED ALL THE NECESSARY IMAGES // IMPORTANT - ONLY ADD YOUR LANG HERE IF YOU'VE ALREADY ADDED ALL THE NECESSARY IMAGES
if (!lang) { if (!lang) {
lang = i18next.resolvedLanguage; lang = i18next.resolvedLanguage;
} }
@ -507,7 +500,7 @@ export function verifyLang(lang?: string): boolean {
} }
/** /**
* Prints the type and name of all game objects in a container for debuggin purposes * Prints the type and name of all game objects in a container for debugging purposes
* @param container container with game objects inside it * @param container container with game objects inside it
*/ */
export function printContainerList(container: Phaser.GameObjects.Container): void { export function printContainerList(container: Phaser.GameObjects.Container): void {
@ -582,17 +575,12 @@ export function capitalizeString(str: string, sep: string, lowerFirstChar: boole
return null; return null;
} }
/**
* Returns if an object is null or undefined
* @param object
*/
export function isNullOrUndefined(object: any): object is undefined | null { export function isNullOrUndefined(object: any): object is undefined | null {
return null === object || undefined === object; return null === object || undefined === object;
} }
/** /**
* Capitalizes the first letter of a string * Capitalizes the first letter of a string
* @param str
*/ */
export function capitalizeFirstLetter(str: string) { export function capitalizeFirstLetter(str: string) {
return str.charAt(0).toUpperCase() + str.slice(1); return str.charAt(0).toUpperCase() + str.slice(1);
@ -626,7 +614,7 @@ export function getLocalizedSpriteKey(baseKey: string) {
* @param num the number to check * @param num the number to check
* @param min the minimum value (included) * @param min the minimum value (included)
* @param max the maximum value (included) * @param max the maximum value (included)
* @returns true if number is **inclusive** between min and max * @returns `true` if number is **inclusive** between min and max
*/ */
export function isBetween(num: number, min: number, max: number): boolean { export function isBetween(num: number, min: number, max: number): boolean {
return num >= min && num <= max; return num >= min && num <= max;