diff --git a/package.json b/package.json index f0302f9348..ef29b3874f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "homepage": "https://frzyc.github.io/genshin-optimizer/", "name": "genshin-optimizer", - "version": "2.3.2", + "version": "2.4.0", "private": true, "dependencies": { "@fortawesome/fontawesome-svg-core": "^1.2.32", diff --git a/src/App.js b/src/App.js index ab58a9118b..7f54518ae5 100644 --- a/src/App.js +++ b/src/App.js @@ -26,6 +26,8 @@ function App() {
Genshin Optimizer + + + diff --git a/src/Artifact/Artifact.js b/src/Artifact/Artifact.js index 78db70a38a..3b8a05d5d0 100644 --- a/src/Artifact/Artifact.js +++ b/src/Artifact/Artifact.js @@ -4,7 +4,8 @@ import CharacterDatabase from '../Character/CharacterDatabase'; import SlotIcon from '../Components/SlotIcon'; import { ArtifactMainSlotKeys, ArtifactMainStatsData, ArtifactSetsData, ArtifactSlotsData, ArtifactStarsData, ArtifactSubStatsData } from '../Data/ArtifactData'; import Stat from '../Stat'; -import { clampPercent, closeEnoughFloat, closeEnoughInt, deepClone } from '../Util'; +import ArtifactConditionals from '../Util/ArtifactConditionals'; +import { clamp, clampPercent, closeEnoughFloat, closeEnoughInt, deepClone } from '../Util/Util'; import ArtifactBase from './ArtifactBase'; import ArtifactDatabase from './ArtifactDatabase'; @@ -145,36 +146,96 @@ export default class Artifact { } static setToSlots = ArtifactBase.setToSlots; + + static getArtifactSets = (setKey, defVal = null) => + ArtifactSetsData?.[setKey]?.sets || defVal + static getArtifactSetNumStats = (setKey, setNumKey, defVal = null) => + this.getArtifactSets(setKey)?.[setNumKey]?.stats || defVal + + static getArtifactConditionalStats = (setKey, setNumKey, conditionalNum, defVal = null) => { + if (!conditionalNum) return defVal + let conditional = ArtifactSetsData[setKey].sets[setNumKey].conditional + if (!conditional) return defVal + if (Array.isArray(conditional)) { + //multiconditional + let selectedConditionalNum = conditionalNum + let selectedConditional = null + for (const curConditional of conditional) { + if (selectedConditionalNum > curConditional.maxStack) selectedConditionalNum -= curConditional.maxStack + else { + selectedConditional = curConditional; + break; + } + } + if (!selectedConditional) return defVal + let stacks = clamp(selectedConditionalNum, 1, selectedConditional.maxStack) + return Object.fromEntries(Object.entries(selectedConditional.stats).map(([key, val]) => [key, val * stacks])) + } else if (conditional.maxStack > 1) { + //condtional with stacks + let stacks = clamp(conditionalNum, 1, conditional.maxStack) + return Object.fromEntries(Object.entries(conditional.stats).map(([key, val]) => [key, val * stacks])) + } else if (conditional.maxStack === 1) + //boolean conditional + return conditional.stats + return defVal + } + static getArtifactSetEffectsStats = (setToSlots) => { + let artifactSetEffect = [] + Object.entries(setToSlots).forEach(([setKey, artArr]) => + ArtifactSetsData?.[setKey]?.sets && Object.entries(ArtifactSetsData[setKey].sets).forEach(([setNumKey, value]) => + parseInt(setNumKey) <= artArr.length && value.stats && Object.keys(value.stats).length && + Object.entries(value.stats).forEach(([key, statVal]) => + artifactSetEffect.push({ key, statVal })))) + return artifactSetEffect + } static getArtifactSetEffects = (setToSlots) => { let artifactSetEffect = {} - Object.entries(setToSlots).forEach(([setKey, arr]) => { - let numArts = arr.length; - if (ArtifactSetsData[setKey] && ArtifactSetsData[setKey].sets) { - Object.entries(ArtifactSetsData[setKey].sets).forEach(([num, value]) => { - if (parseInt(num) <= numArts) { - !artifactSetEffect[setKey] && (artifactSetEffect[setKey] = {}) - artifactSetEffect[setKey][num] = deepClone(value); - } - }) + Object.entries(setToSlots).forEach(([setKey, artArr]) => { + if (ArtifactSetsData?.[setKey]?.sets) { + let setNumKeys = Object.keys(ArtifactSetsData[setKey].sets).filter(setNumKey => parseInt(setNumKey) <= artArr.length) + if (setNumKeys.length) + artifactSetEffect[setKey] = setNumKeys } }) return artifactSetEffect } - static getAllArtifactSetEffectsObj = (conditionals) => { + + static getArtifactSetEffectText = (setKey, setNumKey, charFinalStats, defVal = "") => { + let setEffectText = ArtifactSetsData?.[setKey]?.sets?.[setNumKey]?.text + if (!setEffectText) return defVal + if (typeof setEffectText === "string") + return setEffectText + else if (typeof setEffectText === "function") + return setEffectText(charFinalStats || {}) + return defVal + } + static getArtifactSetEffectConditional = (setKey, setNumKey, defVal = null) => + ArtifactSetsData?.[setKey]?.sets?.[setNumKey]?.conditional || defVal + + static getAllArtifactSetEffectsObj = (artifactConditionals) => { let ArtifactSetEffectsObj = {}; - Object.entries(ArtifactSetsData).forEach(([key, setObj]) => { + Object.entries(ArtifactSetsData).forEach(([setKey, setObj]) => { let setEffect = {} let hasSetEffect = false if (setObj.sets) Object.entries(setObj.sets).forEach(([setNumKey, setEffectObj]) => { - //TODO conditionals if (setEffectObj.stats && Object.keys(setEffectObj.stats).length > 0) { setEffect[setNumKey] = deepClone(setEffectObj.stats) hasSetEffect = true } + if (setEffectObj.conditional) { + let conditionalNum = ArtifactConditionals.getConditionalNum(artifactConditionals, setKey, setNumKey) + if (conditionalNum) { + let condStats = this.getArtifactConditionalStats(setKey, setNumKey, conditionalNum) + if (condStats) { + setEffect[setNumKey] = deepClone(condStats) + hasSetEffect = true + } + } + } }) if (hasSetEffect) - ArtifactSetEffectsObj[key] = setEffect; + ArtifactSetEffectsObj[setKey] = setEffect; }) return ArtifactSetEffectsObj } diff --git a/src/Artifact/ArtifactBase.js b/src/Artifact/ArtifactBase.js index d5fc7efae4..98e19a9bd8 100644 --- a/src/Artifact/ArtifactBase.js +++ b/src/Artifact/ArtifactBase.js @@ -2,6 +2,7 @@ export default class ArtifactBase { //do not instantiate. constructor() { if (this instanceof ArtifactBase) throw Error('A static class cannot be instantiated.'); } + //returns {setKey:[slotKey...]} static setToSlots = (artifacts) => { let setToSlots = {}; Object.entries(artifacts).forEach(([key, art]) => { @@ -9,6 +10,6 @@ export default class ArtifactBase { if (setToSlots[art.setKey]) setToSlots[art.setKey].push(key) else setToSlots[art.setKey] = [key] }) - return setToSlots //{setKey:[slotKey...]} + return setToSlots } } \ No newline at end of file diff --git a/src/Artifact/ArtifactDatabase.js b/src/Artifact/ArtifactDatabase.js index 01f346911a..7de9509a1c 100644 --- a/src/Artifact/ArtifactDatabase.js +++ b/src/Artifact/ArtifactDatabase.js @@ -1,4 +1,4 @@ -import { deepClone, loadFromLocalStorage, saveToLocalStorage } from "../Util"; +import { deepClone, loadFromLocalStorage, saveToLocalStorage } from "../Util/Util"; var initiated = false var artifactDatabase = {}; var artIdIndex = 1; diff --git a/src/Artifact/ArtifactDisplay.js b/src/Artifact/ArtifactDisplay.js index fe3fc8db9f..c1d5bd7e49 100644 --- a/src/Artifact/ArtifactDisplay.js +++ b/src/Artifact/ArtifactDisplay.js @@ -12,12 +12,12 @@ import { Stars } from '../Components/StarDisplay'; import { ArtifactStarsData, ArtifactSubStatsData } from '../Data/ArtifactData'; import { DatabaseInitAndVerify } from '../DatabaseUtil'; import Stat from '../Stat'; -import { deepClone } from '../Util'; import Artifact from './Artifact'; import ArtifactCard from './ArtifactCard'; import ArtifactDatabase from './ArtifactDatabase'; import ArtifactEditor from './ArtifactEditor'; import ReactGA from 'react-ga'; +import { deepClone } from '../Util/Util'; export default class ArtifactDisplay extends React.Component { constructor(props) { diff --git a/src/Artifact/ArtifactEditor.js b/src/Artifact/ArtifactEditor.js index 10fc4bd1c8..7f9ead20f3 100644 --- a/src/Artifact/ArtifactEditor.js +++ b/src/Artifact/ArtifactEditor.js @@ -6,7 +6,7 @@ import { FloatFormControl, IntFormControl } from '../Components/CustomFormContro import { Stars } from '../Components/StarDisplay'; import { ArtifactSetsData, ArtifactSlotsData, ArtifactStarsData, ArtifactSubStatsData } from '../Data/ArtifactData'; import Stat from '../Stat'; -import { deepClone, getArrLastElement, getRandomElementFromArray, getRandomIntInclusive } from '../Util'; +import { deepClone, getArrLastElement, getRandomElementFromArray, getRandomIntInclusive } from '../Util/Util'; import Artifact from './Artifact'; import ArtifactDatabase from './ArtifactDatabase'; import PercentBadge from './PercentBadge'; @@ -298,12 +298,12 @@ export default class ArtifactEditor extends React.Component { Substat Efficiency - Every time 4 artifact upgrades, you get a substat roll. The substat efficiency calculates as a percentage how high the substat rolled. The Maximum Substat Efficiency of an artifact calculates the efficiency if the remaining upgrades rolled maximum. + Every 4 artifact upgrades, you get a substat roll. The substat efficiency calculates as a percentage how high the substat rolled. The Maximum Substat Efficiency of an artifact calculates the efficiency if the remaining upgrades rolled maximum. } diff --git a/src/Build/BuildDisplay.js b/src/Build/BuildDisplay.js index 1cd41e3c4b..ad3f918bdf 100644 --- a/src/Build/BuildDisplay.js +++ b/src/Build/BuildDisplay.js @@ -2,20 +2,23 @@ import { faSortAmountDownAlt, faSortAmountUp } from '@fortawesome/free-solid-svg import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import React from 'react'; import { Alert, Badge, Button, ButtonGroup, Card, Col, Container, Dropdown, DropdownButton, ListGroup, Modal, Row } from 'react-bootstrap'; +import ReactGA from 'react-ga'; // eslint-disable-next-line import Worker from "worker-loader!./BuildWorker.js"; import Artifact from '../Artifact/Artifact'; -import { ArtifactSetsData, ArtifactSlotsData } from '../Data/ArtifactData'; import ArtifactDatabase from '../Artifact/ArtifactDatabase'; import Character from '../Character/Character'; +import CharacterCard from '../Character/CharacterCard'; import CharacterDatabase from '../Character/CharacterDatabase'; import CharacterDisplayCard from '../Character/CharacterDisplayCard'; +import ConditionalSelector from '../Components/ConditionalSelector'; +import { ArtifactSlotsData } from '../Data/ArtifactData'; import { DatabaseInitAndVerify } from '../DatabaseUtil'; import Stat from '../Stat'; -import { deepClone, loadFromLocalStorage, saveToLocalStorage } from '../Util'; -import Build from './Build'; -import ReactGA from 'react-ga'; +import ArtifactConditionals from '../Util/ArtifactConditionals'; +import { deepClone, loadFromLocalStorage, saveToLocalStorage } from '../Util/Util'; import Weapon from '../Weapon/Weapon'; +import Build from './Build'; export default class BuildDisplay extends React.Component { constructor(props) { @@ -42,6 +45,7 @@ export default class BuildDisplay extends React.Component { gobletMainKey: "", circletMainkey: "", setFilters: [{ key: "", num: 0 }, { key: "", num: 0 }, { key: "", num: 0 }], + artifactConditionals: [],//{ setKey: "", setNumKey: "", conditionalNum: 0 } mainStat: ["", "", ""], buildFilterKey: "atk", asending: false, @@ -87,25 +91,28 @@ export default class BuildDisplay extends React.Component { return { mainStat } }) } - changeSetFilterKey = (index, newkey, setsNumArr) => { - if (this.state.setFilters[index].key === newkey) return - this.setState((state) => { - let setFilters = deepClone(state.setFilters); - let num = 0 - if (setsNumArr && setsNumArr[0]) - num = parseInt(setsNumArr[0]) - setFilters[index] = { key: newkey, num } - return { setFilters } - }) - } + changeSetFilterKey = (index, newkey, setsNumArr) => this.setState(state => { + let oldKey = state.setFilters[index].key + if (oldKey === newkey) return + //remove conditionals with that key + let artifactConditionals = state.artifactConditionals ? state.artifactConditionals.filter(artifactCond => artifactCond.setKey !== oldKey) : [] + let setFilters = state.setFilters; + let num = 0 + //automatically select the 1st element from setsNumArr + if (setsNumArr && setsNumArr[0]) + num = parseInt(setsNumArr[0]) + setFilters[index] = { key: newkey, num } + return { setFilters, artifactConditionals } + }) + dropdownitemsForStar = (star, index) => - Artifact.getArtifactSetsByMaxStarEntries(star).map(([key, setobj]) => { - if (this.state.setFilters.some(filter => filter.key === key)) return false; - let setsNumArr = Object.keys(ArtifactSetsData[key].sets) + Artifact.getArtifactSetsByMaxStarEntries(star).map(([setKey, setobj]) => { + if (this.state.setFilters.some(filter => filter.key === setKey)) return false; + let setsNumArr = Object.keys(Artifact.getArtifactSets(setKey)) let artsAccountedOther = this.state.setFilters.reduce((accu, cur, ind) => (cur.key && ind !== index) ? accu + cur.num : accu, 0) if (setsNumArr.every(num => parseInt(num) + artsAccountedOther > 5)) return false; - return ( this.changeSetFilterKey(index, key, setsNumArr)} + return ( this.changeSetFilterKey(index, setKey, setsNumArr)} > {setobj.name} ) @@ -113,12 +120,12 @@ export default class BuildDisplay extends React.Component { generateBuilds = (split, artifactSetPerms) => { this.setState({ generatingBuilds: true, builds: [] }) - let { setFilters, asending, buildFilterKey, maxBuildsToShow } = this.state + let { setFilters, asending, buildFilterKey, maxBuildsToShow, artifactConditionals } = this.state let character = CharacterDatabase.getCharacter(this.state.selectedCharacterId) let weaponStats = Weapon.createWeaponBundle(character) let initialStats = Character.calculateCharacterWithWeaponStats(character, weaponStats) - let artifactSetEffects = Artifact.getAllArtifactSetEffectsObj() + let artifactSetEffects = Artifact.getAllArtifactSetEffectsObj(artifactConditionals) let splitArtifacts = deepClone(split) //add mainStatVal to each artifact, TODO add main stat assuming fully leveled up Object.values(splitArtifacts).forEach(artArr => { @@ -141,7 +148,7 @@ export default class BuildDisplay extends React.Component { label: Build.calculateTotalBuildNumber(split, artifactSetPerms, this.state.setFilters) }) let builds = e.data.builds.map(obj => - Character.calculateBuildWithObjs(initialStats, obj.artifacts)) + Character.calculateBuildWithObjs(artifactConditionals, initialStats, obj.artifacts)) this.setState({ builds, generatingBuilds: false }) } @@ -161,86 +168,133 @@ export default class BuildDisplay extends React.Component { : (totBuildNumber > this.state.maxBuildsToGenerate ? Current configuration will generate {totBuildNumber} builds for {characterName}. Please restrict artifact configuration to reduce builds to less than {this.state.maxBuildsToGenerate}, or your browser might crash. : Current configuration will generate {totBuildNumber} builds for {characterName}.) + let characterDropDown = + this.setState({ selectedCharacterId: "", builds: [] })}>No Character + {Object.values(charlist).map((char, i) => + this.setState({ selectedCharacterId: char.id, builds: [] })} + > + {char.name} + )} + return Build Generator - - - {/* Character picker */} -
- - this.setState({ selectedCharacterId: "", builds: [] })}> - No Character - - {Object.values(charlist).map((char, i) => - this.setState({ selectedCharacterId: char.id, builds: [] })} - > - {char.name} - )} - -
- {/* Artifact set picker */} - {this.state.setFilters.map((setFilter, index) => -
- - {/* Artifact set */} - - this.changeSetFilterKey(index, "")}> - Unselect Artifact - - Max Rarity 🟊🟊🟊🟊🟊 - {this.dropdownitemsForStar(5, index)} - - Max Rarity 🟊🟊🟊🟊 - {this.dropdownitemsForStar(4, index)} - - Max Rarity 🟊🟊🟊 - {this.dropdownitemsForStar(3, index)} + + + {/* character selection */} + {this.state.selectedCharacterId ? : + + + {characterDropDown} + + } + {/* main stat selector */} + + Artifact Main Stat (Optional) + + {BuildDisplay.artifactsSlotsToSelectMainStats.map((slotKey, index) => + (
+
+ {Artifact.getArtifactSlotNameWithIcon(slotKey)} +
+ + this.changeMainStat(index, "")} >No MainStat + {ArtifactSlotsData[slotKey].stats.map(mainStatKey => + this.changeMainStat(index, mainStatKey)} key={mainStatKey}> + {Stat.getStatNameWithPercent(mainStatKey)} + + )} - {/* set number */} - = 5} - > - {setFilter.key && Artifact.getArtifactSetEffectsObj(setFilter.key) && Object.keys(Artifact.getArtifactSetEffectsObj(setFilter.key)).map(num => { - let artsAccountedOther = this.state.setFilters.reduce((accu, cur) => (cur.key && cur.key !== setFilter.key) ? accu + cur.num : accu, 0) - return (parseInt(num) + artsAccountedOther <= 5) && - ( this.setState((state) => { - let setFilters = deepClone(state.setFilters); - setFilters[index].num = parseInt(num) - return { setFilters } - })} - > - {`${num}-set`} - ) - })} - - -
- )} +
))} +
+
- -
Artifact Main Stat (Optional)
- {BuildDisplay.artifactsSlotsToSelectMainStats.map((slotKey, index) => - (
-
- {Artifact.getArtifactSlotNameWithIcon(slotKey)} -
- - this.changeMainStat(index, "")} >No MainStat - {ArtifactSlotsData[slotKey].stats.map(mainStatKey => - this.changeMainStat(index, mainStatKey)} key={mainStatKey}> - {Stat.getStatNameWithPercent(mainStatKey)} + + {/* Artifact set picker */} + {this.state.setFilters.map((setFilter, index) => { + let { key: setKey, num } = setFilter + let { artifactConditionals } = this.state + return ( + + + + {/* Artifact set */} + + this.changeSetFilterKey(index, "")}> + Unselect Artifact - )} - -
))} - + Max Rarity 🟊🟊🟊🟊🟊 + {this.dropdownitemsForStar(5, index)} + + Max Rarity 🟊🟊🟊🟊 + {this.dropdownitemsForStar(4, index)} + + Max Rarity 🟊🟊🟊 + {this.dropdownitemsForStar(3, index)} + + {/* set number */} + = 5} + > + {setFilter.key && Artifact.getArtifactSetEffectsObj(setFilter.key) && Object.keys(Artifact.getArtifactSetEffectsObj(setFilter.key)).map(num => { + let artsAccountedOther = this.state.setFilters.reduce((accu, cur) => (cur.key && cur.key !== setFilter.key) ? accu + cur.num : accu, 0) + return (parseInt(num) + artsAccountedOther <= 5) && + ( this.setState((state) => { + let setFilters = deepClone(state.setFilters); + setFilters[index].num = parseInt(num) + return { setFilters } + })} + > + {`${num}-set`} + ) + })} + + + + {setFilter.key ? + {Object.keys(Artifact.getArtifactSets(setKey)).filter(setNkey => parseInt(setNkey) <= num).map(setNumKey => { + let setStats = Artifact.getArtifactSetNumStats(setKey, setNumKey) + let conditionalNum = 0; + let conditional = Artifact.getArtifactSetEffectConditional(setKey, setNumKey) + if (conditional) { + conditionalNum = ArtifactConditionals.getConditionalNum(artifactConditionals, setKey, setNumKey) + let conditionalStats = Artifact.getArtifactConditionalStats(setKey, setNumKey, conditionalNum) + if (conditionalStats) { + if (!setStats) setStats = {} + Object.entries(conditionalStats).forEach(([statKey, val]) => + setStats[statKey] = (setStats[statKey] || 0) + val) + } + } + let setStateArtifactConditional = (conditionalNum) => this.setState(state => + ({ artifactConditionals: ArtifactConditionals.setConditional(state.artifactConditionals, setKey, setNumKey, conditionalNum) })) + let conditionalElement = {setNumKey}-Set} + /> + return +
{conditionalElement} {Artifact.getArtifactSetEffectText(setKey, setNumKey)}
+ {setStats ? + {Object.entries(setStats).map(([statKey, val]) => + {Stat.getStatName(statKey)}: {val}{Stat.getStatUnit(statKey)})} + : null} + + })} +
: null} +
+ ) + })} + - + + {this.state.selectedCharacterId && buildAlert} + + - + } @@ -68,7 +60,7 @@ export default function CharacterCard(props) {

{Character.getName(characterKey)}

- {`Lvl. ${Character.getLevelWithOverride(characterData)} C${constellation}`} + {`Lvl. ${Character.getLevelWithOverride(character)} C${constellation}`}
@@ -79,20 +71,19 @@ export default function CharacterCard(props) { - {Object.entries(artifactSetEffect).map(([key, obj]) => { + {Object.entries(Artifact.getArtifactSetEffects(setToSlots)).map(([key, arr]) => { let artifactSetName = Artifact.getArtifactSetName(key) - let highestNum = Math.max(...Object.keys(obj)) - return {artifactSetName}({highestNum}) + let highestNum = Math.max(...arr) + return
{artifactSetName} {highestNum}
})}
- {Object.keys(statIcon).map(statKey => { - // let statVal = Character.getStatValueWithOverride(characterData, statKey) + {statkeys.map(statKey => { let unit = Stat.getStatUnit(statKey) let statVal = build.finalStats[statKey] return -
{statIcon[statKey] && } {Stat.getStatName(statKey)}
+
{StatIconEle(statKey)} {Stat.getStatName(statKey)}
{statVal?.toFixed(Stat.fixedUnit(statKey)) + unit} @@ -100,13 +91,13 @@ export default function CharacterCard(props) { })}
- + {props.footer && - + } ) } \ No newline at end of file diff --git a/src/Character/CharacterDatabase.js b/src/Character/CharacterDatabase.js index 8d34db8e6c..febbd7bde1 100644 --- a/src/Character/CharacterDatabase.js +++ b/src/Character/CharacterDatabase.js @@ -1,4 +1,4 @@ -import { deepClone, loadFromLocalStorage, saveToLocalStorage } from "../Util"; +import { deepClone, loadFromLocalStorage, saveToLocalStorage } from "../Util/Util"; var initiated = false var characterDatabase = {}; var charIdIndex = 1; diff --git a/src/Character/CharacterDisplay.js b/src/Character/CharacterDisplay.js index a2a07a2370..8468dbe1de 100644 --- a/src/Character/CharacterDisplay.js +++ b/src/Character/CharacterDisplay.js @@ -67,7 +67,8 @@ export default class CharacterDisplay extends React.Component { {charIdList.map(id => this.deleteCharacter(id)} onEdit={() => this.editCharacter(id)} /> diff --git a/src/Character/CharacterDisplay/CharacterArtifactPane.js b/src/Character/CharacterDisplay/CharacterArtifactPane.js index e062dcfcd4..b8074dd339 100644 --- a/src/Character/CharacterDisplay/CharacterArtifactPane.js +++ b/src/Character/CharacterDisplay/CharacterArtifactPane.js @@ -1,18 +1,20 @@ -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import React, { useState } from 'react'; import { Accordion, Badge, Button, Card, Col, Row } from 'react-bootstrap'; import Artifact from '../../Artifact/Artifact'; import ArtifactCard from '../../Artifact/ArtifactCard'; -import StatIcon from '../../Components/StatIcon'; +import ConditionalSelector from '../../Components/ConditionalSelector'; +import { StatIconEle } from '../../Components/StatIcon'; import Stat from "../../Stat"; +import ArtifactConditionals from '../../Util/ArtifactConditionals'; import Character from "../Character"; function CharacterArtifactPane(props) { let [showOther, setShowOther] = useState(false) - let { character: { characterKey, compareAgainstEquipped }, equippedBuild, newBuild, editable, forceUpdate } = props + let { character: { characterKey, compareAgainstEquipped, artifactConditionals }, equippedBuild, newBuild, editable, forceUpdate, setState } = props let { character } = props //choose which one to display stats for let build = newBuild ? newBuild : equippedBuild + if (newBuild) artifactConditionals = newBuild.artifactConditionals let eleKey = Character.getElementalKey(characterKey) const statKeys = ["hp", "atk", "def", "ele_mas", "crit_rate", "crit_dmg", "crit_multi", "ener_rech", "heal_bonu", "phy_dmg", "phy_atk",] statKeys.push(`${eleKey}_ele_dmg`) @@ -31,7 +33,7 @@ function CharacterArtifactPane(props) { let buildDiff = (build?.finalStats?.[statKey] || 0) - statVal return -
{StatIcon[statKey] ? : null} {Stat.getStatName(statKey)}
+
{StatIconEle(statKey)} {Stat.getStatName(statKey)}
{statVal?.toFixed(Stat.fixedUnit(statKey)) + unit} {buildDiff ? 0 ? "text-success" : "text-danger"}> {buildDiff > 0 && "+"}{buildDiff?.toFixed(Stat.fixedUnit(statKey)) + unit} : null} @@ -44,13 +46,15 @@ function CharacterArtifactPane(props) { let buildDiff = (newBuild?.finalStats?.[statKey] || 0) - (equippedBuild?.finalStats?.[statKey] || 0) return -
{StatIcon[statKey] ? : null} {Stat.getStatName(statKey)}
+
{StatIconEle(statKey)} {Stat.getStatName(statKey)}
{statVal?.toFixed(Stat.fixedUnit(statKey)) + unit} {buildDiff ? 0 ? "text-success" : "text-danger"}> ({buildDiff > 0 ? "+" : ""}{buildDiff?.toFixed(Stat.fixedUnit(statKey)) + unit}) : null} } + const setStateArtifactConditional = (setKey, setNumKey, conditionalNum) => setState(state => + ({ artifactConditionals: ArtifactConditionals.setConditional(state.artifactConditionals, setKey, setNumKey, conditionalNum) })) return <> @@ -98,12 +102,37 @@ function CharacterArtifactPane(props) { Artifact Set Effects - {Object.entries(build.artifactSetEffect).map(([setKey, effects]) => + {Object.entries(Artifact.getArtifactSetEffects(build.setToSlots)).map(([setKey, setNumKeyArr]) =>
{Artifact.getArtifactSetName(setKey)}
- {Object.entries(effects).map(([num, effect]) => { - return
{num}-Set {effect.text}
+ {setNumKeyArr.map(setNumKey => { + let setStats = Artifact.getArtifactSetNumStats(setKey, setNumKey) + let conditionalNum = 0; + let conditional = Artifact.getArtifactSetEffectConditional(setKey, setNumKey) + if (conditional) { + conditionalNum = ArtifactConditionals.getConditionalNum(artifactConditionals, setKey, setNumKey) + let conditionalStats = Artifact.getArtifactConditionalStats(setKey, setNumKey, conditionalNum) + if (conditionalStats) { + if (!setStats) setStats = {} + Object.entries(conditionalStats).forEach(([statKey, val]) => + setStats[statKey] = (setStats[statKey] || 0) + val) + } + } + let conditionalElement = setStateArtifactConditional(setKey, setNumKey, cnum)} + defEle={{setNumKey}-Set} + /> + return +
{conditionalElement} {Artifact.getArtifactSetEffectText(setKey, setNumKey, build.finalStats)}
+ {setStats ? + {Object.entries(setStats).map(([statKey, val]) => + {Stat.getStatName(statKey)}: {val}{Stat.getStatUnit(statKey)})} + : null} + })}
diff --git a/src/Character/CharacterDisplay/CharacterOverviewPane.js b/src/Character/CharacterDisplay/CharacterOverviewPane.js index f4e3e59937..d262a394bc 100644 --- a/src/Character/CharacterDisplay/CharacterOverviewPane.js +++ b/src/Character/CharacterDisplay/CharacterOverviewPane.js @@ -1,11 +1,12 @@ -import { faCheckSquare, faEdit, faGavel, faQuoteLeft, faSave, faSquare } from "@fortawesome/free-solid-svg-icons" +import { faEdit, faGavel, faQuoteLeft, faSave } from "@fortawesome/free-solid-svg-icons" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" import React, { useState } from "react" -import { Badge, Button, Card, Col, Dropdown, DropdownButton, Image, InputGroup, OverlayTrigger, Row, Tooltip } from "react-bootstrap" +import { Button, Card, Col, Dropdown, DropdownButton, Image, InputGroup, OverlayTrigger, Row, Tooltip } from "react-bootstrap" import Assets from "../../Assets/Assets" +import ConditionalSelector from "../../Components/ConditionalSelector" import { FloatFormControl, IntFormControl } from "../../Components/CustomFormControl" import { Stars } from "../../Components/StarDisplay" -import StatIcon from "../../Components/StatIcon" +import StatIcon, { StatIconEle } from "../../Components/StatIcon" import { CharacterSpecializedStatKey } from "../../Data/CharacterData" import { LevelNameData } from "../../Data/WeaponData" import Stat from "../../Stat" @@ -105,68 +106,14 @@ function WeaponStatsEditorCard(props) { let weaponPassiveName = Weapon.getWeaponPassiveName(weapon.key) let weaponBonusStats = Weapon.getWeaponBonusStat(weapon.key, weapon.refineIndex) let conditionalStats = Weapon.getWeaponConditionalStat(weapon.key, weapon.refineIndex, weapon.conditionalNum) - let conditionalEle = (() => { - let conditional = Weapon.getWeaponConditional(weapon.key) - if (!conditional) return
{weaponPassiveName}
- let conditionalNum = weapon.conditionalNum; - if (Array.isArray(conditional)) { - let selectedConditionalNum = conditionalNum - let selectedConditional - for (const curConditional of conditional) { - if (selectedConditionalNum > curConditional.maxStack) selectedConditionalNum -= curConditional.maxStack - else { - selectedConditional = curConditional; - break; - } - } - if (!selectedConditional) selectedConditionalNum = 0 - //TODO multi conditional - let text = selectedConditionalNum === 0 ? "Not Active" : - (selectedConditional.condition + (selectedConditional.maxStack > 1 ? (`: ${selectedConditionalNum} stack${selectedConditionalNum > 1 ? "s" : ""}`) : "")) - let badge = {text} - let count = 0; - return - -
{weaponPassiveName} {badge}
-
- - setStateWeapon("conditionalNum", 0)}> - Not Active - - {conditional.map(condial => - {[...Array(condial.maxStack).keys()].map(v => v + 1).map(stack => { - let tempcount = ++count - return setStateWeapon("conditionalNum", tempcount)}> - {condial.condition}{selectedConditional.maxStack > 1 ? `: ${stack} stack${stack > 1 ? "s" : ""}` : ""} - - })} - )} - -
- } else if (conditional.maxStack > 1) { - //stacking conditional - let badge = {conditionalNum > 0 ? `${conditionalNum} stack${conditionalNum > 1 ? "s" : ""}` : "Not Active"} - return - -
{weaponPassiveName} {badge}
-
- - setStateWeapon("conditionalNum", 0)}> - Not Active - - {[...Array(conditional.maxStack).keys()].map(v => v + 1).map(stack => - setStateWeapon("conditionalNum", stack)}> - {`${stack} stack${stack > 1 ? "s" : ""}`} - )} - -
- } else if (conditional.maxStack === 1) { - //single boolean conditional - return - } - })() + let conditional = Weapon.getWeaponConditional(weapon.key) + let conditionalNum = weapon.conditionalNum; + let conditionalEle = setStateWeapon("conditionalNum", cnum)} + defEle={
{weaponPassiveName}
} + /> return @@ -240,7 +187,7 @@ function WeaponStatsEditorCard(props) { {subStatKey && {StatIcon[subStatKey] ? : null}{Stat.getStatName(subStatKey)}
} + name={{StatIconEle(subStatKey)}{Stat.getStatName(subStatKey)}} placeholder="Weapon Substat" value={weaponDisplaySubVal} percent={Stat.getStatUnit(subStatKey) === "%"} @@ -321,7 +268,7 @@ function MainStatsCard(props) { let unit = Stat.getStatUnit(statKey) let buildDiff = (build?.finalStats?.[statKey] || 0) - statVal return -
{StatIcon[statKey] ? : null} {Stat.getStatName(statKey)}
+
{StatIconEle(statKey)} {Stat.getStatName(statKey)}
{statVal?.toFixed(Stat.fixedUnit(statKey)) + unit} {buildDiff ? 0 ? "text-success" : "text-danger"}> {buildDiff > 0 && "+"}{buildDiff?.toFixed(Stat.fixedUnit(statKey)) + unit} : null} @@ -334,7 +281,7 @@ function MainStatsCard(props) { let buildDiff = (newBuild?.finalStats?.[statKey] || 0) - (equippedBuild?.finalStats?.[statKey] || 0) return -
{StatIcon[statKey] ? : null} {Stat.getStatName(statKey)}
+
{StatIconEle(statKey)} {Stat.getStatName(statKey)}
{statVal?.toFixed(Stat.fixedUnit(statKey)) + unit} {buildDiff ? 0 ? "text-success" : "text-danger"}> ({buildDiff > 0 && "+"}{buildDiff?.toFixed(Stat.fixedUnit(statKey)) + unit}) : null} diff --git a/src/Character/CharacterDisplayCard.js b/src/Character/CharacterDisplayCard.js index 18926c2125..1d1149fc86 100644 --- a/src/Character/CharacterDisplayCard.js +++ b/src/Character/CharacterDisplayCard.js @@ -7,16 +7,15 @@ import Card from 'react-bootstrap/Card'; import Col from 'react-bootstrap/Col'; import DropdownToggle from 'react-bootstrap/esm/DropdownToggle'; import Row from 'react-bootstrap/Row'; -import ArtifactDatabase from '../Artifact/ArtifactDatabase'; import { WeaponLevelKeys } from '../Data/WeaponData'; import { DatabaseInitAndVerify } from '../DatabaseUtil'; -import { deepClone, getRandomElementFromArray } from '../Util'; +import { deepClone, getRandomElementFromArray } from '../Util/Util'; +import Weapon from '../Weapon/Weapon'; import Character from './Character'; import CharacterDatabase from './CharacterDatabase'; import CharacterArtifactPane from './CharacterDisplay/CharacterArtifactPane'; import CharacterOverviewPane from './CharacterDisplay/CharacterOverviewPane'; import CharacterTalentPane from './CharacterDisplay/CharacterTalentPane'; -import Weapon from '../Weapon/Weapon' const CustomMenu = React.forwardRef( ({ children, style, className, 'aria-labelledby': labeledBy }, ref) => { @@ -51,6 +50,7 @@ export default class CharacterDisplayCard extends React.Component { levelKey: "L1",//combination of level and ascension overrideLevel: 0, equippedArtifacts: {}, + artifactConditionals: [], baseStatOverrides: {},//overriding the baseStat weapon: { key: "", @@ -128,9 +128,8 @@ export default class CharacterDisplayCard extends React.Component { render() { let { footer, newBuild, editable } = this.props let character = this.state - let { characterKey, equippedArtifacts, levelKey, compareAgainstEquipped } = this.state - let equippedArtifactsObjs = Object.fromEntries(Object.entries(equippedArtifacts).map(([key, artid]) => [key, ArtifactDatabase.getArtifact(artid)])) - let equippedBuild = Character.calculateBuild(this.state, equippedArtifactsObjs) + let { characterKey, levelKey, compareAgainstEquipped } = this.state + let equippedBuild = Character.calculateBuild(this.state) let HeaderIconDisplay =
{Character.getName(characterKey)}
@@ -229,7 +228,7 @@ export default class CharacterDisplayCard extends React.Component { /> - + {newBuild ? diff --git a/src/Components/ConditionalSelector.js b/src/Components/ConditionalSelector.js new file mode 100644 index 0000000000..6111acaadd --- /dev/null +++ b/src/Components/ConditionalSelector.js @@ -0,0 +1,71 @@ +import { faCheckSquare, faSquare } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import React from 'react'; +import { Badge, Button, Dropdown } from 'react-bootstrap'; + +export default function ConditionalSelector(props) { + let { conditional, conditionalNum, setConditional, defEle, disabled } = props + if (!conditional) return defEle + if (Array.isArray(conditional)) { + let selectedConditionalNum = conditionalNum + let selectedConditional = null + for (const curConditional of conditional) { + if (selectedConditionalNum > curConditional.maxStack) selectedConditionalNum -= curConditional.maxStack + else { + selectedConditional = curConditional; + break; + } + } + if (!selectedConditional) { + selectedConditionalNum = 0 + selectedConditional = conditional[0] + } + + //multi conditional + let text = selectedConditionalNum === 0 ? "Not Active" : + (selectedConditional.condition + (selectedConditional.maxStack > 1 ? (`: ${selectedConditionalNum} stack${selectedConditionalNum > 1 ? "s" : ""}`) : "")) + let badge = {text} + let count = 0; + return + +
{defEle} {badge}
+
+ + setConditional(0)}> + Not Active + + {conditional.map(condial => + {[...Array(condial.maxStack).keys()].map(v => v + 1).map(stack => { + let tempcount = ++count + return setConditional(tempcount)}> + {condial.condition}{selectedConditional.maxStack > 1 ? `: ${stack} stack${stack > 1 ? "s" : ""}` : ""} + + })} + )} + +
+ } else if (conditional.maxStack > 1) { + //stacking conditional + let badge = {conditionalNum > 0 ? `${conditionalNum} stack${conditionalNum > 1 ? "s" : ""}` : "Not Active"} + return + +
{defEle} {badge}
+
+ + setConditional(0)}> + Not Active + + {[...Array(conditional.maxStack).keys()].map(v => v + 1).map(stack => + setConditional(stack)}> + {`${stack} stack${stack > 1 ? "s" : ""}`} + )} + +
+ } else if (conditional.maxStack === 1) { + //single boolean conditional + return + } + +} \ No newline at end of file diff --git a/src/Components/StatIcon.js b/src/Components/StatIcon.js index 91776be713..b603439896 100644 --- a/src/Components/StatIcon.js +++ b/src/Components/StatIcon.js @@ -1,13 +1,25 @@ import { faDice, faDiceD20, faFirstAid, faFistRaised, faMagic, faShieldAlt, faSync, faTint } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' const StatIcon = { hp: faTint, + hp_: faTint, atk: faFistRaised, + atk_: faFistRaised, def: faShieldAlt, + def_: faShieldAlt, ele_mas: faMagic, crit_rate: faDice, crit_dmg: faDiceD20, ener_rech: faSync, heal_bonu: faFirstAid, } -export default StatIcon \ No newline at end of file + +const StatIconEle = (statKey) => + StatIcon[statKey] ? : null + +export default StatIcon + +export { + StatIconEle +} \ No newline at end of file diff --git a/src/Data/Artifacts/Adventurer/Adventurer.js b/src/Data/Artifacts/Adventurer/Adventurer.js index 2e6d31165b..9f85851311 100644 --- a/src/Data/Artifacts/Adventurer/Adventurer.js +++ b/src/Data/Artifacts/Adventurer/Adventurer.js @@ -3,6 +3,7 @@ import plume from './Item_Adventurer\'s_Tail_Feather.png' import sands from './Item_Adventurer\'s_Pocket_Watch.png' import goblet from './Item_Adventurer\'s_Golden_Goblet.png' import circlet from './Item_Adventurer\'s_Bandana.png' +import WeaponPercent from '../../../Components/WeaponPercent' let artifact = { name: "Adventurer", rarity: [3], pieces: { @@ -25,8 +26,7 @@ let artifact = { stats: { hp: 1000 } }, 4: { - text: "Opening chest regenerates 30% Max HP over 5s.", - stats: {} + text: (charFinalStats)=>Opening chest regenerates 30% Max HP{WeaponPercent(30, charFinalStats.hp)} over 5s., } } } diff --git a/src/Data/Artifacts/ArchaicPetra/ArchaicPetra.js b/src/Data/Artifacts/ArchaicPetra/ArchaicPetra.js index ba631c1aec..a5a5c84d56 100644 --- a/src/Data/Artifacts/ArchaicPetra/ArchaicPetra.js +++ b/src/Data/Artifacts/ArchaicPetra/ArchaicPetra.js @@ -25,8 +25,40 @@ let artifact = { stats: { geo_ele_dmg: 15 } }, 4: { - text: "Upon obtaining a crystal created through a Geo Elemental Reaction, all party members gain 35% RES to that particular element for 10s. Only one form of Elemental RES can be gained in this manner at any one time. Upon obtaining a crystal created through a Geo Elemental Reaction, all party members gain 35% RES to that particular element for 10s. Only one form of Elemental RES can be gained in this manner at any one time.", - stats: {} + text: "Upon obtaining an Elemental Shard created through a Crystallize Reaction, all party members gain 35% DMG to that particular element for 10s. Only one form of Elemental DMG can be gained in this manner at any one time.", + conditional: [{//TODO all party conditional + type: "artifact", + condition:"Pyro", + sourceKey: "ArchaicPetra_4", + maxStack: 1, + stats: { + pyro_ele_dmg: 35, + } + },{ + type: "artifact", + condition:"Electro", + sourceKey: "ArchaicPetra_4", + maxStack: 1, + stats: { + electro_ele_dmg: 35, + } + },{ + type: "artifact", + condition:"Hydro", + sourceKey: "ArchaicPetra_4", + maxStack: 1, + stats: { + hydro_ele_dmg: 35, + } + },{ + type: "artifact", + condition:"Cryo", + sourceKey: "ArchaicPetra_4", + maxStack: 1, + stats: { + cryo_ele_dmg: 35, + } + }] } } } diff --git a/src/Data/Artifacts/Berserker/Berserker.js b/src/Data/Artifacts/Berserker/Berserker.js index 1214412217..699d5c5732 100644 --- a/src/Data/Artifacts/Berserker/Berserker.js +++ b/src/Data/Artifacts/Berserker/Berserker.js @@ -4,7 +4,7 @@ import sands from './Item_Berserker\'s_Timepiece.png' import goblet from './Item_Berserker\'s_Bone_Goblet.png' import circlet from './Item_Berserker\'s_Battle_Mask.png' let artifact = { - name: "Berserker", rarity: [3, 4], + name: "Berserker", rarity: [3, 4], pieces: { flower: "Berserker's Rose", plume: "Berserker's Indigo Feather", @@ -26,7 +26,14 @@ let artifact = { }, 4: { text: "When HP is below 70%, CRIT Rate increases by an additional 24%.", - stats: {} + conditional: { + type: "artifact", + sourceKey: "Berserker_4", + maxStack: 1, + stats: { + crit_rate: 24, + } + } } } } diff --git a/src/Data/Artifacts/BlizzardStrayer/BlizzardStrayer.js b/src/Data/Artifacts/BlizzardStrayer/BlizzardStrayer.js index c0d08c309a..fd5d02c0f6 100644 --- a/src/Data/Artifacts/BlizzardStrayer/BlizzardStrayer.js +++ b/src/Data/Artifacts/BlizzardStrayer/BlizzardStrayer.js @@ -26,7 +26,23 @@ let artifact = {//Icebreaker }, 4: { text: "When a character attacks an enemy affected by Cryo, their CRIT Rate is increased by 20%. If the enemy is Frozen, CRIT Rate is increased by an additional 20%", - stats: {} + conditional: [{ + type: "artifact", + sourceKey: "BlizzardStrayer_4", + condition: "Enemy affected by Cryo", + maxStack: 1, + stats: { + crit_rate: 20, + } + }, { + type: "artifact", + sourceKey: "BlizzardStrayer_4", + condition: "Frozen Enemy", + maxStack: 1, + stats: { + crit_rate: 40, + } + }] } } } diff --git a/src/Data/Artifacts/BloodstainedChivalry/BloodstainedChivalry.js b/src/Data/Artifacts/BloodstainedChivalry/BloodstainedChivalry.js index 215c798a85..d41e86ec15 100644 --- a/src/Data/Artifacts/BloodstainedChivalry/BloodstainedChivalry.js +++ b/src/Data/Artifacts/BloodstainedChivalry/BloodstainedChivalry.js @@ -4,7 +4,7 @@ import sands from './Item_Bloodstained_Final_Hour.png' import goblet from './Item_Bloodstained_Chevalier\'s_Goblet.png' import circlet from './Item_Bloodstained_Iron_Mask.png' let artifact = { - name: "Bloodstained Chivalry", rarity: [4, 5], + name: "Bloodstained Chivalry", rarity: [4, 5], pieces: { flower: "Bloodstained Flower of Iron", plume: "Bloodstained Black Plume", @@ -26,7 +26,15 @@ let artifact = { }, 4: { text: "After defeating an opponent, increases Charged Attack DMG by 50%, and reduces its Stamina cost to 0 for 10s.", - stats: {} + conditional: { + type: "artifact", + sourceKey: "BloodstainedChivalry_4", + maxStack: 1, + stats: { + char_atk_dmg: 50, + stamina_dec: 100, + } + } } } } diff --git a/src/Data/Artifacts/BraveHeart/BraveHeart.js b/src/Data/Artifacts/BraveHeart/BraveHeart.js index b5dc6a4af7..74dba8fce1 100644 --- a/src/Data/Artifacts/BraveHeart/BraveHeart.js +++ b/src/Data/Artifacts/BraveHeart/BraveHeart.js @@ -26,7 +26,14 @@ let artifact = { }, 4: { text: "Increases DMG by 30% against enemies with more than 50% HP.", - stats: {} + conditional: { + type: "artifact", + sourceKey: "BraveHeart_4", + maxStack: 1, + stats: { + dmg: 30, + } + } } } } diff --git a/src/Data/Artifacts/CrimsonWitchOfFlames/CrimsonWitchOfFlames.js b/src/Data/Artifacts/CrimsonWitchOfFlames/CrimsonWitchOfFlames.js index 5831a376aa..e5733adc5f 100644 --- a/src/Data/Artifacts/CrimsonWitchOfFlames/CrimsonWitchOfFlames.js +++ b/src/Data/Artifacts/CrimsonWitchOfFlames/CrimsonWitchOfFlames.js @@ -26,7 +26,20 @@ let artifact = { }, 4: { text: "Increases Overloaded and Burning DMG by 40%. Increases Vaporize and Melt DMG by 15%. Using an Elemental Skill increases 2-Piece Set effects by 50% for 10s. Max 3 stacks.", - stats: {} + stats: { + overloaded_dmg: 40, + burning_dmg: 40, + vaporize_dmg: 15, + melt_dmg: 15, + }, + conditional: { + type: "artifact", + sourceKey: "CrimsonWitchOfFlames_4", + maxStack: 3, + stats: { + pyro_ele_dmg: 15 + } + } } } } diff --git a/src/Data/Artifacts/DefendersWill/DefendersWill.js b/src/Data/Artifacts/DefendersWill/DefendersWill.js index 7cb99c2b64..548b6a8493 100644 --- a/src/Data/Artifacts/DefendersWill/DefendersWill.js +++ b/src/Data/Artifacts/DefendersWill/DefendersWill.js @@ -26,7 +26,57 @@ let artifact = { }, 4: { text: "Increases Elemental RES by 30% for each element present in the party.", - stats: {} + conditional: [//TODO all party conditional + { + type: "artifact", + condition: "Anemo", + sourceKey: "DefendersWill_4", + maxStack: 1, + stats: { + aneme_ele_res: 30 + } + }, { + type: "artifact", + condition: "Geo", + sourceKey: "DefendersWill_4", + maxStack: 1, + stats: { + geo_ele_res: 30 + } + }, { + type: "artifact", + condition: "Electro", + sourceKey: "DefendersWill_4", + maxStack: 1, + stats: { + electro_ele_res: 30 + } + }, { + type: "artifact", + condition: "Hydro", + sourceKey: "DefendersWill_4", + maxStack: 1, + stats: { + hydro_ele_res: 30 + } + }, { + type: "artifact", + condition: "pyro", + sourceKey: "DefendersWill_4", + maxStack: 1, + stats: { + pyro_ele_res: 30 + } + }, { + type: "artifact", + condition: "Cryo", + sourceKey: "DefendersWill_4", + maxStack: 1, + stats: { + cryo_ele_res: 30 + } + }, + ] } } } diff --git a/src/Data/Artifacts/Gambler/Gambler.js b/src/Data/Artifacts/Gambler/Gambler.js index 1100870035..d029aa55ce 100644 --- a/src/Data/Artifacts/Gambler/Gambler.js +++ b/src/Data/Artifacts/Gambler/Gambler.js @@ -26,7 +26,14 @@ let artifact = { }, 4: { text: "Defeating an enemy has 100% chance to remove Elemental Skill CD. Can only occur once every 15s.", - stats: {} + conditional: { + type: "artifact", + sourceKey: "Gambler_4", + maxStack: 1, + stats: { + skill_cd_red: 100, + } + } } } } diff --git a/src/Data/Artifacts/GladiatorsFinale/GladiatorsFinale.js b/src/Data/Artifacts/GladiatorsFinale/GladiatorsFinale.js index 41b34e472a..cb664bb31e 100644 --- a/src/Data/Artifacts/GladiatorsFinale/GladiatorsFinale.js +++ b/src/Data/Artifacts/GladiatorsFinale/GladiatorsFinale.js @@ -26,7 +26,14 @@ let artifact = { }, 4: { text: "If the wielder of this artifact set uses a Sword, Claymore or Polearm, increases their Normal Attack DMG by 35%.", - stats: {} + conditional: { + type: "artifact", + sourceKey: "GladiatorsFinale_4", + maxStack: 1, + stats: { + norm_atk_dmg: 35 + } + } } } } diff --git a/src/Data/Artifacts/HeartOfDepth/HeartOfDepth.js b/src/Data/Artifacts/HeartOfDepth/HeartOfDepth.js index c1ca5d4c85..f3e015d440 100644 --- a/src/Data/Artifacts/HeartOfDepth/HeartOfDepth.js +++ b/src/Data/Artifacts/HeartOfDepth/HeartOfDepth.js @@ -26,7 +26,15 @@ let artifact = {//Ocean Conqueror }, 4: { text: "After using Elemental Skill, increases Normal Attack and Charged Attack DMG by 30% for 15s", - stats: {} + conditional: { + type: "artifact", + sourceKey: "HeartOfDepth_4", + maxStack: 1, + stats: { + norm_atk_dmg: 30, + char_atk_dmg: 30 + } + } } } } diff --git a/src/Data/Artifacts/Instructor/Instructor.js b/src/Data/Artifacts/Instructor/Instructor.js index 0a2007cf3f..40bc99b1c7 100644 --- a/src/Data/Artifacts/Instructor/Instructor.js +++ b/src/Data/Artifacts/Instructor/Instructor.js @@ -26,7 +26,14 @@ let artifact = { }, 4: { text: "After using Elemental Skill, increases all party members' Elemental Mastery by 120 for 8s.", - stats: {} + conditional: { + type: "artifact", + sourceKey: "Instructor_4", + maxStack: 1, + stats: { + ele_mas: 120,//TODO all party conditional + } + } } } } diff --git a/src/Data/Artifacts/Lavawalker/Lavawalker.js b/src/Data/Artifacts/Lavawalker/Lavawalker.js index 9d6181be53..7e222d4833 100644 --- a/src/Data/Artifacts/Lavawalker/Lavawalker.js +++ b/src/Data/Artifacts/Lavawalker/Lavawalker.js @@ -26,7 +26,14 @@ let artifact = { }, 4: { text: "Increases DMG against enemies that are Burning or affected by Pyro by 35%.", - stats: {} + conditional: { + type: "artifact", + sourceKey: "Lavawalker_4", + maxStack: 1, + stats: { + dmg: 35, + } + } } } } diff --git a/src/Data/Artifacts/LuckyDog/LuckyDog.js b/src/Data/Artifacts/LuckyDog/LuckyDog.js index d5dd69a696..f1ce55ab8d 100644 --- a/src/Data/Artifacts/LuckyDog/LuckyDog.js +++ b/src/Data/Artifacts/LuckyDog/LuckyDog.js @@ -26,7 +26,6 @@ let artifact = { }, 4: { text: "Picking up Mora restores 300 HP.", - stats: {} } } } diff --git a/src/Data/Artifacts/MaidenBeloved/MaidenBeloved.js b/src/Data/Artifacts/MaidenBeloved/MaidenBeloved.js index 35e6e5277f..0c82ff4faf 100644 --- a/src/Data/Artifacts/MaidenBeloved/MaidenBeloved.js +++ b/src/Data/Artifacts/MaidenBeloved/MaidenBeloved.js @@ -26,7 +26,14 @@ let artifact = { }, 4: { text: "Using an Elemental Skill or Burst increases healing received by all party members by 20% for 10s.", - stats: {} + conditional: { + type: "artifact", + sourceKey: "MaidenBeloved_4", + maxStack: 1, + stats: { + inc_heal: 20,//TODO party conditional + } + } } } } diff --git a/src/Data/Artifacts/MartialArtist/MartialArtist.js b/src/Data/Artifacts/MartialArtist/MartialArtist.js index 361db87e4e..10c49a8e0e 100644 --- a/src/Data/Artifacts/MartialArtist/MartialArtist.js +++ b/src/Data/Artifacts/MartialArtist/MartialArtist.js @@ -29,7 +29,15 @@ let artifact = { }, 4: { text: "After using Elemental Skill, increases Normal Attack and Charged Attack DMG by 25% for 8s.", - stats: {} + conditional: { + type: "artifact", + sourceKey: "MartialArtist_4", + maxStack: 1, + stats: { + norm_atk_dmg: 25, + char_atk_dmg: 25 + } + } } } } diff --git a/src/Data/Artifacts/NoblesseOblige/NoblesseOblige.js b/src/Data/Artifacts/NoblesseOblige/NoblesseOblige.js index 4726fb4b7a..95fe1f41e6 100644 --- a/src/Data/Artifacts/NoblesseOblige/NoblesseOblige.js +++ b/src/Data/Artifacts/NoblesseOblige/NoblesseOblige.js @@ -26,7 +26,14 @@ let artifact = { }, 4: { text: "Using an Elemental Burst increase all party members' ATK by 20% for 12s. This effect cannot stack.", - stats: {} + conditional: {//TODO party conditional + type: "artifact", + sourceKey: "NoblesseOblige_4", + maxStack: 1, + stats: { + atk_: 20, + } + } } } } diff --git a/src/Data/Artifacts/PrayersForDestiny/PrayersForDestiny.js b/src/Data/Artifacts/PrayersForDestiny/PrayersForDestiny.js index d7f4bff3d7..25b33cac34 100644 --- a/src/Data/Artifacts/PrayersForDestiny/PrayersForDestiny.js +++ b/src/Data/Artifacts/PrayersForDestiny/PrayersForDestiny.js @@ -10,7 +10,7 @@ let artifact = { sets: { 1: { text: "Affected by Hydro for 40% less time.", - stats: {} + stats: {}//TODO element affect reduction stat } } } diff --git a/src/Data/Artifacts/PrayersForIllumination/PrayersForIllumination.js b/src/Data/Artifacts/PrayersForIllumination/PrayersForIllumination.js index a6940c8aea..49beeba9f9 100644 --- a/src/Data/Artifacts/PrayersForIllumination/PrayersForIllumination.js +++ b/src/Data/Artifacts/PrayersForIllumination/PrayersForIllumination.js @@ -10,7 +10,7 @@ let artifact = { sets: { 1: { text: "Affected by Pyro for 40% less time.", - stats: {} + stats: {}//TODO element affect reduction stat } } } diff --git a/src/Data/Artifacts/PrayersForWisdom/PrayersForWisdom.js b/src/Data/Artifacts/PrayersForWisdom/PrayersForWisdom.js index b6154cb711..e276247c05 100644 --- a/src/Data/Artifacts/PrayersForWisdom/PrayersForWisdom.js +++ b/src/Data/Artifacts/PrayersForWisdom/PrayersForWisdom.js @@ -10,7 +10,7 @@ let artifact = { sets: { 1: { text: "Affected by Electro for 40% less time.", - stats: {} + stats: {}//TODO element affect reduction stat } } } diff --git a/src/Data/Artifacts/PrayersToSpringtime/PrayersToSpringtime.js b/src/Data/Artifacts/PrayersToSpringtime/PrayersToSpringtime.js index bfe4b899dc..bb59ea1b34 100644 --- a/src/Data/Artifacts/PrayersToSpringtime/PrayersToSpringtime.js +++ b/src/Data/Artifacts/PrayersToSpringtime/PrayersToSpringtime.js @@ -10,7 +10,7 @@ let artifact = { sets: { 1: { text: "Affected by Cryo for 40% less time.", - stats: {} + stats: {}//TODO element affect reduction stat } } } diff --git a/src/Data/Artifacts/ResolutionOfSojourner/ResolutionOfSojourner.js b/src/Data/Artifacts/ResolutionOfSojourner/ResolutionOfSojourner.js index ad27e527a8..15753874e8 100644 --- a/src/Data/Artifacts/ResolutionOfSojourner/ResolutionOfSojourner.js +++ b/src/Data/Artifacts/ResolutionOfSojourner/ResolutionOfSojourner.js @@ -26,7 +26,7 @@ let artifact = { }, 4: { text: "Increases Charged Attack CRIT Rate by 30%.", - stats: {} + stats: { char_atk_crit_rate: 30 } } } } diff --git a/src/Data/Artifacts/RetracingBolide/RetracingBolide.js b/src/Data/Artifacts/RetracingBolide/RetracingBolide.js index 87332eb375..02132b9bc1 100644 --- a/src/Data/Artifacts/RetracingBolide/RetracingBolide.js +++ b/src/Data/Artifacts/RetracingBolide/RetracingBolide.js @@ -26,7 +26,15 @@ let artifact = { }, 4: { text: "Gain an additional 40% Normal and Charged Attack DMG while under the protection of a shield.", - stats: {} + conditional: { + type: "artifact", + sourceKey: "RetracingBolide_4", + maxStack: 1, + stats: { + norm_atk_dmg: 40, + char_atk_dmg: 40 + } + } } } } diff --git a/src/Data/Artifacts/Scholar/Scholar.js b/src/Data/Artifacts/Scholar/Scholar.js index 45d1100813..8353a902f1 100644 --- a/src/Data/Artifacts/Scholar/Scholar.js +++ b/src/Data/Artifacts/Scholar/Scholar.js @@ -26,7 +26,6 @@ let artifact = { }, 4: { text: "Gaining Elemental Particles or Orbs gives 3 Energy to all party members who have a bow or a catalyst equipped. Can only occur once every 3s.", - stats: {} } } } diff --git a/src/Data/Artifacts/TheExile/TheExile.js b/src/Data/Artifacts/TheExile/TheExile.js index d408f13871..9a931f8047 100644 --- a/src/Data/Artifacts/TheExile/TheExile.js +++ b/src/Data/Artifacts/TheExile/TheExile.js @@ -26,7 +26,6 @@ let artifact = { }, 4: { text: "Using an Elemental Burst regenerates 2 Energy for other party members every 2s for 6s. This effect cannot stack.", - stats: {} } } } diff --git a/src/Data/Artifacts/ThunderingFury/ThunderingFury.js b/src/Data/Artifacts/ThunderingFury/ThunderingFury.js index 6f6476ec35..ff13287708 100644 --- a/src/Data/Artifacts/ThunderingFury/ThunderingFury.js +++ b/src/Data/Artifacts/ThunderingFury/ThunderingFury.js @@ -25,7 +25,11 @@ let artifact = { }, 4: { text: "Increases damage caused by Overloaded, Electro-Charged, and Superconduct DMG by 40%. Triggering such effects decreases Elemental Skill CD by 1s. Can only occur once every 0.8s.", - stats: {} + stats: { + overloaded_dmg: 40, + electro_charged_dmg: 40, + superconduct_dmg: 40 + } } } } diff --git a/src/Data/Artifacts/Thundersoother/Thundersoother.js b/src/Data/Artifacts/Thundersoother/Thundersoother.js index aa266be6cf..90d7c3502f 100644 --- a/src/Data/Artifacts/Thundersoother/Thundersoother.js +++ b/src/Data/Artifacts/Thundersoother/Thundersoother.js @@ -26,7 +26,14 @@ let artifact = { }, 4: { text: "Increases DMG against enemies affected by Electro by 35%.", - stats: {} + conditional: { + type: "artifact", + sourceKey: "Thundersoother_4", + maxStack: 1, + stats: { + dmg: 35, + } + } } } } diff --git a/src/Data/Artifacts/TinyMiracle/TinyMiracle.js b/src/Data/Artifacts/TinyMiracle/TinyMiracle.js index 116c78c43b..25d2f59a13 100644 --- a/src/Data/Artifacts/TinyMiracle/TinyMiracle.js +++ b/src/Data/Artifacts/TinyMiracle/TinyMiracle.js @@ -33,7 +33,57 @@ let artifact = { }, 4: { text: "Incoming elemental DMG increases corresponding Elemental RES by 30% for 10s. Can only occur once every 10s.", - stats: {} + conditional: [ + { + type: "artifact", + condition: "Anemo", + sourceKey: "TinyMiracle_4", + maxStack: 1, + stats: { + anemo_ele_res: 30 + } + }, { + type: "artifact", + condition: "Geo", + sourceKey: "TinyMiracle_4", + maxStack: 1, + stats: { + geo_ele_res: 30 + } + }, { + type: "artifact", + condition: "Electro", + sourceKey: "TinyMiracle_4", + maxStack: 1, + stats: { + electro_ele_res: 30 + } + }, { + type: "artifact", + condition: "Hydro", + sourceKey: "TinyMiracle_4", + maxStack: 1, + stats: { + hydro_ele_res: 30 + } + }, { + type: "artifact", + condition: "pyro", + sourceKey: "TinyMiracle_4", + maxStack: 1, + stats: { + pyro_ele_res: 30 + } + }, { + type: "artifact", + condition: "Cryo", + sourceKey: "TinyMiracle_4", + maxStack: 1, + stats: { + cryo_ele_res: 30 + } + }, + ] } } } diff --git a/src/Data/Artifacts/TravelingDoctor/TravelingDoctor.js b/src/Data/Artifacts/TravelingDoctor/TravelingDoctor.js index 6e138de948..cdb29bd65b 100644 --- a/src/Data/Artifacts/TravelingDoctor/TravelingDoctor.js +++ b/src/Data/Artifacts/TravelingDoctor/TravelingDoctor.js @@ -3,6 +3,7 @@ import plume from './Item_Traveling_Doctor\'s_Owl_Feather.png' import sands from './Item_Traveling_Doctor\'s_Pocket_Watch.png' import goblet from './Item_Traveling_Doctor\'s_Medicine_Pot.png' import circlet from './Item_Traveling_Doctor\'s_Handkerchief.png' +import WeaponPercent from '../../../Components/WeaponPercent' let artifact = { name: "Traveling Doctor", rarity: [3], pieces: { @@ -25,8 +26,7 @@ let artifact = { stats: { inc_heal: 20 } }, 4: { - text: "Using Elemental Burst restores 20% HP.", - stats: {} + text: (charFinalStats) => Using Elemental Burst restores 20% HP{WeaponPercent(20, charFinalStats.hp)}., } } } diff --git a/src/Data/Artifacts/ViridescentVenerer/ViridescentVenerer.js b/src/Data/Artifacts/ViridescentVenerer/ViridescentVenerer.js index fb4db496b4..0036a82cda 100644 --- a/src/Data/Artifacts/ViridescentVenerer/ViridescentVenerer.js +++ b/src/Data/Artifacts/ViridescentVenerer/ViridescentVenerer.js @@ -25,7 +25,7 @@ let artifact = { }, 4: { text: "Increases Swirl DMG by 60%. Decreases opponent's Elemental RES to the element infused in the Swirl by 40% for 10s.", - stats: {} + stats: { swirl_dmg: 60 }//TODO enemy resistance } } } diff --git a/src/Data/Artifacts/WanderersTroupe/WanderersTroupe.js b/src/Data/Artifacts/WanderersTroupe/WanderersTroupe.js index 3486a5cb46..d2e2257628 100644 --- a/src/Data/Artifacts/WanderersTroupe/WanderersTroupe.js +++ b/src/Data/Artifacts/WanderersTroupe/WanderersTroupe.js @@ -28,7 +28,14 @@ let artifact = { }, 4: { text: "Increases Charged Attack DMG by 35% if the character uses a Catalyst or Bow.", - stats: {} + conditional: { + type: "artifact", + sourceKey: "WanderersTroupe_4", + maxStack: 1, + stats: { + char_atk_dmg: 35, + } + } } } } diff --git a/src/Data/Weapons/Bow/SacrificialBow.js b/src/Data/Weapons/Bow/SacrificialBow.js index 3c871a9185..a44a13e914 100644 --- a/src/Data/Weapons/Bow/SacrificialBow.js +++ b/src/Data/Weapons/Bow/SacrificialBow.js @@ -12,5 +12,13 @@ const weapon = { subStatKey: "ener_rech", sub: [6.7, 7.7, 9.1, 10.4, 11.8, 11.8, 13.1, 14.5, 15.8, 17.2, 17.2, 18.5, 19.9, 19.9, 21.2, 22.6, 22.6, 23.9, 25.2, 25.2, 26.6, 27.9, 27.9, 29.3, 30.6], }, + conditional: { + type: "weapon", + sourceKey: "SacrificialBow", + maxStack: 1, + stats: () => ({ + red_cd: 100 + }) + } } export default weapon \ No newline at end of file diff --git a/src/Data/Weapons/Catalyst/SacrificialFragments.js b/src/Data/Weapons/Catalyst/SacrificialFragments.js index f4193fd69a..116865c10b 100644 --- a/src/Data/Weapons/Catalyst/SacrificialFragments.js +++ b/src/Data/Weapons/Catalyst/SacrificialFragments.js @@ -12,5 +12,13 @@ const weapon = { subStatKey: "ele_mas", sub: [48, 56, 65, 75, 85, 85, 95, 104, 114, 124, 124, 133, 143, 143, 153, 162, 162, 172, 182, 182, 191, 201, 201, 211, 221], }, + conditional: { + type: "weapon", + sourceKey: "SacrificialFragments", + maxStack: 1, + stats: () => ({ + red_cd: 100 + }) + } } export default weapon \ No newline at end of file diff --git a/src/Data/Weapons/Claymore/SacrificialGreatsword.js b/src/Data/Weapons/Claymore/SacrificialGreatsword.js index 72f0c9d028..8a2c9e234f 100644 --- a/src/Data/Weapons/Claymore/SacrificialGreatsword.js +++ b/src/Data/Weapons/Claymore/SacrificialGreatsword.js @@ -11,6 +11,14 @@ const weapon = { main: [44, 59, 79, 99, 119, 144, 165, 185, 205, 226, 252, 273, 293, 319, 340, 361, 387, 408, 429, 455, 476, 497, 523, 544, 565], subStatKey: "ener_rech", sub: [6.7, 7.7, 9.1, 10.4, 11.8, 11.8, 13.1, 14.5, 15.8, 17.2, 17.2, 18.5, 19.9, 19.9, 21.2, 22.6, 22.6, 23.9, 25.2, 25.2, 26.6, 27.9, 27.9, 29.3, 30.6], + }, + conditional: { + type: "weapon", + sourceKey: "SacrificialGreatsword", + maxStack: 1, + stats: () => ({ + red_cd: 100 + }) } } export default weapon \ No newline at end of file diff --git a/src/Data/Weapons/Sword/SacrificialSword.js b/src/Data/Weapons/Sword/SacrificialSword.js index aedeca9cb2..a46dde15da 100644 --- a/src/Data/Weapons/Sword/SacrificialSword.js +++ b/src/Data/Weapons/Sword/SacrificialSword.js @@ -11,6 +11,14 @@ const weapon = { main: [41, 54, 69, 84, 99, 125, 140, 155, 169, 184, 210, 224, 238, 264, 278, 293, 319, 333, 347, 373, 387, 401, 427, 440, 454], subStatKey: "ener_rech", sub: [13.3, 15.5, 18.2, 20.9, 23.6, 23.6, 26.3, 28.9, 31.6, 34.3, 34.3, 37, 39.7, 39.7, 42.4, 45.1, 45.1, 47.8, 50.5, 50.5, 53.2, 55.9, 55.9, 58.6, 61.3], + }, + conditional: { + type: "weapon", + sourceKey: "SacrificialSword", + maxStack: 1, + stats: () => ({ + red_cd: 100 + }) } } export default weapon \ No newline at end of file diff --git a/src/Home/art_editor.png b/src/Home/art_editor.png index 13594ee0ab..86e1230218 100644 Binary files a/src/Home/art_editor.png and b/src/Home/art_editor.png differ diff --git a/src/Stat.js b/src/Stat.js index 91f4f6ad3b..1169aca59b 100644 --- a/src/Stat.js +++ b/src/Stat.js @@ -72,18 +72,33 @@ const StatData = { inc_heal: { name: "Incoming Healing Bonus", unit: "%" }, pow_shield: { name: "Powerful Shield", unit: "%" }, red_cd: { name: "Reduce CD", unit: "%" }, - //helper stats + //auto norm_atk_dmg: { name: "Normal Attack DMG", unit: "%" }, char_atk_dmg: { name: "Charged Attack DMG", unit: "%" }, + norm_atk_crit_rate: { name: "Nomral Attack CRIT Rate", unit: "%" }, + char_atk_crit_rate: { name: "Charged Attack CRIT Rate", unit: "%" }, + //skill skill_dmg: { name: "Ele. Skill DMG", unit: "%" }, burst_dmg: { name: "Ele. Burst DMG", unit: "%" }, skill_crit_rate: { name: "Ele. Skill CRIT Rate", unit: "%" }, burst_crit_rate: { name: "Ele. Burst CRIT Rate", unit: "%" }, + skill_cd_red: { name: "Ele. Skill CD Red.", unit: "%" }, + burst_cd_red: { name: "Ele. Burst CD Red.", unit: "%" }, + crit_multi: { name: "Crit Multiplier" }, dmg: { name: "All DMG", unit: "%" },//general all damage increase move_spd: { name: "Movement SPD", unit: "%" }, atk_spd: { name: "ATK SPD", unit: "%" }, - weakspot_dmg: { name: "Weakspot DMG", unit: "%" } + weakspot_dmg: { name: "Weakspot DMG", unit: "%" }, + stamina_dec: { name: "Stamina Consmption Dec.", unit: "%" }, + //elemental + overloaded_dmg: { name: "Overloaded DMG", unit: "%" }, + electro_charged_dmg: { name: "Electro-Charged DMG", unit: "%" }, + superconduct_dmg: { name: "Superconduct DMG", unit: "%" }, + burning_dmg: { name: "Overloaded DMG", unit: "%" }, + vaporize_dmg: { name: "Vaporize DMG", unit: "%" }, + melt_dmg: { name: "Melt DMG", unit: "%" }, + swirl_dmg:{ name: "Swirl DMG", unit: "%" }, }; export { diff --git a/src/Util/ArtifactConditionals.js b/src/Util/ArtifactConditionals.js new file mode 100644 index 0000000000..b4c44ba2ba --- /dev/null +++ b/src/Util/ArtifactConditionals.js @@ -0,0 +1,23 @@ +export default class ArtifactConditionals { + constructor() { if (this instanceof ArtifactConditionals) throw Error('A static class cannot be instantiated.'); } + static getConditionalNum(artifactConditionals, setKey, setNumKey) { + if (artifactConditionals) + return artifactConditionals.find(charConditional => + charConditional.setKey === setKey && charConditional.setNumKey === setNumKey)?.conditionalNum || 0 + } + static setConditional(artifactConditionals, setKey, setNumKey, conditionalNum) { + if (!artifactConditionals) artifactConditionals = [] + let index = artifactConditionals.findIndex(artCond => artCond.setKey === setKey && artCond.setNumKey === setNumKey) + if (!conditionalNum && index >= 0) { + //setting conditionalNum to 0 deletes the element + artifactConditionals.splice(index, 1); + } else { + let newArtCond = { setKey, setNumKey, conditionalNum } + if (index >= 0) + artifactConditionals[index] = newArtCond + else + artifactConditionals.push(newArtCond) + } + return artifactConditionals + } +} \ No newline at end of file diff --git a/src/Util.js b/src/Util/Util.js similarity index 100% rename from src/Util.js rename to src/Util/Util.js diff --git a/src/Weapon/Weapon.js b/src/Weapon/Weapon.js index 0141490766..433ccabfc3 100644 --- a/src/Weapon/Weapon.js +++ b/src/Weapon/Weapon.js @@ -1,5 +1,5 @@ import { WeaponData, LevelNameData, WeaponLevelKeys, WeaponTypeData } from '../Data/WeaponData.js' -import { clamp } from '../Util.js'; +import { clamp } from '../Util/Util.js'; export default class Weapon { //do not instantiate. constructor() { if (this instanceof Weapon) throw Error('A static class cannot be instantiated.'); } @@ -34,7 +34,7 @@ export default class Weapon { if (Array.isArray(conditional)) { //multiple conditionals let selectedConditionalNum = conditionalNum - let selectedConditional + let selectedConditional = null for (const curConditional of conditional) { if (selectedConditionalNum > curConditional.maxStack) selectedConditionalNum -= curConditional.maxStack else {