unstyled move selector

This commit is contained in:
Jeff Colombo 2019-03-09 14:00:01 -05:00
parent db1b91c40f
commit 7af3fa3382
21 changed files with 467 additions and 28 deletions

2
dist/db/order.json vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -3,7 +3,7 @@ import POGOProtos from 'pogo-protos';
import { ApiService } from 'api/ApiService';
import { IBaseStatsRank, ICombatMoveStats, IPokemon, TypeEffectiveness, TypeOrder } from 'app/models/Pokemon';
import { CombatMoveStats, IBaseStatsRank, IPokemon, TypeEffectiveness, TypeOrder } from 'app/models/Pokemon';
import { LegacyMoves } from 'common/models/LegacyMoves';
@ -20,7 +20,7 @@ export const parseGameMaster = async () => {
const pokemonTypeEffectiveness : Map<POGOProtos.Enums.PokemonType, Map<POGOProtos.Enums.PokemonType, TypeEffectiveness>> = new Map();
const pokemonTypeWeaknesses : Map<POGOProtos.Enums.PokemonType, Map<POGOProtos.Enums.PokemonType, TypeEffectiveness>> = new Map();
const pokemonTypes : Map<POGOProtos.Enums.PokemonType, Array<number>> = new Map();
const combatMoves : Map<POGOProtos.Enums.PokemonMove, ICombatMoveStats> = new Map();
const combatMoves : CombatMoveStats = new Map();
const pokemonBaseStamina : Array<ICalculateRelativeStats> = [];
const pokemonBaseAttack : Array<ICalculateRelativeStats> = [];
@ -141,8 +141,18 @@ export const parseGameMaster = async () => {
// COMBAT MOVES
} else if (entry.template_id.indexOf('COMBAT_V') === 0 && entry.combat_move && entry.combat_move.unique_id) {
const combatMoveId = entry.combat_move.unique_id || POGOProtos.Enums.PokemonMove.MOVE_UNSET;
const formatMoveName = (moveId : string) => {
const fastMove = '_FAST';
const fastMoveIndex = moveId.lastIndexOf(fastMove);
let moveName = moveId;
if (fastMoveIndex + fastMove.length === moveId.length) {
moveName = moveName.substr(0, fastMoveIndex);
}
return moveName.replace(/_/, ' ').toLowerCase();
};
combatMoves.set(combatMoveId, {
id: combatMoveId,
name: formatMoveName(POGOProtos.Enums.PokemonMove[combatMoveId]),
type: entry.combat_move.type || POGOProtos.Enums.PokemonType.POKEMON_TYPE_NONE,
power: entry.combat_move.power || 0,
energyDelta: entry.combat_move.energy_delta || 0,

View File

@ -2,7 +2,7 @@ import POGOProtos from 'pogo-protos';
import { AjaxRequest } from 'api/AjaxRequest';
import { IConfig } from 'app/models/Config';
import { IConfig, IConfigJson } from 'app/models/Config';
import { ILeaguePokemon } from 'app/models/League';
import { IPokemon, TypeEffectiveness } from 'app/models/Pokemon';
@ -28,8 +28,11 @@ export class PokemonService implements IPokemonService {
const queryParameters = {
};
const response : IConfig = await this.AjaxRequest.ajaxGet('/dist/db/config.json', queryParameters);
return response;
const response : IConfigJson = await this.AjaxRequest.ajaxGet('/dist/db/config.json', queryParameters);
return {
...response,
combatMoves: new Map(response.combatMoves),
};
}
public async getPokemonList() {

View File

@ -12,11 +12,12 @@ import { appReducers } from 'app/index';
import * as ActionsPokemonExplorer from 'app/actions';
import * as ActionsPokemonSelectList from 'app/components/PokemonSelectList/actions';
import { IndividualValueKey, IPokemonAppDispatch } from 'app/types';
import { IndividualValueKey, IPokemonAppDispatch, ITypeCoverage } from 'app/types';
import { Footer } from 'app/components/Footer';
import { Header } from 'app/components/Header';
import { LeagueIvExplorer } from 'app/components/LeagueIvExplorer';
import { MovesExplorer } from 'app/components/MovesExplorer';
import { PokemonDisplay } from 'app/components/PokemonDisplay';
import { PokemonSelectList } from 'app/components/PokemonSelectList/PokemonSelectList';
import { TypeEffectiveDisplay } from 'app/components/TypeEffectiveDisplay';
@ -35,6 +36,7 @@ interface IConnectedPokemonAppProps extends PokemonAppProps, IPokemonAppDispatch
export type Navigation = 'pokedex';
type SubNavigation = 'pvp' | 'types' | 'moves';
interface IState {
isInterruption : boolean;
activeNavigation : Navigation | null;
widgets : { [ key in SubNavigation ] : boolean };
}
@ -45,6 +47,7 @@ class PokemonApp extends React.Component<IConnectedPokemonAppProps, IState> {
super(props);
this.state = {
isInterruption: false,
activeNavigation: null,
widgets: {
pvp: true,
@ -98,13 +101,15 @@ class PokemonApp extends React.Component<IConnectedPokemonAppProps, IState> {
league,
individualValues,
leaguePokemon,
combatMoves,
} = this.props.pokemonExplorerState;
const {
isInterruption,
activeNavigation,
widgets,
} = this.state;
const isOverlayShown = activeNavigation === 'pokedex';
const isOverlayShown = isInterruption || activeNavigation === 'pokedex';
const wrapperCss = classNames(
styles.wrapper,
@ -200,6 +205,12 @@ class PokemonApp extends React.Component<IConnectedPokemonAppProps, IState> {
isHighlighted={ isOverlayShown }
/>
}
{ widgets.types && leaguePokemon !== null &&
<TypeEffectiveDisplay
effectiveness={ leaguePokemon.effectiveness }
pokemonName={ leaguePokemon.name }
/>
}
{ widgets.pvp && leaguePokemon !== null &&
<LeagueIvExplorer
activeLeague={ league }
@ -210,13 +221,16 @@ class PokemonApp extends React.Component<IConnectedPokemonAppProps, IState> {
handleChangeLeague={ this.handleChangeLeague }
/>
}
</div>
{ widgets.types && leaguePokemon !== null &&
<TypeEffectiveDisplay
effectiveness={ leaguePokemon.effectiveness }
pokemonName={ leaguePokemon.name }
{ widgets.moves && leaguePokemon !== null &&
<MovesExplorer
movesById={ combatMoves }
quickMoves={ leaguePokemon.moves.quick }
chargeMoves={ leaguePokemon.moves.cinematic }
handleToggleDropdownOpen={ this.handleToggleInterruption }
handleChangeTypeCoverage={ this.handleChangeTypeCoverage }
/>
}
</div>
<div className={ leftNavCss }>
<button className={ pvpButtonCss } onClick={ this.handlePvpClick }><i className={ pvpCss } /></button>
<button className={ badgeButtonCss } onClick={ this.handleTypesClick }><i className={ badgeCss } /></button>
@ -231,6 +245,12 @@ class PokemonApp extends React.Component<IConnectedPokemonAppProps, IState> {
);
}
private readonly handleToggleInterruption = (isInterruption : boolean) => {
this.setState({
isInterruption,
});
}
private readonly handleOverlayClick = () => {
this.setState({
activeNavigation: null,
@ -327,6 +347,10 @@ class PokemonApp extends React.Component<IConnectedPokemonAppProps, IState> {
this.props.dispatch(ActionsPokemonExplorer.maximizeLevel());
}
private readonly handleChangeTypeCoverage = (coverage : ITypeCoverage) => {
this.props.dispatch(ActionsPokemonExplorer.setTypeCoverage(coverage));
}
private readonly handleChangeLeagueNavigation = (league : League) => {
const {
history,

View File

@ -1,16 +1,18 @@
import { action } from 'typesafe-actions';
import { PokemonExplorerActionTypes, ThunkResult } from 'app/types';
import { ITypeCoverage, PokemonExplorerActionTypes, ThunkResult } from 'app/types';
import { calculateMaxLevelForLeague } from 'app/utils/calculator';
import { ILeaguePokemon, League } from 'app/models/League';
import { IMaxStats } from 'app/models/Pokemon';
import { CombatMoveStats, IMaxStats } from 'app/models/Pokemon';
export const setIsLoading = (isLoading : boolean) => action(PokemonExplorerActionTypes.SET_IS_LOADING, { isLoading });
export const setMaxPossibleStats = (maxStats : IMaxStats) => action(PokemonExplorerActionTypes.SET_MAX_STATS, { maxStats });
export const setCombatMoveStats = (combatMoves : CombatMoveStats) => action(PokemonExplorerActionTypes.SET_COMBAT_MOVE_STATS, { combatMoves });
export const setLeaguePokemon = (leaguePokemon : ILeaguePokemon | null) => action(PokemonExplorerActionTypes.SET_LEAGUE_POKEMON, { leaguePokemon });
export const setIvLevel = (level : number | null) => action(PokemonExplorerActionTypes.SET_IV_LEVEL, { level });
@ -23,11 +25,14 @@ export const setIvDef = (ivDef : number | null) => action(PokemonExplorerActionT
export const setActiveLeague = (league : League) => action(PokemonExplorerActionTypes.SET_ACTIVE_LEAGUE, { league });
export const setTypeCoverage = (typeCoverage : ITypeCoverage) => action(PokemonExplorerActionTypes.SET_TYPE_COVERAGE, { typeCoverage });
export const fetchConfig = (
) : ThunkResult<Promise<void>> => {
return async (dispatch, getState, extraArguments) => {
const config = await extraArguments.services.pokemonService.getConfig();
dispatch(setMaxPossibleStats(config.maxPossibleStats));
dispatch(setCombatMoveStats(config.combatMoves));
};
};

View File

@ -176,7 +176,7 @@ export class LeagueIvExplorer extends React.Component<ILeagueIvExplorerProps, IS
);
return (
<React.Fragment>
<div className={ styles.wrapper }>
<LeagueSelector
activeLeague={ activeLeague }
handleLeagueSelect={ handleChangeLeague }
@ -252,7 +252,7 @@ export class LeagueIvExplorer extends React.Component<ILeagueIvExplorerProps, IS
</div>
</div>
</section>
</React.Fragment>
</div>
);
}

View File

@ -23,9 +23,9 @@ export interface ILeagueStatsListProps {
interface IState {
activePokemonId : POGOProtos.Enums.PokemonId;
activePokemonForm : POGOProtos.Enums.Form;
hasSetActiveStats : boolean;
listRef : React.RefObject<FixedSizeList>;
activeIndex : number;
hasSetActiveStats : boolean;
dimensions : {
width : number;
height : number;
@ -96,8 +96,8 @@ export class LeagueStatsList extends React.Component<ILeagueStatsListProps, ISta
this.state = {
activePokemonId: POGOProtos.Enums.PokemonId.MISSINGNO,
activePokemonForm: POGOProtos.Enums.Form.FORM_UNSET,
listRef: React.createRef(),
hasSetActiveStats: false,
listRef: React.createRef(),
activeIndex: -1,
dimensions: {
width: -1,
@ -159,7 +159,7 @@ export class LeagueStatsList extends React.Component<ILeagueStatsListProps, ISta
activeIvs.ivHp === stats.ivHp &&
activeIvs.ivAtk === stats.ivAtk &&
activeIvs.ivDef === stats.ivDef,
highlight: this.state.activeIndex === index,
highlight: this.state.activeIndex === index, // this css class currently does nothing
}
);
const onClick = () => {

View File

@ -0,0 +1,165 @@
import POGOProtos from 'pogo-protos';
import React from 'react';
import { ContentRect, default as Measure } from 'react-measure';
import { FixedSizeList } from 'react-window';
import classNames from 'classnames';
import { CombatMoveStats, IPokemonMove } from 'app/models/Pokemon';
import { TypeIndicator } from './TypeIndicator';
import * as styles from 'app/styles/MovesDropdown.scss';
export interface IMovesDropdownProps {
movesById : CombatMoveStats;
selectedMove : IPokemonMove | null;
options : Array<IPokemonMove>;
handleToggleOpen : (open : boolean) => void;
handleChangeSelectedOption : (option : IPokemonMove) => void;
}
interface IState {
isMenuOpen : boolean;
listRef : React.RefObject<FixedSizeList>;
dimensions : {
width : number;
height : number;
};
}
interface IRowFactory {
index : number;
style : React.CSSProperties;
}
export class MovesDropdown extends React.Component<IMovesDropdownProps, IState> {
constructor(props : IMovesDropdownProps) {
super(props);
this.state = {
isMenuOpen: false,
listRef: React.createRef(),
dimensions: {
width: -1,
height: -1,
}
};
}
public render() {
const {
movesById,
selectedMove,
options,
} = this.props;
const {
isMenuOpen,
} = this.state;
const { width, height } = this.state.dimensions;
const onResize = (contentRect : ContentRect) => {
if (typeof contentRect.bounds !== 'undefined') {
this.setState({ dimensions: contentRect.bounds });
}
};
const wrapperCss = classNames(
'nes-container',
'is-rounded',
'nes-select',
styles.wrapper,
);
let moveName = 'Select a move';
if (selectedMove !== null) {
const moveStats = movesById.get(selectedMove.id);
if (moveStats) {
moveName = moveStats.name;
} else {
moveName = 'UNKNOWN MOVE';
}
}
return (
<React.Fragment>
<div
className={ wrapperCss }
onClick={ this.toggleMenu }
>
{ moveName }
</div>
{ isMenuOpen &&
<div>
<Measure
bounds={ true }
onResize={ onResize }
>
{
({ measureRef }) => (
<div ref={ measureRef }>
<FixedSizeList
ref={ this.state.listRef }
height={ height }
itemCount={ options.length }
itemSize={ 35 }
width={ width }
>
{ this.rowFactory.bind(this) }
</FixedSizeList>
</div>
)
}
</Measure>
</div>
}
</React.Fragment>
);
}
private readonly toggleMenu = () => {
const isMenuOpen = !this.state.isMenuOpen;
this.setState({
isMenuOpen,
});
this.props.handleToggleOpen(isMenuOpen);
}
private rowFactory({ index, style } : IRowFactory) {
const {
movesById,
selectedMove,
options,
} = this.props;
const move = options[index];
const moveStats = movesById.get(move.id);
const css = classNames(
'list-item', // global style
styles.listItem,
{
active: selectedMove && selectedMove.id === move.id,
}
);
const onClick = () => {
this.props.handleChangeSelectedOption(move);
this.toggleMenu();
};
return (
<React.Fragment>
{ moveStats &&
<a
key={ index }
style={ style }
className={ css }
onClick={ onClick }
>
<span>{ moveStats.name }</span>
<TypeIndicator type={ moveStats.type } />
</a>
}
</React.Fragment>
);
}
}

View File

@ -0,0 +1,153 @@
import React from 'react';
import classNames from 'classnames';
import { CombatMoveStats, ICombatMoveStats, IPokemonMove } from 'app/models/Pokemon';
import { ITypeCoverage } from 'app/types';
import { MovesDropdown } from 'app/components/MovesDropdown';
import { TypeIndicator } from './TypeIndicator';
import * as styles from 'app/styles/MovesExplorer.scss';
export interface IMovesExplorerProps {
movesById : CombatMoveStats;
quickMoves : Array<IPokemonMove>;
chargeMoves : Array<IPokemonMove>;
handleToggleDropdownOpen : (isOpen : boolean) => void;
handleChangeTypeCoverage : (typeCoverage : ITypeCoverage) => void;
}
interface IState {
quickMove : IPokemonMove | null;
chargeMove1 : IPokemonMove | null;
chargeMove2 : IPokemonMove | null;
}
export class MovesExplorer extends React.Component<IMovesExplorerProps, IState> {
constructor(props : IMovesExplorerProps) {
super(props);
this.state = {
quickMove: null,
chargeMove1: null,
chargeMove2: null,
};
}
public render() {
const {
movesById,
quickMoves,
chargeMoves,
handleToggleDropdownOpen,
} = this.props;
const {
quickMove,
chargeMove1,
chargeMove2,
} = this.state;
const wrapperCss = classNames(
'nes-container',
styles.wrapper,
);
const quickMoveType = this.getMoveType(quickMove);
const chargeMove1Type = this.getMoveType(chargeMove1);
const chargeMove2Type = this.getMoveType(chargeMove2);
return (
<div className={ wrapperCss }>
<MovesDropdown
movesById={ movesById }
selectedMove={ quickMove }
options={ quickMoves }
handleToggleOpen={ handleToggleDropdownOpen }
handleChangeSelectedOption={ this.handleChangeQuickMove }
/>
{ quickMove && quickMoveType &&
<TypeIndicator type={ quickMoveType } />
}
<MovesDropdown
movesById={ movesById }
selectedMove={ chargeMove1 }
options={ chargeMoves }
handleToggleOpen={ handleToggleDropdownOpen }
handleChangeSelectedOption={ this.handleChangeChargeMove1 }
/>
{ chargeMove1 && chargeMove1Type &&
<TypeIndicator type={ chargeMove1Type } />
}
<MovesDropdown
movesById={ movesById }
selectedMove={ chargeMove2 }
options={ chargeMoves }
handleToggleOpen={ handleToggleDropdownOpen }
handleChangeSelectedOption={ this.handleChangeChargeMove2 }
/>
{ chargeMove2 && chargeMove2Type &&
<TypeIndicator type={ chargeMove2Type } />
}
</div>
);
}
private readonly getMoveType = (move : IPokemonMove | null) => {
let moveStats : ICombatMoveStats | null = null;
if (move !== null) {
moveStats = this.props.movesById.get(move.id) || null;
}
if (moveStats !== null) {
return moveStats.type;
}
return null;
}
private readonly handleChangeQuickMove = (option : IPokemonMove) => {
const {
chargeMove1,
chargeMove2,
} = this.state;
this.setState({
quickMove: option,
});
this.props.handleChangeTypeCoverage({
type1: this.getMoveType(option),
type2: this.getMoveType(chargeMove1),
type3: this.getMoveType(chargeMove2),
});
}
private readonly handleChangeChargeMove1 = (option : IPokemonMove) => {
const {
quickMove,
chargeMove2,
} = this.state;
this.setState({
chargeMove1: option,
});
this.props.handleChangeTypeCoverage({
type1: this.getMoveType(quickMove),
type2: this.getMoveType(option),
type3: this.getMoveType(chargeMove2),
});
}
private readonly handleChangeChargeMove2 = (option : IPokemonMove) => {
const {
quickMove,
chargeMove1,
} = this.state;
this.setState({
chargeMove2: option,
});
this.props.handleChangeTypeCoverage({
type1: this.getMoveType(quickMove),
type2: this.getMoveType(chargeMove1),
type3: this.getMoveType(option),
});
}
}

View File

@ -1,5 +1,13 @@
import { IMaxStats } from 'app/models/Pokemon';
import POGOProtos from 'pogo-protos';
import { CombatMoveStats, ICombatMoveStats, IMaxStats } from 'app/models/Pokemon';
export interface IConfigJson {
maxPossibleStats : IMaxStats;
combatMoves : Array<[POGOProtos.Enums.PokemonMove, ICombatMoveStats]>;
}
export interface IConfig {
maxPossibleStats : IMaxStats;
combatMoves : CombatMoveStats;
}

View File

@ -65,7 +65,7 @@ export interface IPokemonSpecies {
genus : string;
}
interface IPokemonMove {
export interface IPokemonMove {
id : POGOProtos.Enums.PokemonMove;
isLegacy : boolean;
}
@ -103,7 +103,10 @@ export interface IStats {
export interface ICombatMoveStats {
id : POGOProtos.Enums.PokemonMove;
name : string;
type : POGOProtos.Enums.PokemonType;
power : number;
energyDelta : number;
}
export type CombatMoveStats = Map<POGOProtos.Enums.PokemonMove, ICombatMoveStats>;

View File

@ -21,6 +21,12 @@ export const initialState : IPokemonExplorerState = {
ivDef: null,
},
league: League.GREAT,
combatMoves: new Map(),
typeCoverage: {
type1: null,
type2: null,
type3: null,
},
};
const reduceSetIsLoading = (
@ -41,6 +47,14 @@ const reduceSetMaxPossibleStats = (
},
});
const reduceSetCombatMoveStats = (
state : IPokemonExplorerState,
action : ReturnType<typeof Actions.setCombatMoveStats>
) : IPokemonExplorerState => ({
...state,
combatMoves: action.payload.combatMoves,
});
const reduceSetLeaguePokemon = (
state : IPokemonExplorerState,
action : ReturnType<typeof Actions.setLeaguePokemon>
@ -101,6 +115,16 @@ const reduceSetActiveLeague = (
league: action.payload.league
});
const reduceSetTypeCoverage = (
state : IPokemonExplorerState,
action : ReturnType<typeof Actions.setTypeCoverage>
) : IPokemonExplorerState => ({
...state,
typeCoverage: {
...action.payload.typeCoverage
}
});
export const PokemonExplorerReducers : Reducer<IPokemonExplorerState> = (
state : IPokemonExplorerState = initialState,
action,
@ -110,6 +134,8 @@ export const PokemonExplorerReducers : Reducer<IPokemonExplorerState> = (
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_COMBAT_MOVE_STATS:
return reduceSetCombatMoveStats(state, action as ReturnType<typeof Actions.setCombatMoveStats>);
case PokemonExplorerActionTypes.SET_LEAGUE_POKEMON:
return reduceSetLeaguePokemon(state, action as ReturnType<typeof Actions.setLeaguePokemon>);
case PokemonExplorerActionTypes.SET_IV_LEVEL:
@ -122,6 +148,8 @@ export const PokemonExplorerReducers : Reducer<IPokemonExplorerState> = (
return reduceSetIvDef(state, action as ReturnType<typeof Actions.setIvDef>);
case PokemonExplorerActionTypes.SET_ACTIVE_LEAGUE:
return reduceSetActiveLeague(state, action as ReturnType<typeof Actions.setActiveLeague>);
case PokemonExplorerActionTypes.SET_TYPE_COVERAGE:
return reduceSetTypeCoverage(state, action as ReturnType<typeof Actions.setTypeCoverage>);
default:
return state;
}

View File

@ -1,5 +1,10 @@
@import '~styles/Variables.scss';
.wrapper {
display: flex;
flex-flow: column nowrap;
}
.container {
composes: container from './PokemonApp.scss';
margin-bottom: 1rem;

View File

@ -9,3 +9,4 @@ export const leftColumn: string;
export const pokemonInfoWrapper: string;
export const pokemonRankValue: string;
export const rightColumn: string;
export const wrapper: string;

View File

@ -0,0 +1,7 @@
.wrapper {
text-transform: capitalize;
}
.listItem {
text-transform: capitalize;
}

View File

@ -0,0 +1,4 @@
// This file is automatically generated.
// Please do not change this file!
export const listItem: string;
export const wrapper: string;

View File

@ -0,0 +1,3 @@
.wrapper {
font-size: 1em;
}

View File

@ -0,0 +1,3 @@
// This file is automatically generated.
// Please do not change this file!
export const wrapper: string;

View File

@ -77,9 +77,14 @@ $overlay-opacity: 0.7;
.displayWrapper {
margin: 0 auto;
// flex-basis: 30rem;
width: 30rem;
// width: 30rem;
display: flex;
flex-flow: column nowrap;
flex-flow: column wrap;
align-content: center;
& > * {
width: 425px;
}
}
.container {
@ -90,11 +95,11 @@ $overlay-opacity: 0.7;
.rightColumn {
display: flex;
flex-flow: column nowrap;
flex-basis: 45%;
align-items: center;
}
.leftColumn {
width: 45%;
text-align: center;
}

View File

@ -1,3 +1,5 @@
import POGOProtos from 'pogo-protos';
import { Action } from 'redux';
import { ThunkAction, ThunkDispatch } from 'redux-thunk';
@ -6,7 +8,7 @@ import { IProviderExtraArguments } from 'common/models/IProviderExtraArguments';
import { IPokemonSelectListState } from 'app/components/PokemonSelectList/types';
import { ILeaguePokemon, League } from 'app/models/League';
import { IMaxStats } from 'app/models/Pokemon';
import { CombatMoveStats, IMaxStats } from 'app/models/Pokemon';
import { PokemonService } from 'api/PokemonService';
@ -37,21 +39,31 @@ export interface IIndividualValues {
ivDef : number | null;
}
export interface ITypeCoverage {
type1 : POGOProtos.Enums.PokemonType | null;
type2 : POGOProtos.Enums.PokemonType | null;
type3 : POGOProtos.Enums.PokemonType | null;
}
export interface IPokemonExplorerState {
isLoading : boolean;
leaguePokemon : ILeaguePokemon | null;
maxPossibleStats : IMaxStats;
individualValues : IIndividualValues;
league : League;
combatMoves : CombatMoveStats;
typeCoverage : ITypeCoverage;
}
export const PokemonExplorerActionTypes = {
SET_IS_LOADING: 'POKEMON_EXPLORER/SET_IS_LOADING',
SET_MAX_STATS: 'POKEMON_EXPLORER/SET_MAX_STATS',
SET_COMBAT_MOVE_STATS: 'POKEMON_EXPLORER/SET_COMBAT_MOVE_STATS',
SET_LEAGUE_POKEMON: 'POKEMON_EXPLORER/SET_LEAGUE_POKEMON',
SET_IV_LEVEL: 'POKEMON_EXPLORER/SET_IV_LEVEL',
SET_IV_HP: 'POKEMON_EXPLORER/SET_IV_HP',
SET_IV_ATK: 'POKEMON_EXPLORER/SET_IV_ATK',
SET_IV_DEF: 'POKEMON_EXPLORER/SET_IV_DEF',
SET_ACTIVE_LEAGUE: 'POKEMON_EXPLORER/SET_ACTIVE_LEAGUE',
SET_TYPE_COVERAGE: 'POKEMON_EXPLORER/SET_TYPE_COVERAGE',
};