From e43c7edbf270e3c2491f6d76345f4a6c149c125f Mon Sep 17 00:00:00 2001 From: Jeff Colombo Date: Sat, 2 Feb 2019 11:38:34 -0500 Subject: [PATCH] max level button --- generatePokemonData.ts | 6 ++-- src/scss/index.scss | 12 +++++++ src/ts/app/PokemonApp.tsx | 5 +++ .../PokemonExplorer/LeagueStatsList.tsx | 10 ++++-- .../PokemonExplorer/PokemonExplorer.tsx | 17 +++++++-- .../app/components/PokemonExplorer/actions.ts | 35 +++++++++++++++++++ .../components/PokemonExplorer/reducers.ts | 1 + .../styles/PokemonExplorer.scss | 18 +++++++--- .../styles/PokemonExplorer.scss.d.ts | 1 + .../app/components/PokemonExplorer/types.ts | 3 +- .../PokemonSelectList/PokemonSelectList.tsx | 12 +++---- src/ts/app/models/Pokemon.ts | 4 +-- src/ts/app/utils/calculator.ts | 26 ++++++++++++-- 13 files changed, 126 insertions(+), 24 deletions(-) diff --git a/generatePokemonData.ts b/generatePokemonData.ts index 914de21..0e19284 100644 --- a/generatePokemonData.ts +++ b/generatePokemonData.ts @@ -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 { LevelMultipliers } from 'app/models/LevelMultipliers'; -import { Grade, ILeaguePokemon, IPokemon, IStats, League, PokemonIds, Type } from 'app/models/Pokemon'; +import { Grade, ILeaguePokemon, IPokemon, IStats, League, PokemonId, Type } from 'app/models/Pokemon'; type ICpAndTotalFound = Record>; interface IStatsDistribution { @@ -65,9 +65,9 @@ Pokemon.forEach((mon) => { const baseAtk = mon.stats.baseAttack; const baseDef = mon.stats.baseDefense; const baseHp = mon.stats.baseStamina; - const pokemonDescription = typeof PokemonDescription[mon.id as PokemonIds] !== 'undefined' ? PokemonDescription[mon.id as PokemonIds] : {}; + const pokemonDescription = typeof PokemonDescription[mon.id as PokemonId] !== 'undefined' ? PokemonDescription[mon.id as PokemonId] : {}; const pokemon : ILeaguePokemon = { - id: mon.id, + id: mon.id as PokemonId, name: pokemonDescription.name || name || 'MissingNo.', category: pokemonDescription.category || '', form, diff --git a/src/scss/index.scss b/src/scss/index.scss index 5390946..830e711 100644 --- a/src/scss/index.scss +++ b/src/scss/index.scss @@ -36,6 +36,13 @@ body { color: $main-font-primary-color; } +button.nes-btn:disabled, +button.nes-btn:disabled:hover { + background-color: $main-font-secondary-color; + color: $main-hover-color; + box-shadow: none; +} + a.active:not([href]):not([tabindex]) { color: $main-active-font-color; } @@ -89,5 +96,10 @@ a.list-item { .nes-input, .nes-textarea { background-color: $main-background-color; + outline-color: $main-font-secondary-color; box-shadow: 0 4px $main-border-color, 0 -4px $main-border-color, 4px 0 $main-border-color, -4px 0 $main-border-color; + + &::placeholder { + color: $main-font-secondary-color; + } } diff --git a/src/ts/app/PokemonApp.tsx b/src/ts/app/PokemonApp.tsx index bcbb905..b282434 100644 --- a/src/ts/app/PokemonApp.tsx +++ b/src/ts/app/PokemonApp.tsx @@ -58,6 +58,7 @@ class PokemonApp extends React.Component { leaguePokemon={ leaguePokemon } individualValues={ individualValues } handleChangeIndividualValue={ this.handleChangeIndividualValue } + handleMaximizeLevel={ this.handleMaximizeLevel } /> } @@ -108,6 +109,10 @@ class PokemonApp extends React.Component { break; } } + + private readonly handleMaximizeLevel = () => { + this.props.dispatch(ActionsPokemonExplorer.maximizeLevel()); + } } const mapStateToProps = (state : ReturnType) : PokemonAppProps => { diff --git a/src/ts/app/components/PokemonExplorer/LeagueStatsList.tsx b/src/ts/app/components/PokemonExplorer/LeagueStatsList.tsx index bf25be5..6f835bf 100644 --- a/src/ts/app/components/PokemonExplorer/LeagueStatsList.tsx +++ b/src/ts/app/components/PokemonExplorer/LeagueStatsList.tsx @@ -4,12 +4,13 @@ import { FixedSizeList } from 'react-window'; import classNames from 'classnames'; -import { Grade, IStats } from 'app/models/Pokemon'; +import { Grade, IStats, PokemonId } from 'app/models/Pokemon'; import { IIndividualValues } from './types'; import * as styles from './styles/LeagueStatsList.scss'; export interface ILeagueStatsListProps { + activePokemonId : PokemonId; activeIndividualValues : IIndividualValues; leagueStatsList : Array; @@ -48,7 +49,12 @@ export class LeagueStatsList extends React.Component void; + handleMaximizeLevel : () => void; } interface IState { @@ -160,6 +161,10 @@ export class PokemonExplorer extends React.Component - MAX Lv + MAX LEAGUE Lv @@ -290,6 +296,7 @@ export class PokemonExplorer extends React.Component { + this.props.handleMaximizeLevel(); + } + private readonly handleChangeIvFactory = (type : IndividualValueKey) => { return (event : React.ChangeEvent) => { const raw = event.currentTarget.value; diff --git a/src/ts/app/components/PokemonExplorer/actions.ts b/src/ts/app/components/PokemonExplorer/actions.ts index 6d2c004..ce31e8c 100644 --- a/src/ts/app/components/PokemonExplorer/actions.ts +++ b/src/ts/app/components/PokemonExplorer/actions.ts @@ -1,7 +1,10 @@ import { action } from 'typesafe-actions'; +import { ThunkResult } from 'app/types'; import { PokemonExplorerActionTypes } from './types'; +import { calculateMaxLevelForLeague } from 'app/utils/calculator'; + import { ILeaguePokemon } from 'app/models/Pokemon'; export const setIsLoading = (isLoading : boolean) => action(PokemonExplorerActionTypes.SET_IS_LOADING, { isLoading }); @@ -15,3 +18,35 @@ export const setIvHp = (hp : number | null) => action(PokemonExplorerActionTypes export const setIvAtk = (atk : number | null) => action(PokemonExplorerActionTypes.SET_IV_ATK, { atk }); export const setIvDef = (def : number | null) => action(PokemonExplorerActionTypes.SET_IV_DEF, { def }); + +export const maximizeLevel = ( +) : ThunkResult> => { + return async (dispatch, getState, extraArguments) => { + const pokemonExplorerState = getState().pokemonExplorerState; + const { + hp, + atk, + def, + } = pokemonExplorerState.individualValues; + + if (pokemonExplorerState.leaguePokemon !== null) { + const pokemonLeagueValues = pokemonExplorerState.leaguePokemon.pvp[pokemonExplorerState.league]; + const statsSet = pokemonLeagueValues.some((stats) => { + if (((hp === null) || (stats.ivHp === hp)) && + ((atk === null) || (stats.ivAtk === atk)) && + ((def === null) || (stats.ivDef === def)) + ) { + dispatch(setIvHp(stats.ivHp)); + dispatch(setIvAtk(stats.ivAtk)); + dispatch(setIvDef(stats.ivDef)); + dispatch(setIvLevel(stats.level)); + return true; + } + return false; + }); + if (!statsSet && hp !== null && atk !== null && def !== null) { + dispatch(setIvLevel(calculateMaxLevelForLeague(pokemonExplorerState.leaguePokemon.stats, hp, atk, def, pokemonExplorerState.league))); + } + } + }; +}; diff --git a/src/ts/app/components/PokemonExplorer/reducers.ts b/src/ts/app/components/PokemonExplorer/reducers.ts index 5ba31de..6751d75 100644 --- a/src/ts/app/components/PokemonExplorer/reducers.ts +++ b/src/ts/app/components/PokemonExplorer/reducers.ts @@ -12,6 +12,7 @@ export const initialState : IPokemonExplorerState = { atk: null, def: null, }, + league: 'great', }; const reduceSetIsLoading = ( diff --git a/src/ts/app/components/PokemonExplorer/styles/PokemonExplorer.scss b/src/ts/app/components/PokemonExplorer/styles/PokemonExplorer.scss index 5a17bbc..3db0e36 100644 --- a/src/ts/app/components/PokemonExplorer/styles/PokemonExplorer.scss +++ b/src/ts/app/components/PokemonExplorer/styles/PokemonExplorer.scss @@ -244,16 +244,24 @@ } :global(.nes-field.is-inline) .ivInput { - flex-grow: 0; - width: 3.75rem; - padding-left: 0.5rem; - padding-right: 0.5rem; + width: 4.25em; + padding-left: 0.7em; + padding-right: 0.5em; + + &.levelInput { + width: 6.5em; + } } :global(.nes-field.is-inline).fieldRow { - justify-content: space-evenly; + justify-content: space-between; label { flex-grow: 0; + margin-left: 1em; + + &:first-child { + margin-left: 0; + } } } diff --git a/src/ts/app/components/PokemonExplorer/styles/PokemonExplorer.scss.d.ts b/src/ts/app/components/PokemonExplorer/styles/PokemonExplorer.scss.d.ts index 24579e0..887feb6 100644 --- a/src/ts/app/components/PokemonExplorer/styles/PokemonExplorer.scss.d.ts +++ b/src/ts/app/components/PokemonExplorer/styles/PokemonExplorer.scss.d.ts @@ -5,6 +5,7 @@ export const fieldRow: string; export const formHeader: string; export const ivInput: string; export const leaguePokemonRank: string; +export const levelInput: string; export const pokemonBaseStats: string; export const pokemonInfoLeftColumn: string; export const pokemonInfoRightColumn: string; diff --git a/src/ts/app/components/PokemonExplorer/types.ts b/src/ts/app/components/PokemonExplorer/types.ts index dd57165..badaef8 100644 --- a/src/ts/app/components/PokemonExplorer/types.ts +++ b/src/ts/app/components/PokemonExplorer/types.ts @@ -1,4 +1,4 @@ -import { ILeaguePokemon } from 'app/models/Pokemon'; +import { ILeaguePokemon, League } from 'app/models/Pokemon'; export type IndividualValueKey = 'level' | 'hp' | 'atk' | 'def'; export interface IIndividualValues { @@ -12,6 +12,7 @@ export interface IPokemonExplorerState { isLoading : boolean; leaguePokemon : ILeaguePokemon | null; individualValues : IIndividualValues; + league : League; } export const PokemonExplorerActionTypes = { diff --git a/src/ts/app/components/PokemonSelectList/PokemonSelectList.tsx b/src/ts/app/components/PokemonSelectList/PokemonSelectList.tsx index a96c2d6..9074585 100644 --- a/src/ts/app/components/PokemonSelectList/PokemonSelectList.tsx +++ b/src/ts/app/components/PokemonSelectList/PokemonSelectList.tsx @@ -33,7 +33,7 @@ interface IRowFactory { } export class PokemonSelectList extends React.Component { - private listRef : VariableSizeList | null = null; + private listRef : React.RefObject; constructor(props : IPokemonSelectListProps) { super(props); @@ -44,6 +44,8 @@ export class PokemonSelectList extends React.Component (
this.listRef = element; - private readonly getListItemKey = (index : number) => { return index + this.props.pokemonList[index].id; } @@ -166,8 +166,8 @@ export class PokemonSelectList extends React.Component) => { this.props.handleChangeFilter(event.currentTarget.value) .then(() => { - if (this.listRef !== null) { - this.listRef.resetAfterIndex(0, true); + if (this.listRef.current !== null) { + this.listRef.current.resetAfterIndex(0, true); } }); } diff --git a/src/ts/app/models/Pokemon.ts b/src/ts/app/models/Pokemon.ts index 65b5afe..95c78fd 100644 --- a/src/ts/app/models/Pokemon.ts +++ b/src/ts/app/models/Pokemon.ts @@ -15,7 +15,7 @@ export interface IBaseStats { export type Type = 'bug' | 'dark' | 'dragon' | 'electric' | 'fairy' | 'fighting' | 'fire' | 'flying' | 'ghost' | 'grass' | 'ground' | 'ice' | 'normal' | 'poison' | 'psychic' | 'rock' | 'steel' | 'water'; export interface IPokemon { - id : string; + id : PokemonId; name : string; category : string; form : string | null; @@ -49,7 +49,7 @@ export interface IStats { metaGrade : Grade; } -export type PokemonIds = 'BULBASAUR' | 'IVYSAUR' | 'VENUSAUR' | 'CHARMANDER' | +export type PokemonId = 'BULBASAUR' | 'IVYSAUR' | 'VENUSAUR' | 'CHARMANDER' | 'CHARMELEON' | 'CHARIZARD' | 'SQUIRTLE' | 'WARTORTLE' | 'BLASTOISE' | 'CATERPIE' | 'METAPOD' | 'BUTTERFREE' | 'WEEDLE' | 'KAKUNA' | 'BEEDRILL' | 'PIDGEY' | 'PIDGEOTTO' | 'PIDGEOT' | 'RATTATA' | 'RATTATA_ALOLA' | 'RATICATE' | diff --git a/src/ts/app/utils/calculator.ts b/src/ts/app/utils/calculator.ts index 886429c..893f64e 100644 --- a/src/ts/app/utils/calculator.ts +++ b/src/ts/app/utils/calculator.ts @@ -1,8 +1,15 @@ import { LevelMultipliers } from 'app/models/LevelMultipliers'; -import { IBaseStats } from 'app/models/Pokemon'; +import { IBaseStats, League } from 'app/models/Pokemon'; + +const leagueMaxCp = { + great: 1500, + ultra: 1500 +}; + +const calculateStatsFormula = (baseStats : IBaseStats, ivHp : number, ivAtk : number, ivDef : number) => Math.sqrt(baseStats.baseStamina + ivHp) * Math.sqrt(baseStats.baseDefense + ivDef) * (baseStats.baseAttack + ivAtk); export const calculateCp = (baseStats : IBaseStats, level : number, ivHp : number, ivAtk : number, ivDef : number) => { - const statsFormula = Math.sqrt(baseStats.baseStamina + ivHp) * Math.sqrt(baseStats.baseDefense + ivDef) * (baseStats.baseAttack + ivAtk); + const statsFormula = calculateStatsFormula(baseStats, ivHp, ivAtk, ivDef); const levelMultiplier = LevelMultipliers[(level - 1) * 2]; const cp = Math.floor((statsFormula * Math.pow(levelMultiplier, 2)) / 10); return cp; @@ -13,3 +20,18 @@ export const calculateStatAtLevel = (level : number, baseStatValue : number, ivS const calculatedStat = Math.floor((baseStatValue + ivStat) * levelMultiplier); return calculatedStat; }; + +export const calculateMaxLevelForLeague = (baseStats : IBaseStats, ivHp : number, ivAtk : number, ivDef : number, league : League) => { + const maxCp = leagueMaxCp[league]; + const statsFormula = calculateStatsFormula(baseStats, ivHp, ivAtk, ivDef); + let level = 1; + for (let i = LevelMultipliers.length - 1; i >= 0; i--) { + const levelMultiplier = LevelMultipliers[i]; + const cp = Math.floor((statsFormula * Math.pow(levelMultiplier, 2)) / 10); + if (cp <= maxCp) { + level = (i / 2) + 1; + break; + } + } + return level; +};