add progress bars to base stats

This commit is contained in:
Jeff Colombo 2019-02-02 16:16:22 -05:00
parent e43c7edbf2
commit 2a12c1860e
12 changed files with 242 additions and 45 deletions

View File

@ -3,7 +3,7 @@ import PokemonDescription from 'pokemongo-json-pokedex/output/locales/en-US/poke
import Pokemon from 'pokemongo-json-pokedex/output/pokemon.json'; import Pokemon from 'pokemongo-json-pokedex/output/pokemon.json';
import { LevelMultipliers } from 'app/models/LevelMultipliers'; import { LevelMultipliers } from 'app/models/LevelMultipliers';
import { Grade, ILeaguePokemon, IPokemon, IStats, League, PokemonId, Type } from 'app/models/Pokemon'; import { Grade, IBaseStatsRank, ILeaguePokemon, IMaxStats, IPokemon, IStats, League, PokemonId, Type } from 'app/models/Pokemon';
type ICpAndTotalFound = Record<number, Array<IStats>>; type ICpAndTotalFound = Record<number, Array<IStats>>;
interface IStatsDistribution { interface IStatsDistribution {
@ -14,6 +14,10 @@ interface IMaxCpByLeague {
great : number; great : number;
ultra : number; ultra : number;
} }
interface ICalculateRelativeStats {
id : string;
value : number;
}
const outPath = './dist/db/'; const outPath = './dist/db/';
@ -22,6 +26,17 @@ const maxCpByLeague : IMaxCpByLeague = {
ultra: 2500 ultra: 2500
}; };
const maxPossibleStats : IMaxStats = {
baseStamina: 0,
baseAttack: 0,
baseDefense: 0,
level: 40,
};
const pokemonOrderById : Record<string, IBaseStatsRank> = {};
const pokemonBaseStamina : Array<ICalculateRelativeStats> = [];
const pokemonBaseAttack : Array<ICalculateRelativeStats> = [];
const pokemonBaseDefense : Array<ICalculateRelativeStats> = [];
const getClosestCpMultiplierIndex = (value : number) => { const getClosestCpMultiplierIndex = (value : number) => {
let i; let i;
for (i = 0; i < LevelMultipliers.length; i++) { for (i = 0; i < LevelMultipliers.length; i++) {
@ -59,7 +74,50 @@ const parseNameAndForm = (monId : string, monName : string) => {
name: monName, name: monName,
form: null, form: null,
}; };
} };
Pokemon.forEach((mon) => {
maxPossibleStats.baseStamina = Math.max(mon.stats.baseStamina, maxPossibleStats.baseStamina);
maxPossibleStats.baseAttack = Math.max(mon.stats.baseAttack, maxPossibleStats.baseAttack);
maxPossibleStats.baseDefense = Math.max(mon.stats.baseDefense, maxPossibleStats.baseDefense);
pokemonBaseStamina.push({
id: mon.id,
value: mon.stats.baseStamina,
});
pokemonBaseAttack.push({
id: mon.id,
value: mon.stats.baseAttack,
});
pokemonBaseDefense.push({
id: mon.id,
value: mon.stats.baseDefense,
});
pokemonOrderById[mon.id] = {
staminaRank: -1,
attackRank: -1,
defenseRank: -1,
};
});
pokemonBaseStamina.sort((a, b) => {
return a.value - b.value;
});
pokemonBaseStamina.forEach((stats, index, array) => {
pokemonOrderById[stats.id].staminaRank = Math.floor((index / (array.length - 1)) * 100);
});
pokemonBaseAttack.sort((a, b) => {
return a.value - b.value;
});
pokemonBaseAttack.forEach((stats, index, array) => {
pokemonOrderById[stats.id].attackRank = Math.floor((index / (array.length - 1)) * 100);
});
pokemonBaseDefense.sort((a, b) => {
return a.value - b.value;
});
pokemonBaseDefense.forEach((stats, index, array) => {
pokemonOrderById[stats.id].defenseRank = Math.floor((index / (array.length - 1)) * 100);
});
Pokemon.forEach((mon) => { Pokemon.forEach((mon) => {
const { name, form } = parseNameAndForm(mon.id, mon.name); const { name, form } = parseNameAndForm(mon.id, mon.name);
const baseAtk = mon.stats.baseAttack; const baseAtk = mon.stats.baseAttack;
@ -77,6 +135,7 @@ Pokemon.forEach((mon) => {
type2: mon.types[1] ? mon.types[1].name.toLowerCase() as Type : null, type2: mon.types[1] ? mon.types[1].name.toLowerCase() as Type : null,
}, },
stats: mon.stats, stats: mon.stats,
statsRank: pokemonOrderById[mon.id],
family: mon.family.id, family: mon.family.id,
pvp: { pvp: {
great: [], great: [],
@ -136,17 +195,17 @@ Pokemon.forEach((mon) => {
const maxCp = maxCpByLeague[league]; const maxCp = maxCpByLeague[league];
const maxLeagueLevelMultiplierIndex = getClosestCpMultiplierIndex(Math.sqrt((maxCp * 10) / cpMultiplier)); const maxLeagueLevelMultiplierIndex = getClosestCpMultiplierIndex(Math.sqrt((maxCp * 10) / cpMultiplier));
const maxLeagueLevelMultiplier = LevelMultipliers[maxLeagueLevelMultiplierIndex]; const maxLeagueLevelMultiplier = LevelMultipliers[maxLeagueLevelMultiplierIndex];
const maxLeagueCp = ~~((cpMultiplier * Math.pow(maxLeagueLevelMultiplier, 2)) / 10); const maxLeagueCp = Math.floor((cpMultiplier * Math.pow(maxLeagueLevelMultiplier, 2)) / 10);
const maxLeagueLevel = (maxLeagueLevelMultiplierIndex + 2) / 2; const maxLeagueLevel = (maxLeagueLevelMultiplierIndex + 2) / 2;
pokemonWithIvs = { pokemonWithIvs = {
cp: maxLeagueCp, cp: maxLeagueCp,
level: maxLeagueLevel, level: maxLeagueLevel,
ivHp: ivHp, ivHp,
ivAtk: ivAtk, ivAtk,
ivDef: ivDef, ivDef,
hp: ~~((baseHp + ivHp) * maxLeagueLevelMultiplier), hp: Math.floor((baseHp + ivHp) * maxLeagueLevelMultiplier),
atk: ~~((baseAtk + ivAtk) * maxLeagueLevelMultiplier), atk: Math.floor((baseAtk + ivAtk) * maxLeagueLevelMultiplier),
def: ~~((baseDef + ivDef) * maxLeagueLevelMultiplier), def: Math.floor((baseDef + ivDef) * maxLeagueLevelMultiplier),
total: 0, total: 0,
speciesGrade: Grade.F, speciesGrade: Grade.F,
metaGrade: Grade.F, metaGrade: Grade.F,
@ -245,3 +304,13 @@ fs.mkdir(outPath, { recursive: true }, () => {
} }
}); });
}); });
// TODO: add moves
fs.mkdir(outPath, { recursive: true }, () => {
fs.writeFile(outPath + 'config.json', JSON.stringify({ maxPossibleStats }), (err) => {
if (err) {
/* tslint:disable-next-line:no-console */
return console.error('order', err);
}
});
});

View File

@ -44,39 +44,40 @@ $main-border-color: $gray-scale-4;
$main-hover-color: darken($main-background-color, 5%); $main-hover-color: darken($main-background-color, 5%);
$normal-primary: #a8a77a; // https://bulbapedia.bulbagarden.net/wiki/Category:Type_color_templates
$normal-primary: #a8a878;
$normal-contrast: #fff; $normal-contrast: #fff;
$fire-primary: #ee8130; $fire-primary: #f08030;
$fire-contrast: #fff; $fire-contrast: #fff;
$fighting-primary: #c22e28; $fighting-primary: #c03028;
$fighting-contrast: #fff; $fighting-contrast: #fff;
$water-primary: #6390f0; $water-primary: #6890f0;
$water-contrast: #fff; $water-contrast: #fff;
$flying-primary: #a98ff3; $flying-primary: #a890f0;
$flying-contrast: #fff; $flying-contrast: #fff;
$grass-primary: #7ac74c; $grass-primary: #78c850;
$grass-contrast: #fff; $grass-contrast: #fff;
$poison-primary: #a33ea1; $poison-primary: #a040a0;
$poison-contrast: #fff; $poison-contrast: #fff;
$electric-primary: #f7d02c; $electric-primary: #f8d030;
$electric-contrast: #fff; $electric-contrast: #fff;
$ground-primary: #e2bf65; $ground-primary: #e0c068;
$ground-contrast: #fff; $ground-contrast: #fff;
$psychic-primary: #f95587; $psychic-primary: #f85888;
$psychic-contrast: #fff; $psychic-contrast: #fff;
$rock-primary: #b6a136; $rock-primary: #b8a038;
$rock-contrast: #fff; $rock-contrast: #fff;
$ice-primary: #96d9d6; $ice-primary: #98d8d8;
$ice-contrast: #fff; $ice-contrast: #fff;
$bug-primary: #a6b91a; $bug-primary: #a8b820;
$bug-contrast: #fff; $bug-contrast: #fff;
$dragon-primary: #6f35fc; $dragon-primary: #6f35fc;
$dragon-contrast: #fff; $dragon-contrast: #fff;
$ghost-primary: #735797; $ghost-primary: #705898;
$ghost-contrast: #fff; $ghost-contrast: #fff;
$dark-primary: #705746; $dark-primary: #705848;
$dark-contrast: #fff; $dark-contrast: #fff;
$steel-primary: #b7b7ce; $steel-primary: #b8b8d0;
$steel-contrast: #fff; $steel-contrast: #fff;
$fairy-primary: #d685ad; $fairy-primary: #ee99ac;
$fairy-contrast: #fff; $fairy-contrast: #fff;

View File

@ -1,5 +1,6 @@
import { AjaxUtils } from 'api/AjaxUtils'; import { AjaxUtils } from 'api/AjaxUtils';
import { IConfig } from 'app/models/Config';
import { ILeaguePokemon, IPokemon, League } from 'app/models/Pokemon'; import { ILeaguePokemon, IPokemon, League } from 'app/models/Pokemon';
interface IPokemonJSON extends IPokemon {} interface IPokemonJSON extends IPokemon {}
@ -74,23 +75,28 @@ export class PokemonService implements IPokemonService {
return pokemonLeagueStats; return pokemonLeagueStats;
} }
public getPokemonList() { public async getConfig() {
const queryParameters = { const queryParameters = {
}; };
return AjaxUtils.ajaxGet('/dist/db/order.json', queryParameters) const response : IConfig = await AjaxUtils.ajaxGet('/dist/db/config.json', queryParameters);
.then((response : Array<IPokemonJSON>) => { // TODO: serialize this
return Promise.resolve(PokemonService.serializePokemonList(response)); return response;
});
} }
public getPokemonLeagueStats(pokemonId : string) { public async getPokemonList() {
const queryParameters = { const queryParameters = {
}; };
return AjaxUtils.ajaxGet(`/dist/db/${ pokemonId }.json`, queryParameters) const response : Array<IPokemonJSON> = await AjaxUtils.ajaxGet('/dist/db/order.json', queryParameters);
.then((response : ILeaguePokemonJSON) => { return PokemonService.serializePokemonList(response);
return Promise.resolve(PokemonService.serializePokemonLeagueStats(response)); }
});
public async getPokemonLeagueStats(pokemonId : string) {
const queryParameters = {
};
const response : ILeaguePokemonJSON = await AjaxUtils.ajaxGet(`/dist/db/${ pokemonId }.json`, queryParameters);
return PokemonService.serializePokemonLeagueStats(response);
} }
} }

