diff --git a/app/modules/analyzer/useAnalyzeBuild.ts b/app/modules/analyzer/useAnalyzeBuild.ts index e53a23436..f7df8fc21 100644 --- a/app/modules/analyzer/useAnalyzeBuild.ts +++ b/app/modules/analyzer/useAnalyzeBuild.ts @@ -1,20 +1,30 @@ +import { useSearchParams } from "@remix-run/react"; import * as React from "react"; +import { EMPTY_BUILD } from "~/constants"; import { type BuildAbilitiesTupleWithUnknown, type MainWeaponId, mainWeaponIds, + abilities, } from "../in-game-lists"; +import { AbilityType, AbilityWithUnknown } from "../in-game-lists/types"; import { buildStats } from "./stats"; +const UNKNOWN_SHORT = "U"; + export function useAnalyzeBuild() { - const [build, setBuild] = React.useState([ - ["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"], - ["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"], - ["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"], - ]); - const [mainWeaponId, setMainWeaponId] = React.useState( - mainWeaponIds[0] - ); + const [searchParams, setSearchParams] = useSearchParams(); + + const mainWeaponId = validatedWeaponIdFromSearchParams(searchParams); + const build = validatedBuildFromSearchParams(searchParams); + + const setMainWeaponId = (weaponId: MainWeaponId) => + setSearchParams({ weapon: String(weaponId), build: serializeBuild(build) }); + const setBuild = (newBuild: BuildAbilitiesTupleWithUnknown) => + setSearchParams({ + build: serializeBuild(newBuild), + weapon: String(mainWeaponId), + }); const analyzed = React.useMemo( () => buildStats({ build, weaponSplId: mainWeaponId }), @@ -29,3 +39,74 @@ export function useAnalyzeBuild() { analyzed, }; } + +function serializeBuild(build: BuildAbilitiesTupleWithUnknown) { + return build + .flat() + .map((ability) => (ability === "UNKNOWN" ? UNKNOWN_SHORT : ability)) + .join(","); +} + +function validatedWeaponIdFromSearchParams( + searchParams: URLSearchParams +): MainWeaponId { + const weaponId = searchParams.get("weapon") + ? Number(searchParams.get("weapon")) + : null; + + if (mainWeaponIds.includes(weaponId as any)) { + return weaponId as MainWeaponId; + } + + return mainWeaponIds[0]; +} + +function validatedBuildFromSearchParams( + searchParams: URLSearchParams +): BuildAbilitiesTupleWithUnknown { + const abilitiesArr = searchParams.get("build") + ? searchParams.get("build")?.split(",") + : null; + + if (!abilitiesArr) return EMPTY_BUILD; + + try { + return [ + [ + validateAbility(["STACKABLE", "HEAD_MAIN_ONLY"], abilitiesArr[0]), + validateAbility(["STACKABLE"], abilitiesArr[1]), + validateAbility(["STACKABLE"], abilitiesArr[2]), + validateAbility(["STACKABLE"], abilitiesArr[3]), + ], + [ + validateAbility(["STACKABLE", "CLOTHES_MAIN_ONLY"], abilitiesArr[4]), + validateAbility(["STACKABLE"], abilitiesArr[5]), + validateAbility(["STACKABLE"], abilitiesArr[6]), + validateAbility(["STACKABLE"], abilitiesArr[7]), + ], + [ + validateAbility(["STACKABLE", "SHOES_MAIN_ONLY"], abilitiesArr[8]), + validateAbility(["STACKABLE"], abilitiesArr[9]), + validateAbility(["STACKABLE"], abilitiesArr[10]), + validateAbility(["STACKABLE"], abilitiesArr[11]), + ], + ]; + } catch (err) { + return EMPTY_BUILD; + } +} + +function validateAbility( + legalTypes: Array, + ability?: string +): AbilityWithUnknown { + if (!ability) throw new Error("Ability missing"); + if (ability === UNKNOWN_SHORT) return "UNKNOWN"; + + const abilityObj = abilities.find( + (a) => a.name === ability && legalTypes.includes(a.type) + ); + if (abilityObj) return abilityObj.name; + + throw new Error("Invalid ability"); +}