diff --git a/app/modules/analyzer/index.ts b/app/modules/analyzer/index.ts index 31afacf81..129d5339d 100644 --- a/app/modules/analyzer/index.ts +++ b/app/modules/analyzer/index.ts @@ -5,6 +5,7 @@ export type { Stat, AnalyzedBuild, SpecialEffectType, + HitPoints, } from "./types"; export { useAnalyzeBuild } from "./useAnalyzeBuild"; diff --git a/app/modules/analyzer/objectHitPoints.ts b/app/modules/analyzer/objectHitPoints.ts new file mode 100644 index 000000000..21f71320e --- /dev/null +++ b/app/modules/analyzer/objectHitPoints.ts @@ -0,0 +1,53 @@ +import invariant from "tiny-invariant"; +import { BIG_BUBBLER_ID, CRAB_TANK_ID, SPLASH_WALL_ID } from "../in-game-lists"; +import { specialDeviceHp, specialFieldHp, subStats } from "./stats"; +import type { + AbilityPoints, + HitPoints, + SpecialWeaponParams, + SubWeaponParams, +} from "./types"; +import weaponParams from "./weapon-params.json"; + +const PLACEHOLDER_HP = 1000; + +export const objectHitPoints = (abilityPoints: AbilityPoints): HitPoints => { + const Wsb_Shield = subStats({ + abilityPoints, + subWeaponParams: weaponParams.subWeapons[SPLASH_WALL_ID] as SubWeaponParams, + }).subHp?.value; + const GreatBarrier_Barrier = specialFieldHp({ + abilityPoints, + specialWeaponParams: weaponParams.specialWeapons[ + BIG_BUBBLER_ID + ] as SpecialWeaponParams, + })?.value; + const GreatBarrier_WeakPoint = specialDeviceHp({ + abilityPoints, + specialWeaponParams: weaponParams.specialWeapons[ + BIG_BUBBLER_ID + ] as SpecialWeaponParams, + })?.value; + + invariant(Wsb_Shield); + invariant(GreatBarrier_Barrier); + invariant(GreatBarrier_WeakPoint); + + return { + BulletUmbrellaCanopyNormal: PLACEHOLDER_HP, // ?? needs defaults + BulletUmbrellaCanopyWide: weaponParams.mainWeapons[6010].CanopyHP, + BulletUmbrellaCanopyCompact: weaponParams.mainWeapons[6020].CanopyHP, + Wsb_Shield, + Bomb_TorpedoBullet: PLACEHOLDER_HP, // ?? + Chariot: weaponParams.specialWeapons[CRAB_TANK_ID].ArmorHP, + Gachihoko_Barrier: PLACEHOLDER_HP, // ?? + GreatBarrier_Barrier, + GreatBarrier_WeakPoint, + InkRail: PLACEHOLDER_HP, // ?? + NiceBall_Armor: PLACEHOLDER_HP, // ?? + ShockSonar: PLACEHOLDER_HP, // ?? + Sponge_Versus: PLACEHOLDER_HP, // ?? + Wsb_Flag: PLACEHOLDER_HP, // ?? + Wsb_Sprinkler: PLACEHOLDER_HP, // ?? + }; +}; diff --git a/app/modules/analyzer/stats.ts b/app/modules/analyzer/stats.ts index 39812f140..185f21289 100644 --- a/app/modules/analyzer/stats.ts +++ b/app/modules/analyzer/stats.ts @@ -765,7 +765,9 @@ const SUB_WEAPON_STATS = [ }, { analyzedBuildKey: "subHp", abilityValuesKey: "MaxHP", type: "HP" }, ] as const; -function subStats(args: StatFunctionInput) { +export function subStats( + args: Pick +) { const result: Partial = {}; const SUB_STATS_KEY = "BRU"; @@ -1126,8 +1128,8 @@ function specialPaintRadius( }; } -function specialFieldHp( - args: StatFunctionInput +export function specialFieldHp( + args: Pick ): AnalyzedBuild["stats"]["specialFieldHp"] { if (!hasEffect({ key: "MaxFieldHP", weapon: args.specialWeaponParams })) { return; @@ -1150,8 +1152,8 @@ function specialFieldHp( }; } -function specialDeviceHp( - args: StatFunctionInput +export function specialDeviceHp( + args: Pick ): AnalyzedBuild["stats"]["specialDeviceHp"] { if (!hasEffect({ key: "MaxHP", weapon: args.specialWeaponParams })) { return; diff --git a/app/modules/analyzer/types.ts b/app/modules/analyzer/types.ts index 3021f2e9d..1803e4da2 100644 --- a/app/modules/analyzer/types.ts +++ b/app/modules/analyzer/types.ts @@ -164,6 +164,8 @@ export type DamageType = typeof DAMAGE_TYPE[number]; export type DamageReceiver = typeof DAMAGE_RECEIVERS[number]; +export type HitPoints = Record; + export interface Damage { value: number; type: DamageType; diff --git a/app/modules/analyzer/useObjectDamage.ts b/app/modules/analyzer/useObjectDamage.ts index 561782976..c49fa468f 100644 --- a/app/modules/analyzer/useObjectDamage.ts +++ b/app/modules/analyzer/useObjectDamage.ts @@ -2,6 +2,7 @@ import { useSearchParams } from "@remix-run/react"; import { type MainWeaponId } from "../in-game-lists"; import { damageTypeToWeaponType } from "./constants"; import { damageTypeToMultipliers, fallbackRates } from "./damageMultipliers"; +import { objectHitPoints } from "./objectHitPoints"; import { buildStats } from "./stats"; import { validatedWeaponIdFromSearchParams } from "./utils"; @@ -53,5 +54,7 @@ export function useObjectDamage() { mainWeaponId, handleChange, multipliers, + damages: analyzed.stats.damages, + hitPoints: objectHitPoints(new Map()), }; } diff --git a/app/modules/analyzer/weapon-params.json b/app/modules/analyzer/weapon-params.json index 247507468..a01399359 100644 --- a/app/modules/analyzer/weapon-params.json +++ b/app/modules/analyzer/weapon-params.json @@ -1246,6 +1246,7 @@ } }, "12": { + "ArmorHP": 5000, "overwrites": { "SpecialDurationFrame": { "High": 660, diff --git a/app/routes/object-damage.tsx b/app/routes/object-damage.tsx index c26bfedfd..90d89e3e9 100644 --- a/app/routes/object-damage.tsx +++ b/app/routes/object-damage.tsx @@ -1,22 +1,54 @@ import { WeaponCombobox } from "~/components/Combobox"; +import { Image } from "~/components/Image"; import { Main } from "~/components/Main"; -import { useObjectDamage } from "~/modules/analyzer"; -import type { MainWeaponId } from "~/modules/in-game-lists"; -import { type SendouRouteHandle } from "~/utils/remix"; +import { + DAMAGE_RECEIVERS, + useObjectDamage, + type HitPoints, +} from "~/modules/analyzer"; +import { + type MainWeaponId, + BIG_BUBBLER_ID, + BOOYAH_BOMB_ID, + CRAB_TANK_ID, + SPLASH_WALL_ID, + SQUID_BEAKON_ID, + TORPEDO_ID, + WAVE_BREAKER_ID, + SPRINKLER_ID, +} from "~/modules/in-game-lists"; +import { + mainWeaponImageUrl, + modeImageUrl, + specialWeaponImageUrl, + subWeaponImageUrl, +} from "~/utils/urls"; +import styles from "~/styles/object-damage.css"; +import type { LinksFunction } from "@remix-run/node"; +import type { SendouRouteHandle } from "~/utils/remix"; +import type { DamageReceiver } from "~/modules/analyzer/types"; + +export const links: LinksFunction = () => { + return [{ rel: "stylesheet", href: styles }]; +}; export const handle: SendouRouteHandle = { i18n: ["weapons"], }; export default function ObjectDamagePage() { - const { mainWeaponId, handleChange, multipliers } = useObjectDamage(); + const { mainWeaponId, handleChange, multipliers, damages, hitPoints } = + useObjectDamage(); if (process.env.NODE_ENV !== "development") { return
WIP :)
; } + console.log(JSON.stringify(multipliers, null, 2)); + console.log(JSON.stringify(damages, null, 2)); + return ( -
+
-
{JSON.stringify(multipliers, null, 2)}
+
+ +
); } + +const damageReceiverImages: Record = { + Bomb_TorpedoBullet: subWeaponImageUrl(TORPEDO_ID), + Chariot: specialWeaponImageUrl(CRAB_TANK_ID), + Gachihoko_Barrier: modeImageUrl("RM"), + GreatBarrier_Barrier: specialWeaponImageUrl(BIG_BUBBLER_ID), + GreatBarrier_WeakPoint: specialWeaponImageUrl(BIG_BUBBLER_ID), + InkRail: modeImageUrl("CB"), + NiceBall_Armor: specialWeaponImageUrl(BOOYAH_BOMB_ID), + ShockSonar: specialWeaponImageUrl(WAVE_BREAKER_ID), + Sponge_Versus: modeImageUrl("CB"), + Wsb_Flag: subWeaponImageUrl(SQUID_BEAKON_ID), + Wsb_Shield: subWeaponImageUrl(SPLASH_WALL_ID), + Wsb_Sprinkler: subWeaponImageUrl(SPRINKLER_ID), + BulletUmbrellaCanopyNormal: mainWeaponImageUrl(6000), + BulletUmbrellaCanopyWide: mainWeaponImageUrl(6010), + BulletUmbrellaCanopyCompact: mainWeaponImageUrl(6020), +}; +function DamageReceiversHeader({ hitPoints }: { hitPoints: HitPoints }) { + return ( + <> + {DAMAGE_RECEIVERS.map((receiver, i) => ( + <> + +
{hitPoints[receiver]}
+ + ))} + + ); +} diff --git a/app/styles/object-damage.css b/app/styles/object-damage.css new file mode 100644 index 000000000..fc072f924 --- /dev/null +++ b/app/styles/object-damage.css @@ -0,0 +1,6 @@ +.object-damage__grid { + display: grid; + width: 100%; + gap: var(--s-3); + grid-template-columns: repeat(var(--columns-count), 1fr); +} diff --git a/scripts/create-analyzer-json.ts b/scripts/create-analyzer-json.ts index 9577fe7de..75aab23cc 100644 --- a/scripts/create-analyzer-json.ts +++ b/scripts/create-analyzer-json.ts @@ -314,7 +314,10 @@ function parametersToSpecialWeaponResult(params: any) { resultUnwrapped["SplashAroundPaintRadius"] = undefined; } - return { overwrites: resultUnwrapped }; + return { + ArmorHP: params["WeaponSpChariotParam"]?.["ArmorHP"], + overwrites: resultUnwrapped, + }; } function unwrapSubSpecialSpecUpList(result: any) {