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.
This commit is contained in:
Guangcong Luo 2020-11-04 19:21:38 +00:00
parent 68ac96b7da
commit 00ffe38593
10 changed files with 103 additions and 207 deletions

View File

@ -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);
},

View File

@ -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) {

View File

@ -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];

View File

@ -8,30 +8,21 @@ interface AbilityEventMethods {
onStart?: (this: Battle, target: Pokemon) => void;
}
export interface AbilityData extends EffectData, AbilityEventMethods, EventMethods {
export interface AbilityData extends Partial<Ability>, AbilityEventMethods, EventMethods {
name: string;
/** internal index number */
num?: number;
condition?: Partial<ConditionData>;
rating?: number;
isPermanent?: boolean;
isUnbreakable?: boolean;
suppressWeather?: boolean;
}
export type ModdedAbilityData = AbilityData | Partial<AbilityData> & {inherit: true};
export interface Ability extends Readonly<BasicEffect & AbilityData> {
export class Ability extends BasicEffect implements Readonly<BasicEffect> {
readonly effectType: 'Ability';
rating: number;
}
export class DataAbility extends BasicEffect implements Readonly<BasicEffect & AbilityData> {
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<ConditionData>;
readonly isPermanent?: boolean;
readonly isUnbreakable?: boolean;
constructor(data: AnyObject, ...moreData: (AnyObject | null)[]) {
super(data, ...moreData);

View File

@ -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<Condition>, EventMethods {}
export type ModdedConditionData = ConditionData | Partial<ConditionData> & {inherit: true};
export interface Condition extends Readonly<BasicEffect & ConditionData> {
readonly effectType: 'Status' | 'Condition' | 'Weather';
}
export class DataCondition extends BasicEffect implements Readonly<BasicEffect & ConditionData> {
export class Condition extends BasicEffect implements Readonly<BasicEffect & ConditionData> {
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);

View File

@ -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)[]) {

View File

@ -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<Format>, 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<GameTimerSettings>;
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<string>;
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<FormatData, 'name'> & {inherit: true};
export interface Format extends Readonly<BasicEffect & FormatData> {
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<string, string> {
}
}
export class DataFormat extends BasicEffect implements Readonly<BasicEffect & FormatData> {
export class Format extends BasicEffect implements Readonly<BasicEffect> {
readonly mod: string;
/**
* Name of the team generator algorithm, if this format uses
@ -274,6 +202,44 @@ export class DataFormat extends BasicEffect implements Readonly<BasicEffect & Fo
readonly maxForcedLevel?: number;
readonly noLog: boolean;
readonly battle?: ModdedBattleScriptsData;
readonly pokemon?: ModdedBattlePokemon;
readonly queue?: ModdedBattleQueue;
readonly field?: ModdedField;
readonly cannotMega?: string[];
readonly challengeShow?: boolean;
readonly searchShow?: boolean;
readonly threads?: string[];
readonly timer?: Partial<GameTimerSettings>;
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<string>;
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;

View File

@ -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<Item>, EventMethods {
name: string;
/** just controls location on the item spritesheet */
num?: number;
condition?: Partial<ConditionData>;
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<Omit<ItemData, 'name'>> & {
@ -49,12 +17,12 @@ export type ModdedItemData = ItemData | Partial<Omit<ItemData, 'name'>> & {
onCustap?: (this: Battle, pokemon: Pokemon) => void,
};
export interface Item extends Readonly<BasicEffect & ItemData> {
export class Item extends BasicEffect implements Readonly<BasicEffect> {
readonly effectType: 'Item';
}
export class DataItem extends BasicEffect implements Readonly<BasicEffect & ItemData> {
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<BasicEffect & Item
/** Is this item a Pokeball? */
readonly isPokeball: boolean;
readonly condition?: Partial<ConditionData>;
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;

View File

@ -7,7 +7,7 @@ interface SpeciesAbility {
S?: string;
}
export interface SpeciesData {
export interface SpeciesData extends Partial<Species> {
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<Omit<SpeciesData, 'name'>> & {inherit: true};
@ -68,7 +37,7 @@ export interface SpeciesFormatsData {
export type ModdedSpeciesFormatsData = SpeciesFormatsData & {inherit?: true};
export class Species extends BasicEffect implements Readonly<BasicEffect & SpeciesData & SpeciesFormatsData> {
export class Species extends BasicEffect implements Readonly<BasicEffect & SpeciesFormatsData> {
readonly effectType: 'Pokemon';
/**
* Species ID. Identical to ID. Note that this is the full ID, e.g.

View File

@ -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<Ability>;
@ -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);