From 2100315c806d5c201fea2cc2a4a65c55c08b228f Mon Sep 17 00:00:00 2001 From: Jeff Colombo Date: Wed, 13 Mar 2019 01:28:56 -0400 Subject: [PATCH] finish type coverage functionality --- dist/app.css | 112 +++++-- dist/main-bundle.js | 304 +++++++++++++----- src/scss/Variables.scss | 2 + .../PokemonExplorer/MovesDropdown.tsx | 4 +- .../PokemonExplorer/MovesExplorer.tsx | 55 +--- .../PokemonExplorer/PokemonDisplay.tsx | 6 +- .../PokemonExplorer/PokemonExplorer.tsx | 35 +- .../PokemonExplorer/TypeEffectiveDisplay.tsx | 112 +++++-- .../PokemonExplorer/TypeIndicator.tsx | 20 +- .../styles/TypeEffectiveDisplay.scss | 43 ++- .../styles/TypeEffectiveDisplay.scss.d.ts | 8 +- .../PokemonExplorer/styles/TypeIndicator.scss | 5 + src/ts/app/utils/types.ts | 44 +++ 13 files changed, 535 insertions(+), 215 deletions(-) create mode 100644 src/ts/app/utils/types.ts diff --git a/dist/app.css b/dist/app.css index ba6e25c..9720f1c 100644 --- a/dist/app.css +++ b/dist/app.css @@ -267,91 +267,145 @@ background-color: #a8a878; } .TypeIndicator__pokemonType__3MOQI.normal::after { box-shadow: 0 -4px #a8a878, 0 -8px, 4px 0 #a8a878, 4px -4px, 8px 0, 0 4px #a8a878, 0 8px, -4px 0 #a8a878, -4px 4px, -8px 0, -4px -4px, 4px 4px; } + .TypeIndicator__pokemonType__3MOQI.normal.outline { + color: #a8a878; + background-color: #fff; } .TypeIndicator__pokemonType__3MOQI.fighting { color: #fff; background-color: #c03028; } .TypeIndicator__pokemonType__3MOQI.fighting::after { box-shadow: 0 -4px #c03028, 0 -8px, 4px 0 #c03028, 4px -4px, 8px 0, 0 4px #c03028, 0 8px, -4px 0 #c03028, -4px 4px, -8px 0, -4px -4px, 4px 4px; } + .TypeIndicator__pokemonType__3MOQI.fighting.outline { + color: #c03028; + background-color: #fff; } .TypeIndicator__pokemonType__3MOQI.flying { color: #fff; background-color: #a890f0; } .TypeIndicator__pokemonType__3MOQI.flying::after { box-shadow: 0 -4px #a890f0, 0 -8px, 4px 0 #a890f0, 4px -4px, 8px 0, 0 4px #a890f0, 0 8px, -4px 0 #a890f0, -4px 4px, -8px 0, -4px -4px, 4px 4px; } + .TypeIndicator__pokemonType__3MOQI.flying.outline { + color: #a890f0; + background-color: #fff; } .TypeIndicator__pokemonType__3MOQI.poison { color: #fff; background-color: #a040a0; } .TypeIndicator__pokemonType__3MOQI.poison::after { box-shadow: 0 -4px #a040a0, 0 -8px, 4px 0 #a040a0, 4px -4px, 8px 0, 0 4px #a040a0, 0 8px, -4px 0 #a040a0, -4px 4px, -8px 0, -4px -4px, 4px 4px; } + .TypeIndicator__pokemonType__3MOQI.poison.outline { + color: #a040a0; + background-color: #fff; } .TypeIndicator__pokemonType__3MOQI.ground { color: #fff; background-color: #e0c068; } .TypeIndicator__pokemonType__3MOQI.ground::after { box-shadow: 0 -4px #e0c068, 0 -8px, 4px 0 #e0c068, 4px -4px, 8px 0, 0 4px #e0c068, 0 8px, -4px 0 #e0c068, -4px 4px, -8px 0, -4px -4px, 4px 4px; } + .TypeIndicator__pokemonType__3MOQI.ground.outline { + color: #e0c068; + background-color: #fff; } .TypeIndicator__pokemonType__3MOQI.rock { color: #fff; background-color: #b8a038; } .TypeIndicator__pokemonType__3MOQI.rock::after { box-shadow: 0 -4px #b8a038, 0 -8px, 4px 0 #b8a038, 4px -4px, 8px 0, 0 4px #b8a038, 0 8px, -4px 0 #b8a038, -4px 4px, -8px 0, -4px -4px, 4px 4px; } + .TypeIndicator__pokemonType__3MOQI.rock.outline { + color: #b8a038; + background-color: #fff; } .TypeIndicator__pokemonType__3MOQI.bug { color: #fff; background-color: #a8b820; } .TypeIndicator__pokemonType__3MOQI.bug::after { box-shadow: 0 -4px #a8b820, 0 -8px, 4px 0 #a8b820, 4px -4px, 8px 0, 0 4px #a8b820, 0 8px, -4px 0 #a8b820, -4px 4px, -8px 0, -4px -4px, 4px 4px; } + .TypeIndicator__pokemonType__3MOQI.bug.outline { + color: #a8b820; + background-color: #fff; } .TypeIndicator__pokemonType__3MOQI.ghost { color: #fff; background-color: #705898; } .TypeIndicator__pokemonType__3MOQI.ghost::after { box-shadow: 0 -4px #705898, 0 -8px, 4px 0 #705898, 4px -4px, 8px 0, 0 4px #705898, 0 8px, -4px 0 #705898, -4px 4px, -8px 0, -4px -4px, 4px 4px; } + .TypeIndicator__pokemonType__3MOQI.ghost.outline { + color: #705898; + background-color: #fff; } .TypeIndicator__pokemonType__3MOQI.steel { color: #fff; background-color: #b8b8d0; } .TypeIndicator__pokemonType__3MOQI.steel::after { box-shadow: 0 -4px #b8b8d0, 0 -8px, 4px 0 #b8b8d0, 4px -4px, 8px 0, 0 4px #b8b8d0, 0 8px, -4px 0 #b8b8d0, -4px 4px, -8px 0, -4px -4px, 4px 4px; } + .TypeIndicator__pokemonType__3MOQI.steel.outline { + color: #b8b8d0; + background-color: #fff; } .TypeIndicator__pokemonType__3MOQI.fire { color: #fff; background-color: #f08030; } .TypeIndicator__pokemonType__3MOQI.fire::after { box-shadow: 0 -4px #f08030, 0 -8px, 4px 0 #f08030, 4px -4px, 8px 0, 0 4px #f08030, 0 8px, -4px 0 #f08030, -4px 4px, -8px 0, -4px -4px, 4px 4px; } + .TypeIndicator__pokemonType__3MOQI.fire.outline { + color: #f08030; + background-color: #fff; } .TypeIndicator__pokemonType__3MOQI.water { color: #fff; background-color: #6890f0; } .TypeIndicator__pokemonType__3MOQI.water::after { box-shadow: 0 -4px #6890f0, 0 -8px, 4px 0 #6890f0, 4px -4px, 8px 0, 0 4px #6890f0, 0 8px, -4px 0 #6890f0, -4px 4px, -8px 0, -4px -4px, 4px 4px; } + .TypeIndicator__pokemonType__3MOQI.water.outline { + color: #6890f0; + background-color: #fff; } .TypeIndicator__pokemonType__3MOQI.grass { color: #fff; background-color: #78c850; } .TypeIndicator__pokemonType__3MOQI.grass::after { box-shadow: 0 -4px #78c850, 0 -8px, 4px 0 #78c850, 4px -4px, 8px 0, 0 4px #78c850, 0 8px, -4px 0 #78c850, -4px 4px, -8px 0, -4px -4px, 4px 4px; } + .TypeIndicator__pokemonType__3MOQI.grass.outline { + color: #78c850; + background-color: #fff; } .TypeIndicator__pokemonType__3MOQI.electric { color: #fff; background-color: #f8d030; } .TypeIndicator__pokemonType__3MOQI.electric::after { box-shadow: 0 -4px #f8d030, 0 -8px, 4px 0 #f8d030, 4px -4px, 8px 0, 0 4px #f8d030, 0 8px, -4px 0 #f8d030, -4px 4px, -8px 0, -4px -4px, 4px 4px; } + .TypeIndicator__pokemonType__3MOQI.electric.outline { + color: #f8d030; + background-color: #fff; } .TypeIndicator__pokemonType__3MOQI.psychic { color: #fff; background-color: #f85888; } .TypeIndicator__pokemonType__3MOQI.psychic::after { box-shadow: 0 -4px #f85888, 0 -8px, 4px 0 #f85888, 4px -4px, 8px 0, 0 4px #f85888, 0 8px, -4px 0 #f85888, -4px 4px, -8px 0, -4px -4px, 4px 4px; } + .TypeIndicator__pokemonType__3MOQI.psychic.outline { + color: #f85888; + background-color: #fff; } .TypeIndicator__pokemonType__3MOQI.ice { color: #fff; background-color: #98d8d8; } .TypeIndicator__pokemonType__3MOQI.ice::after { box-shadow: 0 -4px #98d8d8, 0 -8px, 4px 0 #98d8d8, 4px -4px, 8px 0, 0 4px #98d8d8, 0 8px, -4px 0 #98d8d8, -4px 4px, -8px 0, -4px -4px, 4px 4px; } + .TypeIndicator__pokemonType__3MOQI.ice.outline { + color: #98d8d8; + background-color: #fff; } .TypeIndicator__pokemonType__3MOQI.dragon { color: #fff; background-color: #6f35fc; } .TypeIndicator__pokemonType__3MOQI.dragon::after { box-shadow: 0 -4px #6f35fc, 0 -8px, 4px 0 #6f35fc, 4px -4px, 8px 0, 0 4px #6f35fc, 0 8px, -4px 0 #6f35fc, -4px 4px, -8px 0, -4px -4px, 4px 4px; } + .TypeIndicator__pokemonType__3MOQI.dragon.outline { + color: #6f35fc; + background-color: #fff; } .TypeIndicator__pokemonType__3MOQI.dark { color: #fff; background-color: #705848; } .TypeIndicator__pokemonType__3MOQI.dark::after { box-shadow: 0 -4px #705848, 0 -8px, 4px 0 #705848, 4px -4px, 8px 0, 0 4px #705848, 0 8px, -4px 0 #705848, -4px 4px, -8px 0, -4px -4px, 4px 4px; } + .TypeIndicator__pokemonType__3MOQI.dark.outline { + color: #705848; + background-color: #fff; } .TypeIndicator__pokemonType__3MOQI.fairy { color: #fff; background-color: #ee99ac; } .TypeIndicator__pokemonType__3MOQI.fairy::after { box-shadow: 0 -4px #ee99ac, 0 -8px, 4px 0 #ee99ac, 4px -4px, 8px 0, 0 4px #ee99ac, 0 8px, -4px 0 #ee99ac, -4px 4px, -8px 0, -4px -4px, 4px 4px; } + .TypeIndicator__pokemonType__3MOQI.fairy.outline { + color: #ee99ac; + background-color: #fff; } .MovesExplorer__wrapper__2y-BK { font-size: 1em; } @@ -396,51 +450,50 @@ .MovesDropdown__legacy__3-s2n { } -.TypeEffectiveDisplay__multiplierWrapper__14os7 { +.TypeEffectiveDisplay__typeWrapper__3w4wT, +.TypeEffectiveDisplay__typeMultiplierWrapper__1WOCi { position: relative; - z-index: 0; + z-index: 0; } + +.TypeEffectiveDisplay__typeMultiplierWrapper__1WOCi { padding-top: 6px; padding-right: 6px; } - .TypeEffectiveDisplay__multiplierWrapper__14os7 > * { + .TypeEffectiveDisplay__typeMultiplierWrapper__1WOCi > * { position: absolute; } - .TypeEffectiveDisplay__multiplierWrapper__14os7 :nth-child(1) { + .TypeEffectiveDisplay__typeMultiplierWrapper__1WOCi :nth-child(1) { position: inherit; z-index: 3; } - .TypeEffectiveDisplay__multiplierWrapper__14os7 :nth-child(2) { + .TypeEffectiveDisplay__typeMultiplierWrapper__1WOCi :nth-child(2) { top: 0; left: 6px; z-index: 2; } - .TypeEffectiveDisplay__multiplierWrapper__14os7 :nth-child(3) { + .TypeEffectiveDisplay__typeMultiplierWrapper__1WOCi :nth-child(3) { left: 12px; z-index: 1; } - .TypeEffectiveDisplay__multiplierWrapper__14os7 :nth-child(4) { + .TypeEffectiveDisplay__typeMultiplierWrapper__1WOCi :nth-child(4) { left: 18px; z-index: 0; } - .TypeEffectiveDisplay__multiplierWrapper__14os7.TypeEffectiveDisplay__multiplierWrapperX3__1SBG1 { + .TypeEffectiveDisplay__typeMultiplierWrapper__1WOCi.TypeEffectiveDisplay__typeMultiplierWrapperX3__35-RS { padding-top: 12px; padding-right: 12px; } - .TypeEffectiveDisplay__multiplierWrapper__14os7.TypeEffectiveDisplay__multiplierWrapperX3__1SBG1 :nth-child(3) { + .TypeEffectiveDisplay__typeMultiplierWrapper__1WOCi.TypeEffectiveDisplay__typeMultiplierWrapperX3__35-RS :nth-child(3) { top: 0; } - .TypeEffectiveDisplay__multiplierWrapper__14os7.TypeEffectiveDisplay__multiplierWrapperX3__1SBG1 :nth-child(2) { + .TypeEffectiveDisplay__typeMultiplierWrapper__1WOCi.TypeEffectiveDisplay__typeMultiplierWrapperX3__35-RS :nth-child(2) { top: 6px; } - .TypeEffectiveDisplay__multiplierWrapper__14os7.TypeEffectiveDisplay__multiplierWrapperX4__2KqYa { + .TypeEffectiveDisplay__typeMultiplierWrapper__1WOCi.TypeEffectiveDisplay__typeMultiplierWrapperX4__2Onf7 { padding-top: 18px; padding-right: 18px; } - .TypeEffectiveDisplay__multiplierWrapper__14os7.TypeEffectiveDisplay__multiplierWrapperX4__2KqYa :nth-child(4) { + .TypeEffectiveDisplay__typeMultiplierWrapper__1WOCi.TypeEffectiveDisplay__typeMultiplierWrapperX4__2Onf7 :nth-child(4) { top: 0; } - .TypeEffectiveDisplay__multiplierWrapper__14os7.TypeEffectiveDisplay__multiplierWrapperX4__2KqYa :nth-child(3) { + .TypeEffectiveDisplay__typeMultiplierWrapper__1WOCi.TypeEffectiveDisplay__typeMultiplierWrapperX4__2Onf7 :nth-child(3) { top: 6px; } - .TypeEffectiveDisplay__multiplierWrapper__14os7.TypeEffectiveDisplay__multiplierWrapperX4__2KqYa :nth-child(2) { + .TypeEffectiveDisplay__typeMultiplierWrapper__1WOCi.TypeEffectiveDisplay__typeMultiplierWrapperX4__2Onf7 :nth-child(2) { top: 12px; } .TypeEffectiveDisplay__wrapper__1FFIj { margin: 0 1em; } .TypeEffectiveDisplay__wrapper__1FFIj * + h4 { margin-top: 1em; } - .TypeEffectiveDisplay__wrapper__1FFIj h4 p { - font-size: 0.7em; - color: #b6b6b6; - margin: 0; } .TypeEffectiveDisplay__wrapper__1FFIj .TypeEffectiveDisplay__indicatorWrapper__2F3AY { display: flex; flex-flow: row wrap; @@ -448,18 +501,31 @@ .TypeEffectiveDisplay__wrapper__1FFIj .TypeEffectiveDisplay__indicatorWrapper__2F3AY > * { margin-right: 14px; height: 0%; } - .TypeEffectiveDisplay__wrapper__1FFIj .TypeEffectiveDisplay__indicatorWrapper__2F3AY .TypeEffectiveDisplay__multiplierWrapper__14os7 { + .TypeEffectiveDisplay__wrapper__1FFIj .TypeEffectiveDisplay__indicatorWrapper__2F3AY .TypeEffectiveDisplay__typeMultiplierWrapper__1WOCi { width: auto; margin-right: 2px; } - .TypeEffectiveDisplay__wrapper__1FFIj .TypeEffectiveDisplay__indicatorWrapper__2F3AY .TypeEffectiveDisplay__multiplierWrapper__14os7.TypeEffectiveDisplay__multiplierWrapperX3__1SBG1 { + .TypeEffectiveDisplay__wrapper__1FFIj .TypeEffectiveDisplay__indicatorWrapper__2F3AY .TypeEffectiveDisplay__typeMultiplierWrapper__1WOCi.TypeEffectiveDisplay__typeMultiplierWrapperX3__35-RS { margin-right: -4px; } - .TypeEffectiveDisplay__wrapper__1FFIj .TypeEffectiveDisplay__indicatorWrapper__2F3AY .TypeEffectiveDisplay__multiplierWrapper__14os7.TypeEffectiveDisplay__multiplierWrapperX4__2KqYa { + .TypeEffectiveDisplay__wrapper__1FFIj .TypeEffectiveDisplay__indicatorWrapper__2F3AY .TypeEffectiveDisplay__typeMultiplierWrapper__1WOCi.TypeEffectiveDisplay__typeMultiplierWrapperX4__2Onf7 { margin-right: -10px; } - .TypeEffectiveDisplay__wrapper__1FFIj .TypeEffectiveDisplay__indicatorWrapper__2F3AY > *, - .TypeEffectiveDisplay__wrapper__1FFIj .TypeEffectiveDisplay__indicatorWrapper__2F3AY .TypeEffectiveDisplay__multiplierWrapper__14os7 > * { + .TypeEffectiveDisplay__wrapper__1FFIj .TypeEffectiveDisplay__indicatorWrapper__2F3AY .TypeEffectiveDisplay__typeWrapper__3w4wT > *, + .TypeEffectiveDisplay__wrapper__1FFIj .TypeEffectiveDisplay__indicatorWrapper__2F3AY .TypeEffectiveDisplay__typeMultiplierWrapper__1WOCi > * { flex-basis: unset; width: 6.75rem; } +.TypeEffectiveDisplay__isCovered__3qyUq { + position: relative; } + .TypeEffectiveDisplay__isCovered__3qyUq::before { + content: ""; + position: absolute; + right: 25px; + bottom: 20px; + z-index: 10; + width: 2px; + height: 2px; + color: #92cc41; + box-shadow: 18px 2px, 20px 2px, 16px 4px, 18px 4px, 20px 4px, 2px 6px, 14px 6px, 16px 6px, 2px 8px, 4px 8px, 12px 8px, 14px 8px, 4px 10px, 6px 10px, 10px 10px, 12px 10px, 6px 12px, 8px 12px, 10px 12px, 8px 14px; } + .MovesExplorer__wrapper__2y-BK { font-size: 1em; } .MovesExplorer__wrapper__2y-BK .title { diff --git a/dist/main-bundle.js b/dist/main-bundle.js index d32ba06..8da53af 100644 --- a/dist/main-bundle.js +++ b/dist/main-bundle.js @@ -29619,7 +29619,7 @@ function pathToRegexp (path, keys, options) { /*! ModuleConcatenation bailout: Cannot concat with ./node_modules/@babel/runtime/helpers/esm/assertThisInitialized.js because of ./node_modules/react-redux/es/index.js */ /*! ModuleConcatenation bailout: Cannot concat with ./node_modules/@babel/runtime/helpers/esm/extends.js because of ./node_modules/react-redux/es/index.js */ /*! ModuleConcatenation bailout: Cannot concat with ./node_modules/@babel/runtime/helpers/esm/inheritsLoose.js because of ./node_modules/react-redux/es/index.js */ -/*! ModuleConcatenation bailout: Cannot concat with ./node_modules/@babel/runtime/helpers/esm/objectWithoutPropertiesLoose.js because of ./node_modules/react-measure/dist/index.esm.js */ +/*! ModuleConcatenation bailout: Cannot concat with ./node_modules/@babel/runtime/helpers/esm/objectWithoutPropertiesLoose.js because of ./node_modules/react-redux/es/index.js */ /*! ModuleConcatenation bailout: Cannot concat with ./node_modules/react/index.js (<- Module is not an ECMAScript module) */ /***/ (function(module, __webpack_exports__, __webpack_require__) { @@ -38391,7 +38391,8 @@ function (_react_1$default$Comp) { onClick: onClick }, react_1.default.createElement("span", null, moveStats.name), react_1.default.createElement(TypeIndicator_1.TypeIndicator, { className: typeCss, - type: moveStats.type + type: moveStats.type, + theme: TypeIndicator_1.TypeTheme.SOLID }))); } }], [{ @@ -38472,14 +38473,14 @@ var react_1 = __importDefault(__webpack_require__(/*! react */ "./node_modules/r var classnames_1 = __importDefault(__webpack_require__(/*! classnames */ "./node_modules/classnames/index.js")); -var Pokemon_1 = __webpack_require__(/*! app/models/Pokemon */ "./src/ts/app/models/Pokemon.ts"); - var MovesDropdown_1 = __webpack_require__(/*! app/components/PokemonExplorer/MovesDropdown */ "./src/ts/app/components/PokemonExplorer/MovesDropdown.tsx"); var TypeEffectiveDisplay_1 = __webpack_require__(/*! ./TypeEffectiveDisplay */ "./src/ts/app/components/PokemonExplorer/TypeEffectiveDisplay.tsx"); var TypeIndicator_1 = __webpack_require__(/*! ./TypeIndicator */ "./src/ts/app/components/PokemonExplorer/TypeIndicator.tsx"); +var types_1 = __webpack_require__(/*! app/utils/types */ "./src/ts/app/utils/types.ts"); + var styles = __importStar(__webpack_require__(/*! app/components/PokemonExplorer/styles/MovesExplorer.scss */ "./src/ts/app/components/PokemonExplorer/styles/MovesExplorer.scss")); var MovesExplorer = @@ -38497,40 +38498,9 @@ function (_react_1$default$Comp) { _this.calculateTypeCoverage = function () { var _this$props = _this.props, selectedMoves = _this$props.selectedMoves, + movesById = _this$props.movesById, attackTypeEffectiveness = _this$props.attackTypeEffectiveness; - var calculatedffectiveness = new Map(); - Object.values(selectedMoves).forEach(function (move) { - var moveType = _this.getMoveType(move); - - if (moveType !== null) { - var moveEffectiveness = attackTypeEffectiveness.get(moveType); - - if (typeof moveEffectiveness !== 'undefined') { - moveEffectiveness.forEach(function (effectiveness, type) { - var currentEffectiveness = calculatedffectiveness.get(type); - - if (typeof currentEffectiveness === 'undefined' || Pokemon_1.TypeEffectiveness[effectiveness] > Pokemon_1.TypeEffectiveness[currentEffectiveness]) { - calculatedffectiveness.set(type, effectiveness); - } - }); - } - } - }); - return calculatedffectiveness; - }; - - _this.getMoveType = function (move) { - var moveStats = null; - - if (move !== null) { - moveStats = _this.props.movesById.get(move.id) || null; - } - - if (moveStats !== null) { - return moveStats.type; - } - - return null; + return types_1.calculateTypeCoverage(selectedMoves, movesById, attackTypeEffectiveness); }; _this.handleToggleQuickMoveMenu = function (isOpen) { @@ -38573,17 +38543,18 @@ function (_react_1$default$Comp) { movesById = _this$props2.movesById, quickMoves = _this$props2.quickMoves, chargeMoves = _this$props2.chargeMoves, - combatMoveSelectorsOpen = _this$props2.combatMoveSelectorsOpen; + combatMoveSelectorsOpen = _this$props2.combatMoveSelectorsOpen, + pokemonTypeWeaknesses = _this$props2.pokemonTypeWeaknesses; var _this$props$selectedM = this.props.selectedMoves, quickMove = _this$props$selectedM.quickMove, chargeMove1 = _this$props$selectedM.chargeMove1, chargeMove2 = _this$props$selectedM.chargeMove2; var wrapperCss = classnames_1.default('nes-container', styles.wrapper); - var quickMoveType = this.getMoveType(quickMove); + var quickMoveType = types_1.getMoveType(quickMove, movesById); var quickMoveCss = classnames_1.default(_defineProperty({}, styles.legacy, quickMove ? quickMove.isLegacy : false)); - var chargeMove1Type = this.getMoveType(chargeMove1); + var chargeMove1Type = types_1.getMoveType(chargeMove1, movesById); var chargeMove1Css = classnames_1.default(_defineProperty({}, styles.legacy, chargeMove1 ? chargeMove1.isLegacy : false)); - var chargeMove2Type = this.getMoveType(chargeMove2); + var chargeMove2Type = types_1.getMoveType(chargeMove2, movesById); var chargeMove2Css = classnames_1.default(_defineProperty({}, styles.legacy, chargeMove2 ? chargeMove2.isLegacy : false)); return react_1.default.createElement("div", { className: wrapperCss @@ -38597,7 +38568,8 @@ function (_react_1$default$Comp) { handleChangeSelectedOption: this.handleChangeQuickMove }), quickMove && quickMoveType && react_1.default.createElement(TypeIndicator_1.TypeIndicator, { className: quickMoveCss, - type: quickMoveType + type: quickMoveType, + theme: TypeIndicator_1.TypeTheme.SOLID }), react_1.default.createElement(MovesDropdown_1.MovesDropdown, { isMenuOpen: combatMoveSelectorsOpen.chargeMove1, menuLabel: "Charge Move 1", @@ -38608,7 +38580,8 @@ function (_react_1$default$Comp) { handleChangeSelectedOption: this.handleChangeChargeMove1 }), chargeMove1 && chargeMove1Type && react_1.default.createElement(TypeIndicator_1.TypeIndicator, { className: chargeMove1Css, - type: chargeMove1Type + type: chargeMove1Type, + theme: TypeIndicator_1.TypeTheme.SOLID }), react_1.default.createElement(MovesDropdown_1.MovesDropdown, { isMenuOpen: combatMoveSelectorsOpen.chargeMove2, menuLabel: "Charge Move 2", @@ -38619,10 +38592,12 @@ function (_react_1$default$Comp) { handleChangeSelectedOption: this.handleChangeChargeMove2 }), chargeMove2 && chargeMove2Type && react_1.default.createElement(TypeIndicator_1.TypeIndicator, { className: chargeMove2Css, - type: chargeMove2Type + type: chargeMove2Type, + theme: TypeIndicator_1.TypeTheme.SOLID }), react_1.default.createElement("div", null, react_1.default.createElement("h4", null, "Type Coverage"), react_1.default.createElement(TypeEffectiveDisplay_1.TypeEffectiveDisplay, { - pokemonName: "test", - effectiveness: this.calculateTypeCoverage() + mode: TypeEffectiveDisplay_1.EffectivenessMode.OFFENSE, + effectiveness: this.calculateTypeCoverage(), + coverage: pokemonTypeWeaknesses }))); } }]); @@ -38764,13 +38739,15 @@ function (_react_1$default$Comp) { fairy: formatter_1.Forms.fairy.indexOf(leaguePokemon.form) > -1 }); var type1 = react_1.default.createElement(TypeIndicator_1.TypeIndicator, { - type: leaguePokemon.types.type1 + type: leaguePokemon.types.type1, + theme: TypeIndicator_1.TypeTheme.SOLID }); var type2 = null; if (leaguePokemon.types.type2) { type2 = react_1.default.createElement(TypeIndicator_1.TypeIndicator, { - type: leaguePokemon.types.type2 + type: leaguePokemon.types.type2, + theme: TypeIndicator_1.TypeTheme.SOLID }); } @@ -38874,6 +38851,8 @@ var react_redux_1 = __webpack_require__(/*! react-redux */ "./node_modules/react var classnames_1 = __importDefault(__webpack_require__(/*! classnames */ "./node_modules/classnames/index.js")); +var Pokemon_1 = __webpack_require__(/*! app/models/Pokemon */ "./src/ts/app/models/Pokemon.ts"); + var ActionsPokemonExplorer = __importStar(__webpack_require__(/*! app/components/PokemonExplorer/actions */ "./src/ts/app/components/PokemonExplorer/actions.ts")); var LeagueIvExplorer_1 = __webpack_require__(/*! app/components/PokemonExplorer/LeagueIvExplorer */ "./src/ts/app/components/PokemonExplorer/LeagueIvExplorer.tsx"); @@ -38886,6 +38865,8 @@ var TypeEffectiveDisplay_1 = __webpack_require__(/*! app/components/PokemonExplo var navigation_1 = __webpack_require__(/*! app/utils/navigation */ "./src/ts/app/utils/navigation.ts"); +var types_1 = __webpack_require__(/*! app/utils/types */ "./src/ts/app/utils/types.ts"); + var styles = __importStar(__webpack_require__(/*! app/styles/PokemonApp.scss */ "./src/ts/app/styles/PokemonApp.scss")); var PokemonExplorer = @@ -38900,6 +38881,18 @@ function (_react_1$default$Comp) { _this = _possibleConstructorReturn(this, _getPrototypeOf(PokemonExplorer).call(this, props)); + _this.getSuperEffectiveTypes = function (effectiveness) { + var superEffectiveTypes = []; + Array.from(effectiveness).reduce(function (accumulator, currentValue) { + if (currentValue[1] > Pokemon_1.TypeEffectiveness.NEUTRAL) { + accumulator.push(currentValue[0]); + } + + return accumulator; + }, superEffectiveTypes); + return superEffectiveTypes; + }; + _this.handleToggleDropdownOpen = function (menu, isOpen) { var combatMoveSelectorsOpen = Object.assign({}, _this.props.pokemonExplorerState.combatMoveSelectorsOpen, _defineProperty({}, menu, isOpen)); @@ -39037,7 +39030,11 @@ function (_react_1$default$Comp) { var tmCss = classnames_1.default(iconCss, 'tm', { active: widgets.moves }); - var tmButtonCss = classnames_1.default(); + var tmButtonCss = classnames_1.default(); // weaknesses are indicated by types that cause super effective damage on defense + + var pokemonTypeWeaknesses = leaguePokemon !== null ? this.getSuperEffectiveTypes(leaguePokemon.effectiveness) : []; // strengths are indicuated by types that do super effective damage on offense + + var moveTypeStrengths = this.getSuperEffectiveTypes(types_1.calculateTypeCoverage(selectedCombatMoves, combatMoves, attackTypeEffectiveness)); return react_1.default.createElement("div", { className: styles.body }, react_1.default.createElement("div", { @@ -39045,10 +39042,13 @@ function (_react_1$default$Comp) { }, leaguePokemon !== null && react_1.default.createElement(PokemonDisplay_1.PokemonDisplay, { leaguePokemon: leaguePokemon, isHighlighted: isOverlayShown - }), widgets.types && leaguePokemon !== null && react_1.default.createElement(TypeEffectiveDisplay_1.TypeEffectiveDisplay, { + }), widgets.types && leaguePokemon !== null && react_1.default.createElement("div", { + className: "nes-container" + }, react_1.default.createElement(TypeEffectiveDisplay_1.TypeEffectiveDisplay, { + mode: TypeEffectiveDisplay_1.EffectivenessMode.DEFENSE, effectiveness: leaguePokemon.effectiveness, - pokemonName: leaguePokemon.name - }), widgets.pvp && leaguePokemon !== null && react_1.default.createElement(LeagueIvExplorer_1.LeagueIvExplorer, { + coverage: moveTypeStrengths + })), widgets.pvp && leaguePokemon !== null && react_1.default.createElement(LeagueIvExplorer_1.LeagueIvExplorer, { activeLeague: league, leaguePokemon: leaguePokemon, individualValues: individualValues, @@ -39060,6 +39060,7 @@ function (_react_1$default$Comp) { quickMoves: leaguePokemon.moves.quick, chargeMoves: leaguePokemon.moves.cinematic, selectedMoves: selectedCombatMoves, + pokemonTypeWeaknesses: pokemonTypeWeaknesses, attackTypeEffectiveness: attackTypeEffectiveness, combatMoveSelectorsOpen: combatMoveSelectorsOpen, handleToggleDropdownOpen: this.handleToggleDropdownOpen, @@ -39281,72 +39282,125 @@ var TypeIndicator_1 = __webpack_require__(/*! ./TypeIndicator */ "./src/ts/app/c var styles = __importStar(__webpack_require__(/*! app/components/PokemonExplorer/styles/TypeEffectiveDisplay.scss */ "./src/ts/app/components/PokemonExplorer/styles/TypeEffectiveDisplay.scss")); +var EffectivenessMode; + +(function (EffectivenessMode) { + EffectivenessMode[EffectivenessMode["OFFENSE"] = 0] = "OFFENSE"; + EffectivenessMode[EffectivenessMode["DEFENSE"] = 1] = "DEFENSE"; +})(EffectivenessMode = exports.EffectivenessMode || (exports.EffectivenessMode = {})); + var TypeEffectiveDisplay = /*#__PURE__*/ function (_react_1$default$Comp) { _inherits(TypeEffectiveDisplay, _react_1$default$Comp); function TypeEffectiveDisplay() { + var _this; + _classCallCheck(this, TypeEffectiveDisplay); - return _possibleConstructorReturn(this, _getPrototypeOf(TypeEffectiveDisplay).apply(this, arguments)); + _this = _possibleConstructorReturn(this, _getPrototypeOf(TypeEffectiveDisplay).apply(this, arguments)); + + _this.isTypeCovered = function (type) { + return _this.props.coverage.some(function (coverageType) { + return coverageType === type; + }); + }; + + return _this; } _createClass(TypeEffectiveDisplay, [{ key: "render", value: function render() { + var _this2 = this; + var _this$props = this.props, - effectiveness = _this$props.effectiveness, - pokemonName = _this$props.pokemonName; + mode = _this$props.mode, + effectiveness = _this$props.effectiveness; var wrapperCss = classnames_1.default('nes-container', styles.wrapper); var indicatorWrapperCss = classnames_1.default(styles.indicatorWrapper); - var multiplierWrapperX2Css = classnames_1.default(styles.multiplierWrapper); - var multiplierWrapperX3Css = classnames_1.default(styles.multiplierWrapper, styles.multiplierWrapperX3); - var multiplierWrapperX4Css = classnames_1.default(styles.multiplierWrapper, styles.multiplierWrapperX4); + var multiplierWrapperX2Css = classnames_1.default(styles.typeMultiplierWrapper); + var multiplierWrapperX3Css = classnames_1.default(styles.typeMultiplierWrapper, styles.typeMultiplierWrapperX3); + var multiplierWrapperX4Css = classnames_1.default(styles.typeMultiplierWrapper, styles.typeMultiplierWrapperX4); var superEffectiveX2 = []; var superEffective = []; var notVeryEffective = []; var notVeryEffectiveX2 = []; var notVeryEffectiveX3 = []; var notVeryEffectiveX4 = []; + var supereffectiveCoverage = []; + var supereffectiveCoverageX2 = []; + var notVeryEffectiveCoverage = []; + var notVeryEffectiveCoverageX2 = []; + var notVeryEffectiveCoverageX3 = []; + var notVeryEffectiveCoverageX4 = []; effectiveness.forEach(function (value, key) { - var typeIndicator; - - if (value === Pokemon_1.TypeEffectiveness.SUPER_EFFECTIVE || value === Pokemon_1.TypeEffectiveness.NOT_VERY_EFFECTIVE) { - // using `key` because these are later transformed into an array of duplicate elements - typeIndicator = react_1.default.createElement(TypeIndicator_1.TypeIndicator, { - key: key, - type: key - }); - } else { - typeIndicator = react_1.default.createElement(TypeIndicator_1.TypeIndicator, { - type: key - }); - } - switch (value) { case Pokemon_1.TypeEffectiveness.SUPER_EFFECTIVE_X2: - superEffectiveX2.push(typeIndicator); + superEffectiveX2.push(react_1.default.createElement(TypeIndicator_1.TypeIndicator, { + type: key, + theme: TypeIndicator_1.TypeTheme.SOLID + })); + supereffectiveCoverageX2.push(_this2.isTypeCovered(key)); break; case Pokemon_1.TypeEffectiveness.SUPER_EFFECTIVE: - superEffective.push(typeIndicator); + superEffective.push(react_1.default.createElement(TypeIndicator_1.TypeIndicator, { + key: key, + type: key, + theme: TypeIndicator_1.TypeTheme.SOLID + })); + supereffectiveCoverage.push(_this2.isTypeCovered(key)); break; case Pokemon_1.TypeEffectiveness.NOT_VERY_EFFECTIVE: - notVeryEffective.push(typeIndicator); + notVeryEffective.push(react_1.default.createElement(TypeIndicator_1.TypeIndicator, { + key: key, + type: key, + theme: TypeIndicator_1.TypeTheme.OUTLINE + })); + + if (mode === EffectivenessMode.DEFENSE) { + notVeryEffectiveCoverage.push(_this2.isTypeCovered(key)); + } + break; case Pokemon_1.TypeEffectiveness.IMMUNE: - notVeryEffectiveX2.push(typeIndicator); + notVeryEffectiveX2.push(react_1.default.createElement(TypeIndicator_1.TypeIndicator, { + type: key, + theme: TypeIndicator_1.TypeTheme.OUTLINE + })); + + if (mode === EffectivenessMode.DEFENSE) { + notVeryEffectiveCoverageX2.push(_this2.isTypeCovered(key)); + } + break; case Pokemon_1.TypeEffectiveness.NOT_VERY_EFFECTIVE_X3: - notVeryEffectiveX3.push(typeIndicator); + notVeryEffectiveX3.push(react_1.default.createElement(TypeIndicator_1.TypeIndicator, { + type: key, + theme: TypeIndicator_1.TypeTheme.OUTLINE + })); + + if (mode === EffectivenessMode.DEFENSE) { + notVeryEffectiveCoverageX3.push(_this2.isTypeCovered(key)); + } + break; case Pokemon_1.TypeEffectiveness.IMMUNE_X2: - notVeryEffectiveX4.push(typeIndicator); + notVeryEffectiveX4.push(react_1.default.createElement(TypeIndicator_1.TypeIndicator, { + type: key, + theme: TypeIndicator_1.TypeTheme.OUTLINE + })); + + if (mode === EffectivenessMode.DEFENSE) { + notVeryEffectiveCoverageX4.push(_this2.isTypeCovered(key)); + } + break; case Pokemon_1.TypeEffectiveness.NEUTRAL: @@ -39360,31 +39414,41 @@ function (_react_1$default$Comp) { }); return react_1.default.createElement("div", { className: wrapperCss - }, (notVeryEffective.length > 0 || notVeryEffectiveX2.length > 0 || notVeryEffectiveX3.length > 0 || notVeryEffectiveX4.length > 0) && react_1.default.createElement(react_1.default.Fragment, null, react_1.default.createElement("h4", null, pokemonName, "'s Resistances", react_1.default.createElement("p", null, "It's Not Very Effective...")), react_1.default.createElement("div", { + }, (notVeryEffective.length > 0 || notVeryEffectiveX2.length > 0 || notVeryEffectiveX3.length > 0 || notVeryEffectiveX4.length > 0) && react_1.default.createElement(react_1.default.Fragment, null, react_1.default.createElement("h4", null, mode === EffectivenessMode.DEFENSE ? 'Resistances' : "It's not very effective..."), react_1.default.createElement("div", { className: indicatorWrapperCss }, notVeryEffectiveX4.length > 0 && notVeryEffectiveX4.map(function (element, index) { return react_1.default.createElement("div", { key: "-4x".concat(index), - className: multiplierWrapperX4Css + className: "".concat(multiplierWrapperX4Css, " ").concat(notVeryEffectiveCoverageX4[index] ? styles.isCovered : '') }, element, element, element, element); }), notVeryEffectiveX3.length > 0 && notVeryEffectiveX3.map(function (element, index) { return react_1.default.createElement("div", { key: "-3x".concat(index), - className: multiplierWrapperX3Css + className: "".concat(multiplierWrapperX3Css, " ").concat(notVeryEffectiveCoverageX2[index] ? styles.isCovered : '') }, element, element, element); }), notVeryEffectiveX2.length > 0 && notVeryEffectiveX2.map(function (element, index) { return react_1.default.createElement("div", { key: "-2x".concat(index), - className: multiplierWrapperX2Css + className: "".concat(multiplierWrapperX2Css, " ").concat(notVeryEffectiveCoverageX2[index] ? styles.isCovered : '') }, element, element); - }), notVeryEffective.length > 0 && notVeryEffective)), (superEffective.length > 0 || superEffectiveX2.length > 0) && react_1.default.createElement(react_1.default.Fragment, null, react_1.default.createElement("h4", null, pokemonName, "'s Weaknesses", react_1.default.createElement("p", null, "It's Super Effective!")), react_1.default.createElement("div", { + }), notVeryEffective.length > 0 && notVeryEffective.map(function (element, index) { + return react_1.default.createElement("div", { + key: index, + className: "".concat(styles.typeWrapper, " ").concat(notVeryEffectiveCoverage[index] ? styles.isCovered : '') + }, element); + }))), (superEffective.length > 0 || superEffectiveX2.length > 0) && react_1.default.createElement(react_1.default.Fragment, null, react_1.default.createElement("h4", null, mode === EffectivenessMode.DEFENSE ? 'Weaknesses' : "It's super effective!"), react_1.default.createElement("div", { className: indicatorWrapperCss }, superEffectiveX2.length > 0 && superEffectiveX2.map(function (element, index) { return react_1.default.createElement("div", { key: "+2x".concat(index), - className: multiplierWrapperX2Css + className: "".concat(multiplierWrapperX2Css, " ").concat(supereffectiveCoverageX2[index] ? styles.isCovered : '') }, element, element); - }), superEffective.length > 0 && superEffective))); + }), superEffective.length > 0 && superEffective.map(function (element, index) { + return react_1.default.createElement("div", { + key: index, + className: "".concat(styles.typeWrapper, " ").concat(supereffectiveCoverage[index] ? styles.isCovered : '') + }, element); + })))); } }]); @@ -39452,6 +39516,13 @@ var formatter_1 = __webpack_require__(/*! app/utils/formatter */ "./src/ts/app/u var styles = __importStar(__webpack_require__(/*! app/components/PokemonExplorer/styles/TypeIndicator.scss */ "./src/ts/app/components/PokemonExplorer/styles/TypeIndicator.scss")); +var TypeTheme; + +(function (TypeTheme) { + TypeTheme[TypeTheme["SOLID"] = 0] = "SOLID"; + TypeTheme[TypeTheme["OUTLINE"] = 1] = "OUTLINE"; +})(TypeTheme = exports.TypeTheme || (exports.TypeTheme = {})); + var TypeIndicator = /*#__PURE__*/ function (_react_1$default$Comp) { @@ -39468,10 +39539,12 @@ function (_react_1$default$Comp) { value: function render() { var _this$props = this.props, className = _this$props.className, + theme = _this$props.theme, type = _this$props.type; - var containerCss = classnames_1.default('nes-container', 'with-title'); - var containerRoundCss = classnames_1.default(containerCss, 'is-rounded'); - var pokemonTypeCss = classnames_1.default(className, containerRoundCss, styles.pokemonType); + var containerCss = classnames_1.default('nes-container', 'with-title', 'is-rounded'); + var pokemonTypeCss = classnames_1.default(className, containerCss, styles.pokemonType, { + outline: theme === TypeTheme.OUTLINE + }); return react_1.default.createElement("div", { className: "".concat(pokemonTypeCss, " ").concat(formatter_1.formatType(type)) }, formatter_1.formatType(type)); @@ -39890,7 +39963,7 @@ module.exports = {"baseStatRow":"StatDisplay__baseStatRow__1B60A"}; /***/ (function(module, exports, __webpack_require__) { // extracted by mini-css-extract-plugin -module.exports = {"multiplierWrapper":"TypeEffectiveDisplay__multiplierWrapper__14os7","multiplierWrapperX3":"TypeEffectiveDisplay__multiplierWrapperX3__1SBG1","multiplierWrapperX4":"TypeEffectiveDisplay__multiplierWrapperX4__2KqYa","wrapper":"TypeEffectiveDisplay__wrapper__1FFIj","indicatorWrapper":"TypeEffectiveDisplay__indicatorWrapper__2F3AY"}; +module.exports = {"typeWrapper":"TypeEffectiveDisplay__typeWrapper__3w4wT","typeMultiplierWrapper":"TypeEffectiveDisplay__typeMultiplierWrapper__1WOCi","typeMultiplierWrapperX3":"TypeEffectiveDisplay__typeMultiplierWrapperX3__35-RS","typeMultiplierWrapperX4":"TypeEffectiveDisplay__typeMultiplierWrapperX4__2Onf7","wrapper":"TypeEffectiveDisplay__wrapper__1FFIj","indicatorWrapper":"TypeEffectiveDisplay__indicatorWrapper__2F3AY","isCovered":"TypeEffectiveDisplay__isCovered__3qyUq"}; /***/ }), @@ -41180,6 +41253,59 @@ exports.appendQueryString = function (location, parameters) { return '?' + search.toString(); }; +/***/ }), + +/***/ "./src/ts/app/utils/types.ts": +/*!***********************************!*\ + !*** ./src/ts/app/utils/types.ts ***! + \***********************************/ +/*! no static exports found */ +/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +exports.calculateTypeCoverage = function (selectedMoves, movesById, attackTypeEffectiveness) { + var calculatedffectiveness = new Map(); + Object.values(selectedMoves).forEach(function (move) { + var moveType = exports.getMoveType(move, movesById); + + if (moveType !== null) { + var moveEffectiveness = attackTypeEffectiveness.get(moveType); + + if (typeof moveEffectiveness !== 'undefined') { + moveEffectiveness.forEach(function (effectiveness, type) { + var currentEffectiveness = calculatedffectiveness.get(type); + + if (typeof currentEffectiveness === 'undefined' || effectiveness > currentEffectiveness) { + calculatedffectiveness.set(type, effectiveness); + } + }); + } + } + }); + return calculatedffectiveness; +}; + +exports.getMoveType = function (move, movesById) { + var moveStats = null; + + if (move !== null) { + moveStats = movesById.get(move.id) || null; + } + + if (moveStats !== null) { + return moveStats.type; + } + + return null; +}; + /***/ }) /******/ }); diff --git a/src/scss/Variables.scss b/src/scss/Variables.scss index f3af946..b64ec3b 100644 --- a/src/scss/Variables.scss +++ b/src/scss/Variables.scss @@ -1,4 +1,5 @@ @import '~nes.css/scss/base/color-palette'; +@import '~nes.css/scss/base/variables'; $container-width: ( desktop: 425px, @@ -51,6 +52,7 @@ $main-active-font-color: $gray-scale-1; $main-border-color: $gray-scale-4; $main-overlay-color: $gray-scale-4; $main-hover-color: darken($main-background-color, 5%); +$main-success: map-get($success-colors, 'normal'); $great-league-colors: ( primary: #649bde, diff --git a/src/ts/app/components/PokemonExplorer/MovesDropdown.tsx b/src/ts/app/components/PokemonExplorer/MovesDropdown.tsx index 7efaac7..0eda396 100644 --- a/src/ts/app/components/PokemonExplorer/MovesDropdown.tsx +++ b/src/ts/app/components/PokemonExplorer/MovesDropdown.tsx @@ -6,7 +6,7 @@ import classNames from 'classnames'; import { CombatMoveStats, IPokemonMove } from 'app/models/Pokemon'; -import { TypeIndicator } from './TypeIndicator'; +import { TypeIndicator, TypeTheme } from './TypeIndicator'; import * as styles from 'app/components/PokemonExplorer/styles/MovesDropdown.scss'; @@ -174,7 +174,7 @@ export class MovesDropdown extends React.Component onClick={ onClick } > { moveStats.name } - + } diff --git a/src/ts/app/components/PokemonExplorer/MovesExplorer.tsx b/src/ts/app/components/PokemonExplorer/MovesExplorer.tsx index 9c5fe27..81c7cfa 100644 --- a/src/ts/app/components/PokemonExplorer/MovesExplorer.tsx +++ b/src/ts/app/components/PokemonExplorer/MovesExplorer.tsx @@ -5,13 +5,15 @@ import React from 'react'; import classNames from 'classnames'; import { AttackTypeEffectiveness } from 'app/models/Config'; -import { CombatMoveStats, ICombatMoveStats, IPokemonMove, TypeEffectiveness } from 'app/models/Pokemon'; +import { CombatMoveStats, IPokemon, IPokemonMove } from 'app/models/Pokemon'; import { CombatMoveSelectorsOpen, SelectedCombatMoves } from 'app/components/PokemonExplorer/types'; import { MovesDropdown } from 'app/components/PokemonExplorer/MovesDropdown'; -import { TypeEffectiveDisplay } from './TypeEffectiveDisplay'; -import { TypeIndicator } from './TypeIndicator'; +import { EffectivenessMode, TypeEffectiveDisplay } from './TypeEffectiveDisplay'; +import { TypeIndicator, TypeTheme } from './TypeIndicator'; + +import { calculateTypeCoverage, getMoveType } from 'app/utils/types'; import * as styles from 'app/components/PokemonExplorer/styles/MovesExplorer.scss'; @@ -20,6 +22,7 @@ export interface IMovesExplorerProps { quickMoves : Array; chargeMoves : Array; selectedMoves : SelectedCombatMoves; + pokemonTypeWeaknesses : Array; attackTypeEffectiveness : AttackTypeEffectiveness; combatMoveSelectorsOpen : CombatMoveSelectorsOpen; handleToggleDropdownOpen : (menu : keyof CombatMoveSelectorsOpen, isOpen : boolean) => void; @@ -34,6 +37,7 @@ export class MovesExplorer extends React.Component { quickMoves, chargeMoves, combatMoveSelectorsOpen, + pokemonTypeWeaknesses, } = this.props; const { quickMove, @@ -46,15 +50,15 @@ export class MovesExplorer extends React.Component { styles.wrapper, ); - const quickMoveType = this.getMoveType(quickMove); + const quickMoveType = getMoveType(quickMove, movesById); const quickMoveCss = classNames({ [styles.legacy]: quickMove ? quickMove.isLegacy : false, }); - const chargeMove1Type = this.getMoveType(chargeMove1); + const chargeMove1Type = getMoveType(chargeMove1, movesById); const chargeMove1Css = classNames({ [styles.legacy]: chargeMove1 ? chargeMove1.isLegacy : false, }); - const chargeMove2Type = this.getMoveType(chargeMove2); + const chargeMove2Type = getMoveType(chargeMove2, movesById); const chargeMove2Css = classNames({ [styles.legacy]: chargeMove2 ? chargeMove2.isLegacy : false, }); @@ -71,7 +75,7 @@ export class MovesExplorer extends React.Component { handleChangeSelectedOption={ this.handleChangeQuickMove } /> { quickMove && quickMoveType && - + } { handleChangeSelectedOption={ this.handleChangeChargeMove1 } /> { chargeMove1 && chargeMove1Type && - + } { handleChangeSelectedOption={ this.handleChangeChargeMove2 } /> { chargeMove2 && chargeMove2Type && - + }

