analyzer graph view

This commit is contained in:
Sendou 2020-05-23 20:32:46 +03:00
parent 33d0fb5766
commit 79aba396f8
9 changed files with 471 additions and 191 deletions

View File

@ -2168,9 +2168,9 @@
}
},
"@testing-library/user-event": {
"version": "10.3.4",
"resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-10.3.4.tgz",
"integrity": "sha512-ZuqR2cBAOArvBdPiIjmAKaZdFAtZSH6Oz2I5+uHonMXRyR0uTtz2fTWyPuiu4JATdK7gLIq1s02BMJGWgmh1lA==",
"version": "10.3.5",
"resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-10.3.5.tgz",
"integrity": "sha512-9Jg5pg12099EoOb4Z/+UNtCFpFIblD6INkgeWuV/AjUiYP+s/u6aYGPaLrUYioJ8duQ3V421B29GVE/3JELjCQ==",
"requires": {
"@babel/runtime": "^7.9.6"
},

View File

@ -12,7 +12,7 @@
"@sendou/react-sketch": "^0.5.2",
"@testing-library/jest-dom": "^5.8.0",
"@testing-library/react": "^10.0.4",
"@testing-library/user-event": "^10.3.4",
"@testing-library/user-event": "^10.3.5",
"@types/jest": "^25.2.3",
"@types/reach__router": "^1.3.5",
"@types/react": "^16.9.35",

View File

@ -7,19 +7,26 @@ import { Build } from "../../types"
import PageHeader from "../common/PageHeader"
import WeaponSelector from "../common/WeaponSelector"
import BuildStats from "./BuildStats"
import EditableBuild from "./EditableBuild"
import EditableBuilds from "./EditableBuilds"
import MyThemeContext from "../../themeContext"
const defaultBuild: Partial<Build> = {
weapon: "Splattershot Jr.",
headgear: ["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"],
clothing: ["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"],
shoes: ["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"],
}
const BuildAnalyzerPage: React.FC<RouteComponentProps> = () => {
const { themeColor } = useContext(MyThemeContext)
const [build, setBuild] = useState<Partial<Build>>({
weapon: "Splattershot Jr.",
headgear: ["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"],
clothing: ["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"],
shoes: ["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"],
})
const [hideExtra, setHideExtra] = useState(true)
const [build, setBuild] = useState<Partial<Build>>(defaultBuild)
const [otherBuild, setOtherBuild] = useState<Partial<Build>>(defaultBuild)
const [showOther, setShowOther] = useState(false)
const [otherFocused, setOtherFocused] = useState(false)
const [hideExtra, setHideExtra] = useState(false)
const explanations = useAbilityEffects(build)
const otherExplanations = useAbilityEffects(otherBuild)
console.log("explanations", explanations)
@ -34,11 +41,24 @@ const BuildAnalyzerPage: React.FC<RouteComponentProps> = () => {
<WeaponSelector
value={build.weapon}
label=""
setValue={(weapon) => setBuild({ ...build, weapon })}
setValue={(weapon) => {
setBuild({ ...build, weapon })
setOtherBuild({ ...otherBuild, weapon })
}}
menuIsOpen={!build.weapon}
/>
</Box>
<EditableBuild build={build} setBuild={setBuild} />
<EditableBuilds
build={build}
otherBuild={otherBuild}
setBuild={otherFocused ? setOtherBuild : setBuild}
showOther={showOther}
setShowOther={setShowOther}
changeFocus={() => {
setOtherFocused(!otherFocused)
}}
otherFocused={otherFocused}
/>
<Box my="1em">
<FormLabel htmlFor="show-all">Hide stats at base value</FormLabel>
<Switch
@ -52,6 +72,7 @@ const BuildAnalyzerPage: React.FC<RouteComponentProps> = () => {
<BuildStats
build={build}
explanations={explanations}
otherExplanations={showOther ? otherExplanations : undefined}
hideExtra={hideExtra}
/>
</Box>

View File

@ -1,23 +1,44 @@
import React, { useContext } from "react"
import React, { useContext, useState } from "react"
import { Explanation } from "../../hooks/useAbilityEffects"
import MyThemeContext from "../../themeContext"
import { Ability, Build } from "../../types"
import { mainOnlyAbilities } from "../../utils/lists"
import { Progress, Box, Flex } from "@chakra-ui/core"
import {
Progress,
Box,
Flex,
Popover,
PopoverTrigger,
PopoverContent,
} from "@chakra-ui/core"
import AbilityIcon from "../builds/AbilityIcon"
import {
LineChart,
CartesianGrid,
XAxis,
YAxis,
Legend,
Tooltip,
Line,
ResponsiveContainer,
} from "recharts"
import IconButton from "../elements/IconButton"
import { FaChartLine, FaInfo, FaQuestion } from "react-icons/fa"
interface BuildStatsProps {
explanations: Explanation[]
otherExplanations?: Explanation[]
build: Partial<Build>
hideExtra: boolean
}
const BuildStats: React.FC<BuildStatsProps> = ({
explanations,
otherExplanations,
build,
hideExtra,
}) => {
const { themeColor, themeColorWithShade } = useContext(MyThemeContext)
const { colorMode, themeColorWithShade } = useContext(MyThemeContext)
const abilityArrays: Ability[][] = [
build.headgear ?? [],
build.clothing ?? [],
@ -39,11 +60,37 @@ const BuildStats: React.FC<BuildStatsProps> = ({
)
const BuildStat: React.FC<{
title: String
title: string
effect: string
otherEffect?: string
ability: Ability
info?: string
progressBarValue: number
}> = ({ title, effect, ability, progressBarValue = 0 }) => {
otherProgressBarValue?: number
getEffect?: (ap: number) => number
}> = ({
title,
effect,
ability,
otherEffect,
otherProgressBarValue,
getEffect,
info,
progressBarValue = 0,
}) => {
const [showChart, setShowChart] = useState(false)
const { themeColorHex, darkerBgColor, themeColorWithShade } = useContext(
MyThemeContext
)
const getData = () => {
const toReturn = []
for (let i = 0; i < 58; i++) {
toReturn.push({ name: `${i}AP`, [title]: getEffect!(i) })
}
return toReturn
}
return (
<>
<Flex justifyContent="space-between">
@ -51,43 +98,136 @@ const BuildStats: React.FC<BuildStatsProps> = ({
<Box mr="0.5em">
<AbilityIcon ability={ability} size="TINY" />
</Box>
<IconButton
icon={FaChartLine}
onClick={() => setShowChart(!showChart)}
/>
{title}
{info && (
<Popover trigger="hover" placement="top-start">
<PopoverTrigger>
<Box>
<Box
color={themeColorWithShade}
ml="0.2em"
as={FaQuestion}
mb="0.2em"
/>
</Box>
</PopoverTrigger>
<PopoverContent
zIndex={4}
p="0.5em"
bg={darkerBgColor}
border="0"
>
{info}
</PopoverContent>
</Popover>
)}
</Flex>
<Box
fontWeight="bold"
color={themeColorWithShade}
color={`orange.${colorMode === "dark" ? "200" : "500"}`}
alignSelf="flex-end"
>
{effect}
</Box>
</Flex>
<Progress
color={themeColor}
height="32px"
color="orange"
height={otherEffect ? "16px" : "32px"}
value={progressBarValue}
hasStripe
isAnimated
/>
{otherEffect && (
<>
<Progress
color="blue"
height="16px"
value={otherProgressBarValue}
hasStripe
isAnimated
/>
<Flex justifyContent="space-between">
<Box />
<Box
fontWeight="bold"
color={`blue.${colorMode === "dark" ? "200" : "500"}`}
alignSelf="flex-end"
>
{otherEffect}
</Box>
</Flex>
</>
)}
{getEffect && showChart && (
<Box my="1em" ml="-26px">
<ResponsiveContainer width="100%" height={300}>
<LineChart data={getData()}>
<CartesianGrid strokeDasharray="3 3" color="#000" />
<XAxis dataKey="name" />
<YAxis />
<Tooltip
contentStyle={{
background: darkerBgColor,
borderRadius: "5px",
border: 0,
}}
/>
<Legend />
<Line
type="monotone"
dataKey={title}
stroke={themeColorHex}
dot={false}
/>
</LineChart>
</ResponsiveContainer>
</Box>
)}
</>
)
}
const explanationsToUse = !hideExtra
/*const explanationsToUse = !hideExtra
? explanations
: explanations.filter((e) => e.effectFromMax !== 0)
: explanations.filter((e) => e.effectFromMax !== 0)*/
return (
<>
{explanationsToUse.map((explanation) => (
<Box my="1em" key={explanation.title}>
<BuildStat
title={explanation.title}
effect={explanation.effect}
ability={explanation.ability}
progressBarValue={explanation.effectFromMax}
/>
</Box>
))}
{explanations.map((_explanation, index) => {
const explanation = explanations[index]
const otherExplanation = otherExplanations
? otherExplanations[index]
: undefined
if (
explanation.effectFromMax === 0 &&
(!otherExplanation || otherExplanation.effectFromMax === 0) &&
hideExtra
) {
return null
}
return (
<Box my="1em" key={explanation.title}>
<BuildStat
title={explanation.title}
ability={explanation.ability}
effect={explanation.effect}
progressBarValue={
explanation.effectFromMaxActual ?? explanation.effectFromMax
}
otherEffect={otherExplanation?.effect}
otherProgressBarValue={otherExplanation?.effectFromMax}
getEffect={explanation.getEffect}
info={explanation.info}
/>
</Box>
)
})}
</>
)
}

View File

@ -1,125 +0,0 @@
import React from "react"
import AbilityButtons from "../user/AbilityButtons"
import ViewSlots from "../builds/ViewSlots"
import { Box, Flex } from "@chakra-ui/core"
import {
Build,
Ability,
HeadOnlyAbility,
ClothingOnlyAbility,
ShoesOnlyAbility,
StackableAbility,
} from "../../types"
import {
headOnlyAbilities,
clothingOnlyAbilities,
shoesOnlyAbilities,
} from "../../utils/lists"
import { Explanation } from "../../hooks/useAbilityEffects"
interface EditableBuildProps {
build: Partial<Build>
setBuild: React.Dispatch<React.SetStateAction<Partial<Build>>>
}
const EditableBuild: React.FC<EditableBuildProps> = ({ build, setBuild }) => {
const handleChange = (value: Object) => setBuild({ ...build, ...value })
const handleAbilityButtonClick = (ability: Ability) => {
if (headOnlyAbilities.indexOf(ability as any) !== -1) {
if (build.headgear![0] === "UNKNOWN") {
handleChange({
headgear: [
ability,
build.headgear![1],
build.headgear![2],
build.headgear![3],
],
})
}
} else if (clothingOnlyAbilities.indexOf(ability as any) !== -1) {
if (build.clothing![0] === "UNKNOWN") {
handleChange({
clothing: [
ability,
build.clothing![1],
build.clothing![2],
build.clothing![3],
],
})
}
} else if (shoesOnlyAbilities.indexOf(ability as any) !== -1) {
if (build.shoes![0] === "UNKNOWN") {
handleChange({
shoes: [ability, build.shoes![1], build.shoes![2], build.shoes![3]],
})
}
} else {
const headI = build.headgear!.indexOf("UNKNOWN")
if (headI !== -1) {
const copy = build.headgear!.slice()
copy[headI] = ability as HeadOnlyAbility | StackableAbility
handleChange({
headgear: copy,
})
return
}
const clothingI = build.clothing!.indexOf("UNKNOWN")
if (clothingI !== -1) {
const copy = build.clothing!.slice()
copy[clothingI] = ability as ClothingOnlyAbility | StackableAbility
handleChange({
clothing: copy,
})
return
}
const shoesI = build.shoes!.indexOf("UNKNOWN")
if (shoesI !== -1) {
const copy = build.shoes!.slice()
copy[shoesI] = ability as ShoesOnlyAbility | StackableAbility
handleChange({
shoes: copy,
})
}
}
}
const handleClickBuildAbility = (
slot: "HEAD" | "CLOTHING" | "SHOES",
index: number
) => {
if (slot === "HEAD") {
const copy = build.headgear!.slice()
copy[index] = "UNKNOWN"
handleChange({
headgear: copy,
})
} else if (slot === "CLOTHING") {
const copy = build.clothing!.slice()
copy[index] = "UNKNOWN"
handleChange({
clothing: copy,
})
} else {
const copy = build.shoes!.slice()
copy[index] = "UNKNOWN"
handleChange({
shoes: copy,
})
}
}
return (
<>
<ViewSlots build={build} onAbilityClick={handleClickBuildAbility} />
<Box mt="1em">
<AbilityButtons
onClick={(ability) => handleAbilityButtonClick(ability)}
/>
</Box>
</>
)
}
export default EditableBuild

View File

@ -0,0 +1,189 @@
import React from "react"
import AbilityButtons from "../user/AbilityButtons"
import ViewSlots from "../builds/ViewSlots"
import { Box, Flex } from "@chakra-ui/core"
import {
Build,
Ability,
HeadOnlyAbility,
ClothingOnlyAbility,
ShoesOnlyAbility,
StackableAbility,
} from "../../types"
import {
headOnlyAbilities,
clothingOnlyAbilities,
shoesOnlyAbilities,
} from "../../utils/lists"
import Button from "../elements/Button"
import { FaPlus, FaMinus } from "react-icons/fa"
interface EditableBuildsProps {
build: Partial<Build>
otherBuild: Partial<Build>
setBuild: React.Dispatch<React.SetStateAction<Partial<Build>>>
showOther: boolean
setShowOther: React.Dispatch<React.SetStateAction<boolean>>
otherFocused: boolean
changeFocus: () => void
}
const EditableBuilds: React.FC<EditableBuildsProps> = ({
build,
otherBuild,
setBuild,
showOther,
setShowOther,
otherFocused,
changeFocus,
}) => {
const buildToEdit = otherFocused ? otherBuild : build
const handleChange = (value: Object) => setBuild({ ...buildToEdit, ...value })
const handleAbilityButtonClick = (ability: Ability) => {
if (headOnlyAbilities.indexOf(ability as any) !== -1) {
if (buildToEdit.headgear![0] === "UNKNOWN") {
handleChange({
headgear: [
ability,
buildToEdit.headgear![1],
buildToEdit.headgear![2],
buildToEdit.headgear![3],
],
})
}
} else if (clothingOnlyAbilities.indexOf(ability as any) !== -1) {
if (buildToEdit.clothing![0] === "UNKNOWN") {
handleChange({
clothing: [
ability,
buildToEdit.clothing![1],
buildToEdit.clothing![2],
buildToEdit.clothing![3],
],
})
}
} else if (shoesOnlyAbilities.indexOf(ability as any) !== -1) {
if (buildToEdit.shoes![0] === "UNKNOWN") {
handleChange({
shoes: [
ability,
buildToEdit.shoes![1],
buildToEdit.shoes![2],
buildToEdit.shoes![3],
],
})
}
} else {
const headI = buildToEdit.headgear!.indexOf("UNKNOWN")
if (headI !== -1) {
const copy = buildToEdit.headgear!.slice()
copy[headI] = ability as HeadOnlyAbility | StackableAbility
handleChange({
headgear: copy,
})
return
}
const clothingI = buildToEdit.clothing!.indexOf("UNKNOWN")
if (clothingI !== -1) {
const copy = buildToEdit.clothing!.slice()
copy[clothingI] = ability as ClothingOnlyAbility | StackableAbility
handleChange({
clothing: copy,
})
return
}
const shoesI = buildToEdit.shoes!.indexOf("UNKNOWN")
if (shoesI !== -1) {
const copy = buildToEdit.shoes!.slice()
copy[shoesI] = ability as ShoesOnlyAbility | StackableAbility
handleChange({
shoes: copy,
})
}
}
}
const handleClickBuildAbility = (
slot: "HEAD" | "CLOTHING" | "SHOES",
index: number
) => {
if (slot === "HEAD") {
const copy = buildToEdit.headgear!.slice()
copy[index] = "UNKNOWN"
handleChange({
headgear: copy,
})
} else if (slot === "CLOTHING") {
const copy = buildToEdit.clothing!.slice()
copy[index] = "UNKNOWN"
handleChange({
clothing: copy,
})
} else {
const copy = buildToEdit.shoes!.slice()
copy[index] = "UNKNOWN"
handleChange({
shoes: copy,
})
}
}
return (
<>
<Flex justifyContent="space-evenly" flexWrap="wrap" mb="1em">
<Flex flexDirection="column">
<ViewSlots
build={build}
onAbilityClick={!otherFocused ? handleClickBuildAbility : undefined}
m="1em"
cursor={!otherFocused ? undefined : "not-allowed"}
/>
{showOther && (
<Button
disabled={!otherFocused}
color="orange"
onClick={() => changeFocus()}
>
{!otherFocused ? "Editing" : "Edit"}
</Button>
)}
</Flex>
{showOther && (
<Flex flexDirection="column">
<ViewSlots
build={otherBuild}
onAbilityClick={
otherFocused ? handleClickBuildAbility : undefined
}
m="1em"
cursor={otherFocused ? undefined : "not-allowed"}
/>
<Button
disabled={otherFocused}
color="blue"
onClick={() => changeFocus()}
>
{otherFocused ? "Editing" : "Edit"}
</Button>
</Flex>
)}
</Flex>
<Button
icon={showOther ? FaMinus : FaPlus}
onClick={() => setShowOther(!showOther)}
my="1em"
>
{showOther ? "Stop comparing" : "Compare"}
</Button>
<Box mt="1em">
<AbilityButtons
onClick={(ability) => handleAbilityButtonClick(ability)}
/>
</Box>
</>
)
}
export default EditableBuilds

View File

@ -1,16 +1,20 @@
import React from "react"
import { Build, Ability } from "../../types"
import AbilityIcon from "./AbilityIcon"
import { Flex, Box } from "@chakra-ui/core"
import { Flex, Box, BoxProps } from "@chakra-ui/core"
interface ViewSlotsProps {
build: Partial<Build>
onAbilityClick?: (gear: "HEAD" | "CLOTHING" | "SHOES", index: number) => void
}
const ViewSlots: React.FC<ViewSlotsProps> = ({ build, onAbilityClick }) => {
const ViewSlots: React.FC<ViewSlotsProps & BoxProps> = ({
build,
onAbilityClick,
...props
}) => {
return (
<>
<Box {...props}>
<Flex alignItems="center" justifyContent="center">
{(
build.headgear ??
@ -76,7 +80,7 @@ const ViewSlots: React.FC<ViewSlotsProps> = ({ build, onAbilityClick }) => {
</Box>
))}
</Flex>
</>
</Box>
)
}

View File

@ -13,6 +13,7 @@ interface ButtonProps {
size?: "xs" | "sm" | "lg" | "md"
icon?: IconType
width?: string
color?: string
outlined?: boolean
disabled?: boolean
loading?: boolean
@ -26,6 +27,7 @@ const Button: React.FC<ButtonProps & ChakraButtonProps> = ({
disabled,
loading,
width,
color,
outlined = false,
...props
}) => {
@ -33,7 +35,7 @@ const Button: React.FC<ButtonProps & ChakraButtonProps> = ({
return (
<ChakraButton
variant={outlined ? "outline" : "solid"}
variantColor={themeColor}
variantColor={color ?? themeColor}
leftIcon={icon}
onClick={onClick}
size={size}

View File

@ -8,33 +8,10 @@ export interface Explanation {
title: string
effect: string
effectFromMax: number
effectFromMaxActual?: number
ability: Ability
info?: string
}
interface WeaponDataFromJson {
InkSaverLv?: "Middle" | "High" | string
InkSaverType?: "A" | "B" | "C" | "D" | string
Sub?: string
Special?: string
mInkConsume?: number
mInkConsumeRepeat?: number
mFullChargeInkConsume?: number
mMinChargeInkConsume?: number
mInkConsumeSplashJump?: number
mInkConsumeSplashStand?: number
mSideStepInkConsume?: number
mInkConsumeUmbrella?: number
ShotMoveVelType?: "A" | "B" | "C" | "D" | "E" | string
MoveVelLv?: "Low" | "Middle" | "High" | string
SpecialCost?: number
//mBurst_PaintR?: number
//mBurst_PaintRMid?: number
//mBurst_PaintRHigh?: number
//mTargetInCircleRadius?: number
//mTargetInCircleRadiusMid?: number
//mTargetInCircleRadiusHigh?: number
//mPaintGauge_SpecialFrm?: number
getEffect?: (ap: number) => number
}
function buildToAP(build: Partial<Build>) {
@ -119,7 +96,12 @@ export default function useAbilityEffects(build: Partial<Build>) {
title,
effect: `${parseFloat((1 / (mInkConsume * effect[0])).toFixed(2))}`,
effectFromMax: effect[1],
effectFromMaxActual: (getEffect(highMidLow, 57)[0] / effect[0]) * 100,
ability: "ISM" as Ability,
getEffect: (ap: number) =>
parseFloat(
(1 / (mInkConsume * getEffect(highMidLow, ap)[0])).toFixed(2)
),
})
}
@ -131,7 +113,12 @@ export default function useAbilityEffects(build: Partial<Build>) {
(1 / (mInkConsumeRepeat * effect[0])).toFixed(2)
)}`,
effectFromMax: effect[1],
effectFromMaxActual: (getEffect(highMidLow, 57)[0] / effect[0]) * 100,
ability: "ISM" as Ability,
getEffect: (ap: number) =>
parseFloat(
(1 / (mInkConsumeRepeat * getEffect(highMidLow, ap)[0])).toFixed(2)
),
})
}
@ -143,7 +130,15 @@ export default function useAbilityEffects(build: Partial<Build>) {
(1 / (mFullChargeInkConsume * effect[0])).toFixed(2)
)}`,
effectFromMax: effect[1],
effectFromMaxActual: (getEffect(highMidLow, 57)[0] / effect[0]) * 100,
ability: "ISM" as Ability,
getEffect: (ap: number) =>
parseFloat(
(
1 /
(mFullChargeInkConsume * getEffect(highMidLow, ap)[0])
).toFixed(2)
),
})
}
@ -155,7 +150,14 @@ export default function useAbilityEffects(build: Partial<Build>) {
(1 / (mMinChargeInkConsume * effect[0])).toFixed(2)
)}`,
effectFromMax: effect[1],
effectFromMaxActual: (getEffect(highMidLow, 57)[0] / effect[0]) * 100,
ability: "ISM" as Ability,
getEffect: (ap: number) =>
parseFloat(
(1 / (mMinChargeInkConsume * getEffect(highMidLow, ap)[0])).toFixed(
2
)
),
})
}
@ -172,7 +174,15 @@ export default function useAbilityEffects(build: Partial<Build>) {
(1 / (mInkConsumeSplashJump * effect[0])).toFixed(2)
)}`,
effectFromMax: effect[1],
effectFromMaxActual: (getEffect(highMidLow, 57)[0] / effect[0]) * 100,
ability: "ISM" as Ability,
getEffect: (ap: number) =>
parseFloat(
(
1 /
(mInkConsumeSplashJump * getEffect(highMidLow, ap)[0])
).toFixed(2)
),
})
} else if (mInkConsumeSplashJump && mInkConsumeSplashStand) {
toReturn.push({
@ -181,7 +191,15 @@ export default function useAbilityEffects(build: Partial<Build>) {
(1 / (mInkConsumeSplashStand * effect[0])).toFixed(2)
)}`,
effectFromMax: effect[1],
effectFromMaxActual: (getEffect(highMidLow, 57)[0] / effect[0]) * 100,
ability: "ISM" as Ability,
getEffect: (ap: number) =>
parseFloat(
(
1 /
(mInkConsumeSplashStand * getEffect(highMidLow, ap)[0])
).toFixed(2)
),
})
toReturn.push({
@ -190,31 +208,57 @@ export default function useAbilityEffects(build: Partial<Build>) {
(1 / (mInkConsumeSplashJump * effect[0])).toFixed(2)
)}`,
effectFromMax: effect[1],
effectFromMaxActual: (getEffect(highMidLow, 57)[0] / effect[0]) * 100,
ability: "ISM" as Ability,
getEffect: (ap: number) =>
parseFloat(
(
1 /
(mInkConsumeSplashJump * getEffect(highMidLow, ap)[0])
).toFixed(2)
),
})
}
const mSideStepInkConsume = buildWeaponData.mSideStepInkConsume
if (mSideStepInkConsume) {
toReturn.push({
title: "Dodge rolls per ink tank",
title: "Dodge roll ink consumption",
effect: `${parseFloat(
(mSideStepInkConsume * effect[0] * 100).toFixed(2)
)}% of ink tank`,
effectFromMax: effect[1],
effectFromMaxActual: parseFloat(
(mSideStepInkConsume * effect[0] * 100).toFixed(2)
),
ability: "ISM" as Ability,
getEffect: (ap: number) =>
parseFloat(
(mSideStepInkConsume * getEffect(highMidLow, ap)[0] * 100).toFixed(
2
)
),
})
}
const mInkConsumeUmbrella = buildWeaponData.mInkConsumeUmbrella
if (mInkConsumeUmbrella) {
toReturn.push({
title: "Brella launch ink consumption",
title: "Brella shield launch ink consumption",
effect: `${parseFloat(
(mInkConsumeUmbrella * effect[0] * 100).toFixed(2)
)}% of ink tank`,
effectFromMax: effect[1],
effectFromMaxActual: parseFloat(
(mInkConsumeUmbrella * effect[0] * 100).toFixed(2)
),
ability: "ISM" as Ability,
getEffect: (ap: number) =>
parseFloat(
(mInkConsumeUmbrella * getEffect(highMidLow, ap)[0] * 100).toFixed(
2
)
),
})
}
@ -246,6 +290,9 @@ export default function useAbilityEffects(build: Partial<Build>) {
(effect[0] * inkConsumption * 100).toFixed(2)
)}% of ink tank`,
effectFromMax: effect[1],
effectFromMaxActual: parseFloat(
(effect[0] * inkConsumption * 100).toFixed(2)
),
ability: "ISS" as Ability,
},
]
@ -279,6 +326,8 @@ export default function useAbilityEffects(build: Partial<Build>) {
(Math.ceil(effectSquid[0]) / 60).toFixed(2)
)} seconds)`,
effectFromMax: effectSquid[1],
effectFromMaxActual:
(effectSquid[0] / getEffect(highMidLowSquid, 0)[0]) * 100,
ability: "REC" as Ability,
},
/*{