mirror of
https://github.com/smogon/pokemon-showdown.git
synced 2026-05-18 11:14:39 -05:00
For side conditions, `onStart`/`onRestart`/`onResidual`/`onEnd` have been renamed `onSideStart`/`onSideRestart`/`onSideResidual`/`onSideEnd`, with the `onResidualOrder` properties renamed `onSideResidualOrder`. For field conditions, `onStart`/`onRestart`/`onResidual`/`onEnd` have been renamed `onFieldStart`/`onFieldRestart`/`onFieldResidual`/`onFieldEnd`, with the `onResidualOrder` properties renamed `onFieldResidualOrder`. (The `onField` and `onSide` part helps make it clear to the type system that the first argument is a Field or Side, not a Pokemon.) Side and field conditions can now use `onResidual` to tick separately on each pokemon in Speed order. `onResidualOrder` (the per-pokemon tick) can be timed separate from `onSideResidualOrder` (the per-condition tick), allowing conditions to end at a different priority than they tick per-pokemon. Relatedly, `onTeamPreview` and `onStart` in formats now need to be `onFieldTeamPreview` and `onFieldStart`. Unrelatedly, `effectData` has been renamed `effectState`, and the corresponding state containers (`pokemon.statusData`, `pokemon.speciesData`, `pokemon.itemData`, `pokemon.abilityData`, `field.weatherData`, `field.terrainData`) have been similarly renamed. I renamed the types a while ago, but I was holding off renaming the fields because it would be a breaking change. But this is a breaking change anyway, so we might as well do it now. Note: `onResidual` will tick even on `onSideEnd` turns, although `onSideResidual` won't. When refactoring weather, remember to check `this.state.duration` so you don't deal weather damage on the ending turn. Intended as a better fix for #8216
112 lines
3.0 KiB
TypeScript
112 lines
3.0 KiB
TypeScript
import {PokemonEventMethods} from './dex-conditions';
|
|
import {BasicEffect, toID} from './dex-data';
|
|
|
|
interface AbilityEventMethods {
|
|
onCheckShow?: (this: Battle, pokemon: Pokemon) => void;
|
|
onEnd?: (this: Battle, target: Pokemon & Side & Field) => void;
|
|
onPreStart?: (this: Battle, pokemon: Pokemon) => void;
|
|
onStart?: (this: Battle, target: Pokemon) => void;
|
|
}
|
|
|
|
export interface AbilityData extends Partial<Ability>, AbilityEventMethods, PokemonEventMethods {
|
|
name: string;
|
|
}
|
|
|
|
export type ModdedAbilityData = AbilityData | Partial<AbilityData> & {inherit: true};
|
|
|
|
export class Ability extends BasicEffect implements Readonly<BasicEffect> {
|
|
readonly effectType: 'Ability';
|
|
|
|
/** Rating from -1 Detrimental to +5 Essential; see `data/abilities.ts` for details. */
|
|
readonly rating: number;
|
|
readonly suppressWeather: boolean;
|
|
readonly condition?: ConditionData;
|
|
readonly isPermanent?: boolean;
|
|
readonly isUnbreakable?: boolean;
|
|
|
|
constructor(data: AnyObject) {
|
|
super(data);
|
|
|
|
this.fullname = `ability: ${this.name}`;
|
|
this.effectType = 'Ability';
|
|
this.suppressWeather = !!data.suppressWeather;
|
|
this.rating = data.rating || 0;
|
|
|
|
if (!this.gen) {
|
|
if (this.num >= 234) {
|
|
this.gen = 8;
|
|
} else if (this.num >= 192) {
|
|
this.gen = 7;
|
|
} else if (this.num >= 165) {
|
|
this.gen = 6;
|
|
} else if (this.num >= 124) {
|
|
this.gen = 5;
|
|
} else if (this.num >= 77) {
|
|
this.gen = 4;
|
|
} else if (this.num >= 1) {
|
|
this.gen = 3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export class DexAbilities {
|
|
readonly dex: ModdedDex;
|
|
readonly abilityCache = new Map<ID, Ability>();
|
|
allCache: readonly Ability[] | null = null;
|
|
|
|
constructor(dex: ModdedDex) {
|
|
this.dex = dex;
|
|
}
|
|
|
|
get(name: string | Ability = ''): Ability {
|
|
if (name && typeof name !== 'string') return name;
|
|
|
|
const id = toID(name);
|
|
return this.getByID(id);
|
|
}
|
|
|
|
getByID(id: ID): Ability {
|
|
let ability = this.abilityCache.get(id);
|
|
if (ability) return ability;
|
|
|
|
if (this.dex.data.Aliases.hasOwnProperty(id)) {
|
|
ability = this.get(this.dex.data.Aliases[id]);
|
|
} else if (id && this.dex.data.Abilities.hasOwnProperty(id)) {
|
|
const abilityData = this.dex.data.Abilities[id] as any;
|
|
const abilityTextData = this.dex.getDescs('Abilities', id, abilityData);
|
|
ability = new Ability({
|
|
name: id,
|
|
...abilityData,
|
|
...abilityTextData,
|
|
});
|
|
if (ability.gen > this.dex.gen) {
|
|
(ability as any).isNonstandard = 'Future';
|
|
}
|
|
if (this.dex.currentMod === 'letsgo' && ability.id !== 'noability') {
|
|
(ability as any).isNonstandard = 'Past';
|
|
}
|
|
if ((this.dex.currentMod === 'letsgo' || this.dex.gen <= 2) && ability.id === 'noability') {
|
|
(ability as any).isNonstandard = null;
|
|
}
|
|
} else {
|
|
ability = new Ability({
|
|
id, name: id, exists: false,
|
|
});
|
|
}
|
|
|
|
if (ability.exists) this.abilityCache.set(id, ability);
|
|
return ability;
|
|
}
|
|
|
|
all(): readonly Ability[] {
|
|
if (this.allCache) return this.allCache;
|
|
const abilities = [];
|
|
for (const id in this.dex.data.Abilities) {
|
|
abilities.push(this.getByID(id as ID));
|
|
}
|
|
this.allCache = abilities;
|
|
return this.allCache;
|
|
}
|
|
}
|