Type Coverage

@@ -111,36 +116,10 @@ export class MovesExplorer extends React.Component { private readonly calculateTypeCoverage = () => { const { selectedMoves, + movesById, attackTypeEffectiveness, } = this.props; - const calculatedffectiveness : Map = new Map(); - Object.values(selectedMoves).forEach((move) => { - const moveType = this.getMoveType(move); - if (moveType !== null) { - const moveEffectiveness = attackTypeEffectiveness.get(moveType); - if (typeof moveEffectiveness !== 'undefined') { - moveEffectiveness.forEach((effectiveness, type) => { - const currentEffectiveness = calculatedffectiveness.get(type); - if (typeof currentEffectiveness === 'undefined' || TypeEffectiveness[effectiveness] > TypeEffectiveness[currentEffectiveness]) { - calculatedffectiveness.set(type, effectiveness); - } - }); - } - } - }); - - return calculatedffectiveness; - } - - 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; + return calculateTypeCoverage(selectedMoves, movesById, attackTypeEffectiveness); } private readonly handleToggleQuickMoveMenu = (isOpen : boolean) => { diff --git a/src/ts/app/components/PokemonExplorer/PokemonDisplay.tsx b/src/ts/app/components/PokemonExplorer/PokemonDisplay.tsx index 28a6e3d..b14c6f2 100644 --- a/src/ts/app/components/PokemonExplorer/PokemonDisplay.tsx +++ b/src/ts/app/components/PokemonExplorer/PokemonDisplay.tsx @@ -9,7 +9,7 @@ import { IPokemon } from 'app/models/Pokemon'; import { formatDexNumber, formatForm, Forms } from 'app/utils/formatter'; import { StatDisplay } from './StatDisplay'; -import { TypeIndicator } from './TypeIndicator'; +import { TypeIndicator, TypeTheme } from './TypeIndicator'; import * as styles from 'app/components/PokemonExplorer/styles/PokemonDisplay.scss'; @@ -95,10 +95,10 @@ export class PokemonDisplay extends React.Component { }, ); - const type1 = ; + const type1 = ; let type2 : JSX.Element | null = null; if (leaguePokemon.types.type2) { - type2 = ; + type2 = ; } return ( diff --git a/src/ts/app/components/PokemonExplorer/PokemonExplorer.tsx b/src/ts/app/components/PokemonExplorer/PokemonExplorer.tsx index 9366d8b..d23acbb 100644 --- a/src/ts/app/components/PokemonExplorer/PokemonExplorer.tsx +++ b/src/ts/app/components/PokemonExplorer/PokemonExplorer.tsx @@ -1,3 +1,5 @@ +import POGOProtos from 'pogo-protos'; + import React from 'react'; import { connect } from 'react-redux'; import { RouteComponentProps } from 'react-router-dom'; @@ -6,7 +8,7 @@ import classNames from 'classnames'; import { AttackTypeEffectiveness } from 'app/models/Config'; import { League } from 'app/models/League'; -import { CombatMoveStats } from 'app/models/Pokemon'; +import { CombatMoveStats, TypeEffectiveness } from 'app/models/Pokemon'; import * as ActionsPokemonExplorer from 'app/components/PokemonExplorer/actions'; import { @@ -21,9 +23,10 @@ import { IRouterProps } from 'app/types'; import { LeagueIvExplorer } from 'app/components/PokemonExplorer/LeagueIvExplorer'; import { MovesExplorer } from 'app/components/PokemonExplorer/MovesExplorer'; import { PokemonDisplay } from 'app/components/PokemonExplorer/PokemonDisplay'; -import { TypeEffectiveDisplay } from 'app/components/PokemonExplorer/TypeEffectiveDisplay'; +import { EffectivenessMode, TypeEffectiveDisplay } from 'app/components/PokemonExplorer/TypeEffectiveDisplay'; import { appendQueryString } from 'app/utils/navigation'; +import { calculateTypeCoverage } from 'app/utils/types'; import * as styles from 'app/styles/PokemonApp.scss'; @@ -125,6 +128,11 @@ class PokemonExplorer extends React.Component
@@ -135,10 +143,13 @@ class PokemonExplorer extends React.Component } { widgets.types && leaguePokemon !== null && - +
+ +
} { widgets.pvp && leaguePokemon !== null && ) => { + const superEffectiveTypes : Array = []; + Array.from(effectiveness).reduce((accumulator, currentValue) => { + if (currentValue[1] > TypeEffectiveness.NEUTRAL) { + accumulator.push(currentValue[0]); + } + return accumulator; + }, superEffectiveTypes); + return superEffectiveTypes; + } + private readonly handleToggleDropdownOpen = (menu : keyof CombatMoveSelectorsOpen, isOpen : boolean) => { const combatMoveSelectorsOpen : CombatMoveSelectorsOpen = { ...this.props.pokemonExplorerState.combatMoveSelectorsOpen, diff --git a/src/ts/app/components/PokemonExplorer/TypeEffectiveDisplay.tsx b/src/ts/app/components/PokemonExplorer/TypeEffectiveDisplay.tsx index 574598b..1049bd2 100644 --- a/src/ts/app/components/PokemonExplorer/TypeEffectiveDisplay.tsx +++ b/src/ts/app/components/PokemonExplorer/TypeEffectiveDisplay.tsx @@ -1,24 +1,32 @@ +import POGOProtos from 'pogo-protos'; + import React from 'react'; import classNames from 'classnames'; import { IPokemon, TypeEffectiveness } from 'app/models/Pokemon'; -import { TypeIndicator } from './TypeIndicator'; +import { TypeIndicator, TypeTheme } from './TypeIndicator'; import * as styles from 'app/components/PokemonExplorer/styles/TypeEffectiveDisplay.scss'; +export enum EffectivenessMode { + OFFENSE, + DEFENSE, +} + export interface ITypeEffectiveDisplayProps { - pokemonName : string; + mode : EffectivenessMode; effectiveness : IPokemon['effectiveness']; + coverage : Array; } export class TypeEffectiveDisplay extends React.Component { public render() { const { + mode, effectiveness, - pokemonName, } = this.props; const wrapperCss = classNames( @@ -29,15 +37,15 @@ export class TypeEffectiveDisplay extends React.Component = []; @@ -46,35 +54,45 @@ export class TypeEffectiveDisplay extends React.Component = []; const notVeryEffectiveX3 : Array = []; const notVeryEffectiveX4 : Array = []; + const supereffectiveCoverage : Array = []; + const supereffectiveCoverageX2 : Array = []; + const notVeryEffectiveCoverage : Array = []; + const notVeryEffectiveCoverageX2 : Array = []; + const notVeryEffectiveCoverageX3 : Array = []; + const notVeryEffectiveCoverageX4 : Array = []; effectiveness.forEach((value, key) => { - let typeIndicator : JSX.Element; - if (value === TypeEffectiveness.SUPER_EFFECTIVE || - value === TypeEffectiveness.NOT_VERY_EFFECTIVE - ) { - // using `key` because these are later transformed into an array of duplicate elements - typeIndicator = ; - } else { - typeIndicator = ; - } - switch (value) { case TypeEffectiveness.SUPER_EFFECTIVE_X2: - superEffectiveX2.push(typeIndicator); + superEffectiveX2.push(); + supereffectiveCoverageX2.push(this.isTypeCovered(key)); break; case TypeEffectiveness.SUPER_EFFECTIVE: - superEffective.push(typeIndicator); + superEffective.push(); + supereffectiveCoverage.push(this.isTypeCovered(key)); break; case TypeEffectiveness.NOT_VERY_EFFECTIVE: - notVeryEffective.push(typeIndicator); + notVeryEffective.push(); + if (mode === EffectivenessMode.DEFENSE) { + notVeryEffectiveCoverage.push(this.isTypeCovered(key)); + } break; case TypeEffectiveness.IMMUNE: - notVeryEffectiveX2.push(typeIndicator); + notVeryEffectiveX2.push(); + if (mode === EffectivenessMode.DEFENSE) { + notVeryEffectiveCoverageX2.push(this.isTypeCovered(key)); + } break; case TypeEffectiveness.NOT_VERY_EFFECTIVE_X3: - notVeryEffectiveX3.push(typeIndicator); + notVeryEffectiveX3.push(); + if (mode === EffectivenessMode.DEFENSE) { + notVeryEffectiveCoverageX3.push(this.isTypeCovered(key)); + } break; case TypeEffectiveness.IMMUNE_X2: - notVeryEffectiveX4.push(typeIndicator); + notVeryEffectiveX4.push(); + if (mode === EffectivenessMode.DEFENSE) { + notVeryEffectiveCoverageX4.push(this.isTypeCovered(key)); + } break; case TypeEffectiveness.NEUTRAL: // do nothing @@ -89,32 +107,56 @@ export class TypeEffectiveDisplay extends React.Component { (notVeryEffective.length > 0 || notVeryEffectiveX2.length > 0 || notVeryEffectiveX3.length > 0 || notVeryEffectiveX4.length > 0) && -

{ pokemonName }'s Resistances

It's Not Very Effective...

+

{ mode === EffectivenessMode.DEFENSE ? 'Resistances' : `It's not very effective...` }

{ notVeryEffectiveX4.length > 0 && - notVeryEffectiveX4.map((element, index) =>
{ element }{ element }{ element }{ element }
) + notVeryEffectiveX4.map((element, index) => +
+ { element }{ element }{ element }{ element } +
+ ) } { notVeryEffectiveX3.length > 0 && - notVeryEffectiveX3.map((element, index) =>
{ element }{ element }{ element }
) + notVeryEffectiveX3.map((element, index) => +
+ { element }{ element }{ element } +
+ ) } { notVeryEffectiveX2.length > 0 && - notVeryEffectiveX2.map((element, index) =>
{ element }{ element }
) + notVeryEffectiveX2.map((element, index) => +
+ { element }{ element } +
+ ) } { notVeryEffective.length > 0 && - notVeryEffective + notVeryEffective.map((element, index) => +
+ { element } +
+ ) }
} { (superEffective.length > 0 || superEffectiveX2.length > 0) && -

{ pokemonName }'s Weaknesses

It's Super Effective!

+

{ mode === EffectivenessMode.DEFENSE ? 'Weaknesses' : `It's super effective!` }

{ superEffectiveX2.length > 0 && - superEffectiveX2.map((element, index) =>
{ element }{ element }
) + superEffectiveX2.map((element, index) => +
+ { element }{ element } +
+ ) } { superEffective.length > 0 && - superEffective + superEffective.map((element, index) => +
+ { element } +
+ ) }
@@ -122,4 +164,10 @@ export class TypeEffectiveDisplay extends React.Component ); } + + private readonly isTypeCovered = (type : POGOProtos.Enums.PokemonType) => { + return this.props.coverage.some((coverageType) => { + return coverageType === type; + }); + } } diff --git a/src/ts/app/components/PokemonExplorer/TypeIndicator.tsx b/src/ts/app/components/PokemonExplorer/TypeIndicator.tsx index 4e007dd..2fe2b83 100644 --- a/src/ts/app/components/PokemonExplorer/TypeIndicator.tsx +++ b/src/ts/app/components/PokemonExplorer/TypeIndicator.tsx @@ -8,27 +8,37 @@ import { formatType } from 'app/utils/formatter'; import * as styles from 'app/components/PokemonExplorer/styles/TypeIndicator.scss'; +export enum TypeTheme { + SOLID, + OUTLINE, +} + export interface ITypeEffectiveDisplayProps { className? : string; type : POGOProtos.Enums.PokemonType; + theme : TypeTheme; } export class TypeIndicator extends React.Component { public render() { - const { className, type } = this.props; + const { + className, + theme, + type, + } = this.props; const containerCss = classNames( 'nes-container', 'with-title', - ); - const containerRoundCss = classNames( - containerCss, 'is-rounded', ); const pokemonTypeCss = classNames( className, - containerRoundCss, + containerCss, styles.pokemonType, + { + outline: theme === TypeTheme.OUTLINE + } ); return
{ formatType(type) }
; diff --git a/src/ts/app/components/PokemonExplorer/styles/TypeEffectiveDisplay.scss b/src/ts/app/components/PokemonExplorer/styles/TypeEffectiveDisplay.scss index 76cd011..3287fb4 100644 --- a/src/ts/app/components/PokemonExplorer/styles/TypeEffectiveDisplay.scss +++ b/src/ts/app/components/PokemonExplorer/styles/TypeEffectiveDisplay.scss @@ -1,8 +1,12 @@ @import '~styles/Variables.scss'; -.multiplierWrapper { +.typeWrapper, +.typeMultiplierWrapper { position: relative; z-index: 0; // so the contents don't show up over the overlay +} + +.typeMultiplierWrapper { padding-top: 6px; padding-right: 6px; @@ -31,7 +35,7 @@ z-index: 0; } - &.multiplierWrapperX3 { + &.typeMultiplierWrapperX3 { padding-top: 12px; padding-right: 12px; @@ -44,7 +48,7 @@ } } - &.multiplierWrapperX4 { + &.typeMultiplierWrapperX4 { padding-top: 18px; padding-right: 18px; @@ -69,12 +73,6 @@ margin-top: 1em; } - h4 p { - font-size: 0.7em; - color: $main-font-secondary-color; - margin: 0; - } - .indicatorWrapper { display: flex; flex-flow: row wrap; @@ -85,23 +83,40 @@ height: 0%; // stop single effectiveness indicators from growing to match height/margin of multiple effectiveness indicators } - .multiplierWrapper { + .typeMultiplierWrapper { width: auto; margin-right: 2px; - &.multiplierWrapperX3 { + &.typeMultiplierWrapperX3 { margin-right: -4px; } - &.multiplierWrapperX4 { + &.typeMultiplierWrapperX4 { margin-right: -10px; } } - & > *, - .multiplierWrapper > * { + .typeWrapper > *, + .typeMultiplierWrapper > * { flex-basis: unset; width: 6.75rem; } } } + +.isCovered { + position: relative; + + &::before { + // inspired by nes.css's `.nes-checkbox` + content: ""; + position: absolute; + right: 25px; + bottom: 20px; + z-index: 10; + width: 2px; + height: 2px; + color: $main-success; + box-shadow: 18px 2px, 20px 2px, 16px 4px, 18px 4px, 20px 4px, 2px 6px, 14px 6px, 16px 6px, 2px 8px, 4px 8px, 12px 8px, 14px 8px, 4px 10px, 6px 10px, 10px 10px, 12px 10px, 6px 12px, 8px 12px, 10px 12px, 8px 14px; + } +} diff --git a/src/ts/app/components/PokemonExplorer/styles/TypeEffectiveDisplay.scss.d.ts b/src/ts/app/components/PokemonExplorer/styles/TypeEffectiveDisplay.scss.d.ts index 8a6b396..40645c3 100644 --- a/src/ts/app/components/PokemonExplorer/styles/TypeEffectiveDisplay.scss.d.ts +++ b/src/ts/app/components/PokemonExplorer/styles/TypeEffectiveDisplay.scss.d.ts @@ -1,7 +1,9 @@ // This file is automatically generated. // Please do not change this file! export const indicatorWrapper: string; -export const multiplierWrapper: string; -export const multiplierWrapperX3: string; -export const multiplierWrapperX4: string; +export const isCovered: string; +export const typeMultiplierWrapper: string; +export const typeMultiplierWrapperX3: string; +export const typeMultiplierWrapperX4: string; +export const typeWrapper: string; export const wrapper: string; diff --git a/src/ts/app/components/PokemonExplorer/styles/TypeIndicator.scss b/src/ts/app/components/PokemonExplorer/styles/TypeIndicator.scss index ef9d59e..859b693 100644 --- a/src/ts/app/components/PokemonExplorer/styles/TypeIndicator.scss +++ b/src/ts/app/components/PokemonExplorer/styles/TypeIndicator.scss @@ -28,6 +28,11 @@ &::after { @include rounded-box-shadow($primary); } + + &:global(.outline) { + color: $primary; + background-color: $contrast; + } } } } diff --git a/src/ts/app/utils/types.ts b/src/ts/app/utils/types.ts new file mode 100644 index 0000000..8a95797 --- /dev/null +++ b/src/ts/app/utils/types.ts @@ -0,0 +1,44 @@ +import POGOProtos from 'pogo-protos'; + +import { AttackTypeEffectiveness } from 'app/models/Config'; +import { CombatMoveStats, ICombatMoveStats, IPokemonMove, TypeEffectiveness } from 'app/models/Pokemon'; + +import { SelectedCombatMoves } from 'app/components/PokemonExplorer/types'; + +export const calculateTypeCoverage = ( + selectedMoves : SelectedCombatMoves, + movesById : CombatMoveStats, + attackTypeEffectiveness : AttackTypeEffectiveness, +) => { + const calculatedffectiveness : Map = new Map(); + Object.values(selectedMoves).forEach((move) => { + const moveType = getMoveType(move, movesById); + if (moveType !== null) { + const moveEffectiveness = attackTypeEffectiveness.get(moveType); + if (typeof moveEffectiveness !== 'undefined') { + moveEffectiveness.forEach((effectiveness, type) => { + const currentEffectiveness = calculatedffectiveness.get(type); + if (typeof currentEffectiveness === 'undefined' || effectiveness > currentEffectiveness) { + calculatedffectiveness.set(type, effectiveness); + } + }); + } + } + }); + + return calculatedffectiveness; +}; + +export const getMoveType = ( + move : IPokemonMove | null, + movesById : CombatMoveStats, +) => { + let moveStats : ICombatMoveStats | null = null; + if (move !== null) { + moveStats = movesById.get(move.id) || null; + } + if (moveStats !== null) { + return moveStats.type; + } + return null; +};