From 00ffe38593473c2a8d04fd34be387eea813a2f2b Mon Sep 17 00:00:00 2001 From: Guangcong Luo Date: Wed, 4 Nov 2020 19:21:38 +0000 Subject: [PATCH] Inherit ItemData from Item etc Previously, Condition inherited from ConditionData. Now, ConditionData inherits from Condition. The advantage of the new approach is that now, Condition and DataCondition no longer need to be separate types, and there should be much less duplication of type definitions in general. This has also been done for - ItemData/Item/DataItem - AbilityData/Ability/DataAbility - FormatData/Format/DataFormat Species and DataSpecies was already merged, but this also reverses their inheritance (saving a lot of duplicated definitions in the process!) The only one left is MoveData, which is just super complicated and will need its own commit. --- config/formats.ts | 2 +- data/mods/ssb/moves.ts | 3 +- sim/battle.ts | 2 +- sim/dex-abilities.ts | 21 +++----- sim/dex-conditions.ts | 27 ++++------ sim/dex-data.ts | 11 ++-- sim/dex-formats.ts | 114 +++++++++++++++-------------------------- sim/dex-items.ts | 53 ++++++------------- sim/dex-species.ts | 35 +------------ sim/dex.ts | 42 +++++++-------- 10 files changed, 103 insertions(+), 207 deletions(-) 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);