From 980cad656dcb567a1e5e29fedef383ab9d91ad19 Mon Sep 17 00:00:00 2001 From: Fred Date: Sun, 27 Dec 2020 15:57:19 -0500 Subject: [PATCH] 2.4.0 - Added conditional stats to artifacts. Now you can calculate builds using temporary stats. - Added conditional stats to every artifact sets (if applicable) - Added navigation menu toggle for mobile devices - Small layout changes --- package.json | 2 +- src/App.js | 3 + src/Artifact/Artifact.js | 89 ++++++- src/Artifact/ArtifactBase.js | 3 +- src/Artifact/ArtifactDatabase.js | 2 +- src/Artifact/ArtifactDisplay.js | 2 +- src/Artifact/ArtifactEditor.js | 6 +- src/Build/BuildDisplay.js | 249 +++++++++++------- src/Character/Character.js | 27 +- src/Character/CharacterCard.js | 59 ++--- src/Character/CharacterDatabase.js | 2 +- src/Character/CharacterDisplay.js | 3 +- .../CharacterDisplay/CharacterArtifactPane.js | 45 +++- .../CharacterDisplay/CharacterOverviewPane.js | 83 ++---- src/Character/CharacterDisplayCard.js | 13 +- src/Components/ConditionalSelector.js | 71 +++++ src/Components/StatIcon.js | 14 +- src/Data/Artifacts/Adventurer/Adventurer.js | 4 +- .../Artifacts/ArchaicPetra/ArchaicPetra.js | 36 ++- src/Data/Artifacts/Berserker/Berserker.js | 11 +- .../BlizzardStrayer/BlizzardStrayer.js | 18 +- .../BloodstainedChivalry.js | 12 +- src/Data/Artifacts/BraveHeart/BraveHeart.js | 9 +- .../CrimsonWitchOfFlames.js | 15 +- .../Artifacts/DefendersWill/DefendersWill.js | 52 +++- src/Data/Artifacts/Gambler/Gambler.js | 9 +- .../GladiatorsFinale/GladiatorsFinale.js | 9 +- .../Artifacts/HeartOfDepth/HeartOfDepth.js | 10 +- src/Data/Artifacts/Instructor/Instructor.js | 9 +- src/Data/Artifacts/Lavawalker/Lavawalker.js | 9 +- src/Data/Artifacts/LuckyDog/LuckyDog.js | 1 - .../Artifacts/MaidenBeloved/MaidenBeloved.js | 9 +- .../Artifacts/MartialArtist/MartialArtist.js | 10 +- .../NoblesseOblige/NoblesseOblige.js | 9 +- .../PrayersForDestiny/PrayersForDestiny.js | 2 +- .../PrayersForIllumination.js | 2 +- .../PrayersForWisdom/PrayersForWisdom.js | 2 +- .../PrayersToSpringtime.js | 2 +- .../ResolutionOfSojourner.js | 2 +- .../RetracingBolide/RetracingBolide.js | 10 +- src/Data/Artifacts/Scholar/Scholar.js | 1 - src/Data/Artifacts/TheExile/TheExile.js | 1 - .../ThunderingFury/ThunderingFury.js | 6 +- .../Thundersoother/Thundersoother.js | 9 +- src/Data/Artifacts/TinyMiracle/TinyMiracle.js | 52 +++- .../TravelingDoctor/TravelingDoctor.js | 4 +- .../ViridescentVenerer/ViridescentVenerer.js | 2 +- .../WanderersTroupe/WanderersTroupe.js | 9 +- src/Data/Weapons/Bow/SacrificialBow.js | 8 + .../Weapons/Catalyst/SacrificialFragments.js | 8 + .../Weapons/Claymore/SacrificialGreatsword.js | 8 + src/Data/Weapons/Sword/SacrificialSword.js | 8 + src/Home/art_editor.png | Bin 83328 -> 83328 bytes src/Stat.js | 19 +- src/Util/ArtifactConditionals.js | 23 ++ src/{ => Util}/Util.js | 0 src/Weapon/Weapon.js | 4 +- 57 files changed, 795 insertions(+), 292 deletions(-) create mode 100644 src/Components/ConditionalSelector.js create mode 100644 src/Util/ArtifactConditionals.js rename src/{ => Util}/Util.js (100%) 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 13594ee0ab3ee1baf58f8bfdee13fbb2a356b852..86e123021874ab494d2dabc95e06af6459d21f2f 100644 GIT binary patch delta 24428 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 delta 24332 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 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 {