View File

@ -25,9 +25,14 @@ class PokemonApp extends React.Component<IConnectedPokemonAppProps> {
super(props); super(props);
} }
public componentWillMount() { public async componentWillMount() {
this.props.dispatch(ActionsPokemonSelectList.fetchPokemonList()) const { dispatch } = this.props;
.then(() => this.props.dispatch(ActionsPokemonSelectList.setIsLoading(false)));
await Promise.all([
dispatch(ActionsPokemonExplorer.fetchConfig()),
dispatch(ActionsPokemonSelectList.fetchPokemonList())
]);
dispatch(ActionsPokemonSelectList.setIsLoading(false));
} }
public render() { public render() {

View File

@ -2,7 +2,7 @@ import React from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { Grade, ILeaguePokemon, IStats } from 'app/models/Pokemon'; import { Grade, ILeaguePokemon, IMaxStats, IStats, } from 'app/models/Pokemon';
import { calculateCp, calculateStatAtLevel } from 'app/utils/calculator'; import { calculateCp, calculateStatAtLevel } from 'app/utils/calculator';
import { formatDexNumber } from 'app/utils/formatter'; import { formatDexNumber } from 'app/utils/formatter';
@ -182,6 +182,28 @@ export class PokemonExplorer extends React.Component<IPokemonExplorerProps, ISta
} }
); );
const progressStaminaCss = classNames('nes-progress', {
'is-success': leaguePokemon.statsRank.staminaRank > 66,
'is-warning': leaguePokemon.statsRank.staminaRank >= 34 && leaguePokemon.statsRank.staminaRank <= 66,
'is-error': leaguePokemon.statsRank.staminaRank < 34,
});
const progressAttackCss = classNames('nes-progress', {
'is-success': leaguePokemon.statsRank.attackRank > 66,
'is-warning': leaguePokemon.statsRank.attackRank >= 34 && leaguePokemon.statsRank.attackRank <= 66,
'is-error': leaguePokemon.statsRank.attackRank < 34,
});
const progressDefenseCss = classNames('nes-progress', {
'is-success': leaguePokemon.statsRank.defenseRank > 66,
'is-warning': leaguePokemon.statsRank.defenseRank >= 34 && leaguePokemon.statsRank.defenseRank <= 66,
'is-error': leaguePokemon.statsRank.defenseRank < 34,
});
const baseStamina : number = leaguePokemon.stats.baseStamina;
const baseAttack : number = leaguePokemon.stats.baseAttack;
const baseDefense : number = leaguePokemon.stats.baseDefense;
let type1 : JSX.Element | null = null; let type1 : JSX.Element | null = null;
if (leaguePokemon.types.type1) { if (leaguePokemon.types.type1) {
type1 = <div className={ `${pokemonType} ${leaguePokemon.types.type1}` }>{ leaguePokemon.types.type1 }</div>; type1 = <div className={ `${pokemonType} ${leaguePokemon.types.type1}` }>{ leaguePokemon.types.type1 }</div>;
@ -212,9 +234,39 @@ export class PokemonExplorer extends React.Component<IPokemonExplorerProps, ISta
<h5>{ leaguePokemon.category }</h5> <h5>{ leaguePokemon.category }</h5>
<section className={ baseStatsCss }> <section className={ baseStatsCss }>
<h3 className={ containerTitleCss }>Base Stats</h3> <h3 className={ containerTitleCss }>Base Stats</h3>
<div>HP &nbsp;{ leaguePokemon.stats.baseStamina }</div> <div className={ styles.baseStatRow }>
<div>ATK { leaguePokemon.stats.baseAttack }</div> <span>HP &nbsp;{ baseStamina < 100 && String.fromCharCode(160) }{ baseStamina }</span>
<div>DEF { leaguePokemon.stats.baseDefense }</div> <progress
className={ progressStaminaCss }
max={ 100 }
value={ leaguePokemon.statsRank.staminaRank }
title={ `${leaguePokemon.statsRank.staminaRank}%` }
>
{ leaguePokemon.statsRank.staminaRank }%
</progress>
</div>
<div className={ styles.baseStatRow }>
<span>ATK { baseAttack < 100 && String.fromCharCode(160) }{ baseAttack }</span>
<progress
className={ progressAttackCss }
max={ 100 }
value={ leaguePokemon.statsRank.attackRank }
title={ `${leaguePokemon.statsRank.attackRank}%` }
>
{ leaguePokemon.statsRank.attackRank }%
</progress>
</div>
<div className={ styles.baseStatRow }>
<span>DEF { baseDefense < 100 && String.fromCharCode(160) }{ baseDefense }</span>
<progress
className={ progressDefenseCss }
max={ 100 }
value={ leaguePokemon.statsRank.defenseRank }
title={ `${leaguePokemon.statsRank.defenseRank}%` }
>
{ leaguePokemon.statsRank.defenseRank }%
</progress>
</div>
</section> </section>
</div> </div>
</div> </div>

View File

@ -5,10 +5,12 @@ import { PokemonExplorerActionTypes } from './types';
import { calculateMaxLevelForLeague } from 'app/utils/calculator'; import { calculateMaxLevelForLeague } from 'app/utils/calculator';
import { ILeaguePokemon } from 'app/models/Pokemon'; import { ILeaguePokemon, IMaxStats } from 'app/models/Pokemon';
export const setIsLoading = (isLoading : boolean) => action(PokemonExplorerActionTypes.SET_IS_LOADING, { isLoading }); export const setIsLoading = (isLoading : boolean) => action(PokemonExplorerActionTypes.SET_IS_LOADING, { isLoading });
export const setMaxPossibleStats = (maxStats : IMaxStats) => action(PokemonExplorerActionTypes.SET_MAX_STATS, { maxStats });
export const setLeaguePokemon = (leaguePokemon : ILeaguePokemon | null) => action(PokemonExplorerActionTypes.SET_LEAGUE_POKEMON, { leaguePokemon }); export const setLeaguePokemon = (leaguePokemon : ILeaguePokemon | null) => action(PokemonExplorerActionTypes.SET_LEAGUE_POKEMON, { leaguePokemon });
export const setIvLevel = (level : number | null) => action(PokemonExplorerActionTypes.SET_IV_LEVEL, { level }); export const setIvLevel = (level : number | null) => action(PokemonExplorerActionTypes.SET_IV_LEVEL, { level });
@ -19,6 +21,14 @@ export const setIvAtk = (atk : number | null) => action(PokemonExplorerActionTyp
export const setIvDef = (def : number | null) => action(PokemonExplorerActionTypes.SET_IV_DEF, { def }); export const setIvDef = (def : number | null) => action(PokemonExplorerActionTypes.SET_IV_DEF, { def });
export const fetchConfig = (
) : ThunkResult<Promise<void>> => {
return async (dispatch, getState, extraArguments) => {
const config = await extraArguments.services.pokemonService.getConfig();
dispatch(setMaxPossibleStats(config.maxPossibleStats));
};
};
export const maximizeLevel = ( export const maximizeLevel = (
) : ThunkResult<Promise<void>> => { ) : ThunkResult<Promise<void>> => {
return async (dispatch, getState, extraArguments) => { return async (dispatch, getState, extraArguments) => {

View File

@ -6,6 +6,12 @@ import { IPokemonExplorerState, PokemonExplorerActionTypes } from './types';
export const initialState : IPokemonExplorerState = { export const initialState : IPokemonExplorerState = {
isLoading: false, isLoading: false,
leaguePokemon: null, leaguePokemon: null,
maxPossibleStats: {
baseStamina: 0,
baseAttack: 0,
baseDefense: 0,
level: 0,
},
individualValues: { individualValues: {
level: null, level: null,
hp: null, hp: null,
@ -23,6 +29,16 @@ const reduceSetIsLoading = (
isLoading: action.payload.isLoading, isLoading: action.payload.isLoading,
}); });
const reduceSetMaxPossibleStats = (
state : IPokemonExplorerState,
action : ReturnType<typeof Actions.setMaxPossibleStats>
) : IPokemonExplorerState => ({
...state,
maxPossibleStats: {
...action.payload.maxStats,
},
});
const reduceSetLeaguePokemon = ( const reduceSetLeaguePokemon = (
state : IPokemonExplorerState, state : IPokemonExplorerState,
action : ReturnType<typeof Actions.setLeaguePokemon> action : ReturnType<typeof Actions.setLeaguePokemon>
@ -82,6 +98,8 @@ export const PokemonExplorerReducers : Reducer<IPokemonExplorerState> = (
switch (action.type) { switch (action.type) {
case PokemonExplorerActionTypes.SET_IS_LOADING: case PokemonExplorerActionTypes.SET_IS_LOADING:
return reduceSetIsLoading(state, action as ReturnType<typeof Actions.setIsLoading>); return reduceSetIsLoading(state, action as ReturnType<typeof Actions.setIsLoading>);
case PokemonExplorerActionTypes.SET_MAX_STATS:
return reduceSetMaxPossibleStats(state, action as ReturnType<typeof Actions.setMaxPossibleStats>);
case PokemonExplorerActionTypes.SET_LEAGUE_POKEMON: case PokemonExplorerActionTypes.SET_LEAGUE_POKEMON:
return reduceSetLeaguePokemon(state, action as ReturnType<typeof Actions.setLeaguePokemon>); return reduceSetLeaguePokemon(state, action as ReturnType<typeof Actions.setLeaguePokemon>);
case PokemonExplorerActionTypes.SET_IV_LEVEL: case PokemonExplorerActionTypes.SET_IV_LEVEL:

View File

@ -235,6 +235,23 @@
align-self: stretch; align-self: stretch;
} }
.baseStatRow {
display: flex;
align-items: center;
& > * {
flex-shrink: 0;
}
& > progress {
flex-shrink: 1;
margin-left: 1em;
width: 5em;
height: 0.5em;
padding: 2px;
}
}
.popkemonIndividualStats { .popkemonIndividualStats {
display: block; display: block;
} }

View File

@ -1,5 +1,6 @@
// This file is automatically generated. // This file is automatically generated.
// Please do not change this file! // Please do not change this file!
export const baseStatRow: string;
export const dexHeader: string; export const dexHeader: string;
export const fieldRow: string; export const fieldRow: string;
export const formHeader: string; export const formHeader: string;

View File

@ -1,4 +1,4 @@
import { ILeaguePokemon, League } from 'app/models/Pokemon'; import { ILeaguePokemon, IMaxStats, League } from 'app/models/Pokemon';
export type IndividualValueKey = 'level' | 'hp' | 'atk' | 'def'; export type IndividualValueKey = 'level' | 'hp' | 'atk' | 'def';
export interface IIndividualValues { export interface IIndividualValues {
@ -11,12 +11,14 @@ export interface IIndividualValues {
export interface IPokemonExplorerState { export interface IPokemonExplorerState {
isLoading : boolean; isLoading : boolean;
leaguePokemon : ILeaguePokemon | null; leaguePokemon : ILeaguePokemon | null;
maxPossibleStats : IMaxStats;
individualValues : IIndividualValues; individualValues : IIndividualValues;
league : League; league : League;
} }
export const PokemonExplorerActionTypes = { export const PokemonExplorerActionTypes = {
SET_IS_LOADING: 'POKEMON_EXPLORER/SET_IS_LOADING', SET_IS_LOADING: 'POKEMON_EXPLORER/SET_IS_LOADING',
SET_MAX_STATS: 'POKEMON_EXPLORER/SET_MAX_STATS',
SET_LEAGUE_POKEMON: 'POKEMON_EXPLORER/SET_LEAGUE_POKEMON', SET_LEAGUE_POKEMON: 'POKEMON_EXPLORER/SET_LEAGUE_POKEMON',
SET_IV_LEVEL: 'POKEMON_EXPLORER/SET_IV_LEVEL', SET_IV_LEVEL: 'POKEMON_EXPLORER/SET_IV_LEVEL',
SET_IV_HP: 'POKEMON_EXPLORER/SET_IV_HP', SET_IV_HP: 'POKEMON_EXPLORER/SET_IV_HP',

View File

@ -0,0 +1,5 @@
import { IMaxStats } from 'app/models/Pokemon';
export interface IConfig {
maxPossibleStats : IMaxStats;
}

View File

@ -13,6 +13,16 @@ export interface IBaseStats {
baseStamina : number; baseStamina : number;
} }
export interface IBaseStatsRank {
attackRank : number;
defenseRank : number;
staminaRank : number;
}
export interface IMaxStats extends IBaseStats {
level : number;
}
export type Type = 'bug' | 'dark' | 'dragon' | 'electric' | 'fairy' | 'fighting' | 'fire' | 'flying' | 'ghost' | 'grass' | 'ground' | 'ice' | 'normal' | 'poison' | 'psychic' | 'rock' | 'steel' | 'water'; export type Type = 'bug' | 'dark' | 'dragon' | 'electric' | 'fairy' | 'fighting' | 'fire' | 'flying' | 'ghost' | 'grass' | 'ground' | 'ice' | 'normal' | 'poison' | 'psychic' | 'rock' | 'steel' | 'water';
export interface IPokemon { export interface IPokemon {
id : PokemonId; id : PokemonId;
@ -26,6 +36,7 @@ export interface IPokemon {
type2 : Type | null; type2 : Type | null;
}; };
stats : IBaseStats; stats : IBaseStats;
statsRank : IBaseStatsRank;
} }
export type League = 'great' | 'ultra'; export type League = 'great' | 'ultra';
export interface ILeaguePokemon extends IPokemon { export interface ILeaguePokemon extends IPokemon {