diff --git a/config/formats.ts b/config/formats.ts index 57e8929be5..3e5dcf73f6 100644 --- a/config/formats.ts +++ b/config/formats.ts @@ -727,7 +727,7 @@ export const Formats: FormatList = [ return { id: move.id, name: move.name, - onStart(pokemon) { + onStart(this: Battle, pokemon: Pokemon) { this.add('-activate', pokemon, 'ability: ' + move.name); this.useMove(move, pokemon); }, diff --git a/data/mods/ssb/moves.ts b/data/mods/ssb/moves.ts index c45cb9da6c..f26197d7b6 100644 --- a/data/mods/ssb/moves.ts +++ b/data/mods/ssb/moves.ts @@ -466,8 +466,7 @@ export const Moves: {[k: string]: ModdedMoveData} = { this.add('replace', target, pokemon.getDetails, target.hp / target.maxhp); // name change target.setAbility(set.ability); - const format = this.format; - if (format.exists && format.onSwitchIn) format.onSwitchIn.call(this, target); + this.singleEvent('SwitchIn', this.format, this.formatData, target); this.add('-message', `${oldName} was sent to the Distortion World and replaced with somebody else!`); let stat: BoostName; for (stat in target.boosts) { diff --git a/sim/battle.ts b/sim/battle.ts index fa77f86e24..8364dafd06 100644 --- a/sim/battle.ts +++ b/sim/battle.ts @@ -77,7 +77,7 @@ export class Battle { readonly deserialized: boolean; readonly strictChoices: boolean; readonly format: Format; - readonly formatData: AnyObject; + readonly formatData: EffectState; readonly gameType: GameType; readonly field: Field; readonly sides: [Side, Side] | [Side, Side, Side, Side]; diff --git a/sim/dex-abilities.ts b/sim/dex-abilities.ts index 4407062c63..ab19fdbca4 100644 --- a/sim/dex-abilities.ts +++ b/sim/dex-abilities.ts @@ -8,30 +8,21 @@ interface AbilityEventMethods { onStart?: (this: Battle, target: Pokemon) => void; } -export interface AbilityData extends EffectData, AbilityEventMethods, EventMethods { +export interface AbilityData extends Partial, AbilityEventMethods, EventMethods { name: string; - /** internal index number */ - num?: number; - condition?: Partial; - rating?: number; - isPermanent?: boolean; - isUnbreakable?: boolean; - suppressWeather?: boolean; } export type ModdedAbilityData = AbilityData | Partial & {inherit: true}; -export interface Ability extends Readonly { +export class Ability extends BasicEffect implements Readonly { readonly effectType: 'Ability'; - rating: number; -} -export class DataAbility extends BasicEffect implements Readonly { - readonly effectType: 'Ability'; - /** Represents how useful or detrimental this ability is. */ + /** Rating from -1 Detrimental to +5 Essential; see `data/abilities.ts` for details. */ readonly rating: number; - /** Whether or not this ability suppresses weather. */ readonly suppressWeather: boolean; + readonly condition?: Partial; + readonly isPermanent?: boolean; + readonly isUnbreakable?: boolean; constructor(data: AnyObject, ...moreData: (AnyObject | null)[]) { super(data, ...moreData); diff --git a/sim/dex-conditions.ts b/sim/dex-conditions.ts index e2b339a40e..a997c7d0e4 100644 --- a/sim/dex-conditions.ts +++ b/sim/dex-conditions.ts @@ -574,28 +574,19 @@ export interface EventMethods { onTypePriority?: number; } -interface ConditionEventMethods { - durationCallback?: (this: Battle, target: Pokemon, source: Pokemon, effect: Effect | null) => number; - onCopy?: (this: Battle, pokemon: Pokemon) => void; - onEnd?: (this: Battle, target: Pokemon & Side & Field) => void; - onRestart?: (this: Battle, target: Pokemon & Side & Field, source: Pokemon, sourceEffect: Effect) => void; - onStart?: (this: Battle, target: Pokemon & Side & Field, source: Pokemon, sourceEffect: Effect) => void; -} - -export interface ConditionData extends EffectData, ConditionEventMethods, EventMethods { - noCopy?: boolean; - affectsFainted?: boolean; - counterMax?: number; -} +export interface ConditionData extends Partial, EventMethods {} export type ModdedConditionData = ConditionData | Partial & {inherit: true}; -export interface Condition extends Readonly { - readonly effectType: 'Status' | 'Condition' | 'Weather'; -} - -export class DataCondition extends BasicEffect implements Readonly { +export class Condition extends BasicEffect implements Readonly { readonly effectType: 'Condition' | 'Weather' | 'Status'; + readonly counterMax?: number; + + readonly durationCallback?: (this: Battle, target: Pokemon, source: Pokemon, effect: Effect | null) => number; + readonly onCopy?: (this: Battle, pokemon: Pokemon) => void; + readonly onEnd?: (this: Battle, target: Pokemon & Side & Field) => void; + readonly onRestart?: (this: Battle, target: Pokemon & Side & Field, source: Pokemon, sourceEffect: Effect) => void; + readonly onStart?: (this: Battle, target: Pokemon & Side & Field, source: Pokemon, sourceEffect: Effect) => void; constructor(data: AnyObject, ...moreData: (AnyObject | null)[]) { super(data, ...moreData); diff --git a/sim/dex-data.ts b/sim/dex-data.ts index 262baf4186..4600fef54e 100644 --- a/sim/dex-data.ts +++ b/sim/dex-data.ts @@ -88,16 +88,17 @@ export class BasicEffect implements EffectData { * glitches (MissingNo etc), Pokestar pokemon, etc. */ isNonstandard: Nonstandard | null; - /** The duration of the effect. */ + /** The duration of the condition - only for pure conditions. */ duration?: number; - /** Whether or not the effect is ignored by Baton Pass. */ + /** Whether or not the condition is ignored by Baton Pass - only for pure conditions. */ noCopy: boolean; - /** Whether or not the effect affects fainted Pokemon. */ + /** Whether or not the condition affects fainted Pokemon. */ affectsFainted: boolean; - /** The status that the effect may cause. */ + /** Moves only: what status does it set? */ status?: ID; - /** The weather that the effect may cause. */ + /** Moves only: what weather does it set? */ weather?: ID; + /** ??? */ sourceEffect: string; constructor(data: AnyObject, ...moreData: (AnyObject | null)[]) { diff --git a/sim/dex-formats.ts b/sim/dex-formats.ts index 6182f0f1ba..5d69b87f78 100644 --- a/sim/dex-formats.ts +++ b/sim/dex-formats.ts @@ -2,85 +2,13 @@ import {Utils} from '../lib/utils'; import {toID, BasicEffect} from './dex-data'; import {EventMethods} from './dex-conditions'; -export interface FormatData extends EventMethods { +export interface FormatData extends Partial, EventMethods { name: string; - - banlist?: string[]; - battle?: ModdedBattleScriptsData; - pokemon?: ModdedBattlePokemon; - queue?: ModdedBattleQueue; - field?: ModdedField; - cannotMega?: string[]; - challengeShow?: boolean; - debug?: boolean; - defaultLevel?: number; - desc?: string; - effectType?: string; - forcedLevel?: number; - gameType?: GameType; - maxForcedLevel?: number; - maxLevel?: number; - mod?: string; - onBasePowerPriority?: number; - onModifyMovePriority?: number; - onModifyTypePriority?: number; - onSwitchInPriority?: number; - rated?: boolean | string; - minSourceGen?: number; - restricted?: string[]; - ruleset?: string[]; - searchShow?: boolean; - team?: string; - teamLength?: {validate?: [number, number], battle?: number}; - threads?: string[]; - timer?: Partial; - tournamentShow?: boolean; - unbanlist?: string[]; - checkLearnset?: ( - this: TeamValidator, move: Move, species: Species, setSources: PokemonSources, set: PokemonSet - ) => {type: string, [any: string]: any} | null; - getEvoFamily?: (this: Format, speciesid: string) => ID; - getSharedPower?: (this: Format, pokemon: Pokemon) => Set; - onAfterMega?: (this: Battle, pokemon: Pokemon) => void; - onBegin?: (this: Battle) => void; - onChangeSet?: ( - this: TeamValidator, set: PokemonSet, format: Format, setHas?: AnyObject, teamHas?: AnyObject - ) => string[] | void; - onModifySpecies?: ( - this: Battle, species: Species, target?: Pokemon, source?: Pokemon, effect?: Effect - ) => Species | void; - onStart?: (this: Battle) => void; - onTeamPreview?: (this: Battle) => void; - onValidateSet?: ( - this: TeamValidator, set: PokemonSet, format: Format, setHas: AnyObject, teamHas: AnyObject - ) => string[] | void; - onValidateTeam?: (this: TeamValidator, team: PokemonSet[], format: Format, teamHas: AnyObject) => string[] | void; - validateSet?: (this: TeamValidator, set: PokemonSet, teamHas: AnyObject) => string[] | null; - validateTeam?: (this: TeamValidator, team: PokemonSet[], options?: { - removeNicknames?: boolean, - skipSets?: {[name: string]: {[key: string]: boolean}}, - }) => string[] | void; - section?: string; - column?: number; } export type FormatList = (FormatData | {section: string, column?: number})[]; export type ModdedFormatData = FormatData | Omit & {inherit: true}; -export interface Format extends Readonly { - readonly effectType: 'Format' | 'Ruleset' | 'Rule' | 'ValidatorRule'; - readonly baseRuleset: string[]; - readonly banlist: string[]; - readonly customRules: string[] | null; - readonly defaultLevel: number; - readonly maxLevel: number; - readonly noLog: boolean; - readonly restricted: string[]; - readonly ruleset: string[]; - readonly unbanlist: string[]; - ruleTable: RuleTable | null; -} - type FormatEffectType = 'Format' | 'Ruleset' | 'Rule' | 'ValidatorRule'; /** rule, source, limit, bans */ @@ -199,7 +127,7 @@ export class RuleTable extends Map { } } -export class DataFormat extends BasicEffect implements Readonly { +export class Format extends BasicEffect implements Readonly { readonly mod: string; /** * Name of the team generator algorithm, if this format uses @@ -274,6 +202,44 @@ export class DataFormat extends BasicEffect implements Readonly; + readonly tournamentShow?: boolean; + readonly checkLearnset?: ( + this: TeamValidator, move: Move, species: Species, setSources: PokemonSources, set: PokemonSet + ) => {type: string, [any: string]: any} | null; + readonly getEvoFamily?: (this: Format, speciesid: string) => ID; + readonly getSharedPower?: (this: Format, pokemon: Pokemon) => Set; + readonly onAfterMega?: (this: Battle, pokemon: Pokemon) => void; + readonly onChangeSet?: ( + this: TeamValidator, set: PokemonSet, format: Format, setHas?: AnyObject, teamHas?: AnyObject + ) => string[] | void; + readonly onModifySpecies?: ( + this: Battle, species: Species, target?: Pokemon, source?: Pokemon, effect?: Effect + ) => Species | void; + readonly onStart?: (this: Battle) => void; + readonly onTeamPreview?: (this: Battle) => void; + readonly onValidateSet?: ( + this: TeamValidator, set: PokemonSet, format: Format, setHas: AnyObject, teamHas: AnyObject + ) => string[] | void; + readonly onValidateTeam?: ( + this: TeamValidator, team: PokemonSet[], format: Format, teamHas: AnyObject + ) => string[] | void; + readonly validateSet?: (this: TeamValidator, set: PokemonSet, teamHas: AnyObject) => string[] | null; + readonly validateTeam?: (this: TeamValidator, team: PokemonSet[], options?: { + removeNicknames?: boolean, + skipSets?: {[name: string]: {[key: string]: boolean}}, + }) => string[] | void; + readonly section?: string; + readonly column?: number; + constructor(data: AnyObject, ...moreData: (AnyObject | null)[]) { super(data, ...moreData); data = this; diff --git a/sim/dex-items.ts b/sim/dex-items.ts index 3abb283b74..b959fa7ea7 100644 --- a/sim/dex-items.ts +++ b/sim/dex-items.ts @@ -8,40 +8,8 @@ interface FlingData { effect?: CommonHandlers['ResultMove']; } -interface ItemEventMethods { - onEat?: ((this: Battle, pokemon: Pokemon) => void) | false; - onPrimal?: (this: Battle, pokemon: Pokemon) => void; - onStart?: (this: Battle, target: Pokemon) => void; - onTakeItem?: ( - (this: Battle, item: Item, pokemon: Pokemon, source: Pokemon, move?: ActiveMove) => boolean | void - ) | boolean; -} - -export interface ItemData extends EffectData, ItemEventMethods, EventMethods { +export interface ItemData extends Partial, EventMethods { name: string; - /** just controls location on the item spritesheet */ - num?: number; - condition?: Partial; - gen: number; - fling?: FlingData; - forcedForme?: string; - ignoreKlutz?: boolean; - isBerry?: boolean; - isChoice?: boolean; - isGem?: boolean; - isPokeball?: boolean; - megaStone?: string; - megaEvolves?: string; - naturalGift?: {basePower: number, type: string}; - onDrive?: string; - onMemory?: string; - onPlate?: string; - spritenum?: number; - zMove?: string | true; - zMoveFrom?: string; - zMoveType?: string; - itemUser?: string[]; - boosts?: SparseBoostsTable | false; } export type ModdedItemData = ItemData | Partial> & { @@ -49,12 +17,12 @@ export type ModdedItemData = ItemData | Partial> & { onCustap?: (this: Battle, pokemon: Pokemon) => void, }; -export interface Item extends Readonly { +export class Item extends BasicEffect implements Readonly { readonly effectType: 'Item'; -} -export class DataItem extends BasicEffect implements Readonly { - readonly effectType: 'Item'; + /** just controls location on the item spritesheet */ + readonly num!: number; + /** * A Move-like object depicting what happens when Fling is used on * this item. @@ -121,6 +89,17 @@ export class DataItem extends BasicEffect implements Readonly; + readonly forcedForme?: string; + readonly isChoice?: boolean; + readonly naturalGift?: {basePower: number, type: string}; + readonly spritenum?: number; + readonly boosts?: SparseBoostsTable | false; + + readonly onEat?: ((this: Battle, pokemon: Pokemon) => void) | false; + readonly onPrimal?: (this: Battle, pokemon: Pokemon) => void; + readonly onStart?: (this: Battle, target: Pokemon) => void; + constructor(data: AnyObject, ...moreData: (AnyObject | null)[]) { super(data, ...moreData); data = this; diff --git a/sim/dex-species.ts b/sim/dex-species.ts index 29e9c311da..02fdec876a 100644 --- a/sim/dex-species.ts +++ b/sim/dex-species.ts @@ -7,7 +7,7 @@ interface SpeciesAbility { S?: string; } -export interface SpeciesData { +export interface SpeciesData extends Partial { name: string; /** National Dex number */ num: number; @@ -17,37 +17,6 @@ export interface SpeciesData { baseStats: StatsTable; eggGroups: string[]; weightkg: number; - color?: string; - heightm?: number; - - canHatch?: boolean; - baseForme?: string; - baseSpecies?: string; - evoLevel?: number; - evoMove?: string; - evoCondition?: string; - evoItem?: string; - evos?: string[]; - evoType?: 'trade' | 'useItem' | 'levelMove' | 'levelExtra' | 'levelFriendship' | 'levelHold' | 'other'; - forme?: string; - gender?: GenderName; - genderRatio?: {[k: string]: number}; - maxHP?: number; - cosmeticFormes?: string[]; - otherFormes?: string[]; - formeOrder?: string[]; - prevo?: string; - gen?: number; - requiredAbility?: string; - requiredItem?: string; - requiredItems?: string[]; - requiredMove?: string; - battleOnly?: string | string[]; - canGigantamax?: string; - cannotDynamax?: boolean; - changesFrom?: string; - maleOnlyHidden?: boolean; - unreleasedHidden?: boolean | 'Past'; } export type ModdedSpeciesData = SpeciesData | Partial> & {inherit: true}; @@ -68,7 +37,7 @@ export interface SpeciesFormatsData { export type ModdedSpeciesFormatsData = SpeciesFormatsData & {inherit?: true}; -export class Species extends BasicEffect implements Readonly { +export class Species extends BasicEffect implements Readonly { readonly effectType: 'Pokemon'; /** * Species ID. Identical to ID. Note that this is the full ID, e.g. diff --git a/sim/dex.ts b/sim/dex.ts index 5c0a524a37..d26f4636a9 100644 --- a/sim/dex.ts +++ b/sim/dex.ts @@ -32,12 +32,12 @@ import * as fs from 'fs'; import * as path from 'path'; import * as Data from './dex-data'; -import {DataCondition} from './dex-conditions'; +import {Condition} from './dex-conditions'; import {DataMove} from './dex-moves'; -import {DataItem} from './dex-items'; -import {DataAbility} from './dex-abilities'; +import {Item} from './dex-items'; +import {Ability} from './dex-abilities'; import {Species} from './dex-species'; -import {DataFormat, RuleTable, mergeFormatLists, ComplexBan, ComplexTeamBan} from './dex-formats'; +import {Format, RuleTable, mergeFormatLists, ComplexBan, ComplexTeamBan} from './dex-formats'; import {PRNG, PRNGSeed} from './prng'; import {Utils} from '../lib/utils'; @@ -73,7 +73,7 @@ const DATA_FILES = { TypeChart: 'typechart', }; -const nullEffect: Condition = new DataCondition({name: '', exists: false}); +const nullEffect: Condition = new Condition({name: '', exists: false}); interface DexTableData { Abilities: DexTable; @@ -101,12 +101,12 @@ export const toID = Data.toID; export class ModdedDex { readonly Data = Data; - readonly Condition = DataCondition; - readonly Ability = DataAbility; - readonly Item = DataItem; + readonly Condition = Condition; + readonly Ability = Ability; + readonly Item = Item; readonly Move = DataMove; readonly Species = Species; - readonly Format = DataFormat; + readonly Format = Format; readonly ModdedDex = ModdedDex; readonly name = "[ModdedDex]"; @@ -542,21 +542,21 @@ export class ModdedDex { let found; if (this.data.Formats.hasOwnProperty(id)) { - effect = new DataFormat({name: id}, this.data.Formats[id]); + effect = new Format({name: id}, this.data.Formats[id]); } else if (this.data.Conditions.hasOwnProperty(id)) { - effect = new DataCondition({name: id}, this.data.Conditions[id]); + effect = new Condition({name: id}, this.data.Conditions[id]); } else if ( (this.data.Moves.hasOwnProperty(id) && (found = this.data.Moves[id]).condition) || (this.data.Abilities.hasOwnProperty(id) && (found = this.data.Abilities[id]).condition) || (this.data.Items.hasOwnProperty(id) && (found = this.data.Items[id]).condition) ) { - effect = new DataCondition({name: found.name || id}, found.condition); + effect = new Condition({name: found.name || id}, found.condition); } else if (id === 'recoil') { - effect = new DataCondition({id, name: 'Recoil', effectType: 'Recoil'}); + effect = new Condition({id, name: 'Recoil', effectType: 'Recoil'}); } else if (id === 'drain') { - effect = new DataCondition({id, name: 'Drain', effectType: 'Drain'}); + effect = new Condition({id, name: 'Drain', effectType: 'Drain'}); } else { - effect = new DataCondition({id, name: id, exists: false}); + effect = new Condition({id, name: id, exists: false}); } this.effectCache.set(id, effect); @@ -617,9 +617,9 @@ export class ModdedDex { } let effect; if (this.data.Formats.hasOwnProperty(id)) { - effect = new DataFormat({name}, this.data.Formats[id], supplementaryAttributes); + effect = new Format({name}, this.data.Formats[id], supplementaryAttributes); } else { - effect = new DataFormat({id, name, exists: false}); + effect = new Format({id, name, exists: false}); } return effect; } @@ -646,7 +646,7 @@ export class ModdedDex { if (id && this.data.Items.hasOwnProperty(id)) { const itemData = this.data.Items[id]; const itemTextData = this.getDescs('Items', id, itemData); - item = new DataItem({name}, itemData, itemTextData); + item = new Item({name}, itemData, itemTextData); if (item.gen > this.gen) { (item as any).isNonstandard = 'Future'; } @@ -655,7 +655,7 @@ export class ModdedDex { (item as any).isNonstandard = 'Past'; } } else { - item = new DataItem({id, name, exists: false}); + item = new Item({id, name, exists: false}); } if (item.exists) this.itemCache.set(id, item); @@ -678,7 +678,7 @@ export class ModdedDex { if (id && this.data.Abilities.hasOwnProperty(id)) { const abilityData = this.data.Abilities[id]; const abilityTextData = this.getDescs('Abilities', id, abilityData); - ability = new DataAbility({name}, abilityData, abilityTextData); + ability = new Ability({name}, abilityData, abilityTextData); if (ability.gen > this.gen) { (ability as any).isNonstandard = 'Future'; } @@ -689,7 +689,7 @@ export class ModdedDex { (ability as any).isNonstandard = null; } } else { - ability = new DataAbility({id, name, exists: false}); + ability = new Ability({id, name, exists: false}); } if (ability.exists) this.abilityCache.set(id, ability);