mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-10 05:06:15 -05:00
Ink consumption table for analyzer
This commit is contained in:
@@ -4,6 +4,7 @@ export type {
|
||||
SubWeaponParams,
|
||||
Stat,
|
||||
AnalyzedBuild,
|
||||
FullInkTankOption,
|
||||
} from "./types";
|
||||
|
||||
export { useAnalyzeBuild } from "./useAnalyzeBuild";
|
||||
|
||||
@@ -205,7 +205,7 @@ function inkConsumeTypeToParamsKeys(
|
||||
case "SWING":
|
||||
return ["InkConsume_SwingParam", "InkConsume_WeaponSwingParam"];
|
||||
case "TAP_SHOT":
|
||||
return ["InkConsumeMinCharge", "InkConsumeMinCharge_ChargeParam"];
|
||||
return ["InkConsumeMinCharge"];
|
||||
case "FULL_CHARGE":
|
||||
return ["InkConsumeFullCharge", "InkConsumeFullCharge_ChargeParam"];
|
||||
case "HORIZONTAL_SWING":
|
||||
@@ -216,10 +216,6 @@ function inkConsumeTypeToParamsKeys(
|
||||
return ["InkConsume_SideStepParam"];
|
||||
case "SHIELD_LAUNCH":
|
||||
return ["InkConsumeUmbrella_WeaponShelterCanopyParam"];
|
||||
case "ROLL_MAX":
|
||||
return ["InkConsumeMaxPerFrame_WeaponRollParam"];
|
||||
case "ROLL_MIN":
|
||||
return ["InkConsumeMinPerFrame_WeaponRollParam"];
|
||||
default: {
|
||||
assertUnreachable(type);
|
||||
}
|
||||
|
||||
@@ -21,10 +21,6 @@ export interface MainWeaponParams {
|
||||
// xxx: morph with another swing one below
|
||||
/** How much ink one swing of brush consumes? */
|
||||
InkConsume_WeaponSwingParam?: number;
|
||||
/** Lower bound of ink consumed per frame when rolling with roller/brush */
|
||||
InkConsumeMaxPerFrame_WeaponRollParam?: number;
|
||||
/** Upper bound of ink consumed per frame when rolling with roller/brush */
|
||||
InkConsumeMinPerFrame_WeaponRollParam?: number;
|
||||
/** How much ink one vertical swing of roller consumes? */
|
||||
InkConsume_WeaponVerticalSwingParam?: number;
|
||||
/** How much ink one horizontal swing of roller consumes? */
|
||||
@@ -38,9 +34,8 @@ export interface MainWeaponParams {
|
||||
InkConsume_WeaponShelterShotgunParam?: number;
|
||||
/** How much ink a dualie dodge roll consumes? */
|
||||
InkConsume_SideStepParam?: number;
|
||||
// xxx: some explanations... the below two are for splatana/bow
|
||||
/** How much ink a fully charger Splatana shot consumes? */
|
||||
InkConsumeFullCharge_ChargeParam?: number;
|
||||
InkConsumeMinCharge_ChargeParam?: number;
|
||||
//InkConsumeMidCharge_ChargeParam?: number;
|
||||
// xxx: what are these?
|
||||
// SpeedInkConsumeMax_WeaponRollParam?: number;
|
||||
@@ -106,10 +101,14 @@ export const INK_CONSUME_TYPES = [
|
||||
"FULL_CHARGE",
|
||||
"SHIELD_LAUNCH",
|
||||
"DUALIE_ROLL",
|
||||
"ROLL_MIN",
|
||||
"ROLL_MAX",
|
||||
] as const;
|
||||
|
||||
export interface FullInkTankOption {
|
||||
subsUsed: number;
|
||||
value: number;
|
||||
type: InkConsumeType;
|
||||
}
|
||||
|
||||
export interface AnalyzedBuild {
|
||||
weapon: {
|
||||
subWeaponSplId: SubWeaponId;
|
||||
@@ -120,10 +119,6 @@ export interface AnalyzedBuild {
|
||||
/** % of special charge saved when dying */
|
||||
specialSavedAfterDeath: Stat;
|
||||
subWeaponWhiteInkFrames?: number;
|
||||
fullInkTankOptions: Array<{
|
||||
subsUsed: number;
|
||||
value: number;
|
||||
type: InkConsumeType;
|
||||
}>;
|
||||
fullInkTankOptions: Array<FullInkTankOption>;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { WeaponCombobox } from "~/components/Combobox";
|
||||
import { Main } from "~/components/Main";
|
||||
import type { Stat } from "~/modules/analyzer";
|
||||
import { useAnalyzeBuild } from "~/modules/analyzer";
|
||||
import type { AnalyzedBuild } from "~/modules/analyzer";
|
||||
import type { AnalyzedBuild, FullInkTankOption } from "~/modules/analyzer";
|
||||
import type { MainWeaponId } from "~/modules/in-game-lists";
|
||||
import styles from "~/styles/analyzer.css";
|
||||
import { Image } from "~/components/Image";
|
||||
@@ -33,19 +33,21 @@ export default function BuildAnalyzerPage() {
|
||||
<div className="analyzer__container">
|
||||
<div className="stack lg items-center">
|
||||
<div className="stack sm items-center w-full">
|
||||
<WeaponCombobox
|
||||
inputName="weapon"
|
||||
initialWeaponId={mainWeaponId}
|
||||
onChange={(opt) =>
|
||||
opt && setMainWeaponId(Number(opt.value) as MainWeaponId)
|
||||
}
|
||||
className="w-full-important"
|
||||
/>
|
||||
<div className="w-full">
|
||||
<WeaponCombobox
|
||||
inputName="weapon"
|
||||
initialWeaponId={mainWeaponId}
|
||||
onChange={(opt) =>
|
||||
opt && setMainWeaponId(Number(opt.value) as MainWeaponId)
|
||||
}
|
||||
className="w-full-important"
|
||||
/>
|
||||
</div>
|
||||
<KitCards analyzed={analyzed} />
|
||||
</div>
|
||||
<AbilitiesSelector selectedAbilities={build} onChange={setBuild} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="stack md">
|
||||
<StatCategory title="Special">
|
||||
<StatCard
|
||||
stat={analyzed.stats.specialPoint}
|
||||
@@ -58,6 +60,12 @@ export default function BuildAnalyzerPage() {
|
||||
suffix="%"
|
||||
/>
|
||||
</StatCategory>
|
||||
<StatCategory
|
||||
title="Actions per ink tank"
|
||||
containerClassName="analyzer__consumption-table-container"
|
||||
>
|
||||
<ConsumptionTable options={analyzed.stats.fullInkTankOptions} />
|
||||
</StatCategory>
|
||||
</div>
|
||||
</div>
|
||||
</Main>
|
||||
@@ -94,18 +102,21 @@ function KitCards({ analyzed }: { analyzed: AnalyzedBuild }) {
|
||||
function StatCategory({
|
||||
title,
|
||||
children,
|
||||
containerClassName = "analyzer__stat-collection",
|
||||
}: {
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
containerClassName?: string;
|
||||
}) {
|
||||
return (
|
||||
<details>
|
||||
<summary className="analyzer__summary">{title}</summary>
|
||||
<div className="analyzer__stat-collection">{children}</div>
|
||||
<div className={containerClassName}>{children}</div>
|
||||
</details>
|
||||
);
|
||||
}
|
||||
|
||||
// xxx: cobra kai style
|
||||
function StatCard({
|
||||
title,
|
||||
stat,
|
||||
@@ -136,3 +147,41 @@ function StatCard({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ConsumptionTable({ options }: { options: Array<FullInkTankOption> }) {
|
||||
const { t } = useTranslation("analyzer");
|
||||
const maxSubsToUse = Math.max(...options.map((opt) => opt.subsUsed));
|
||||
const types = Array.from(new Set(options.map((opt) => opt.type)));
|
||||
|
||||
return (
|
||||
<>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{t("stat.consumption.bomb")}</th>
|
||||
{types.map((type) => (
|
||||
<th key={type}>{t(`stat.consumption.${type}`)}</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{new Array(maxSubsToUse + 1).fill(null).map((_, subsUsed) => {
|
||||
return (
|
||||
<tr key={subsUsed} className="bg-darker-important">
|
||||
<td>×{subsUsed}</td>
|
||||
{options
|
||||
.filter((opt) => opt.subsUsed === subsUsed)
|
||||
.map((opt) => {
|
||||
return <td key={opt.type}>{opt.value}</td>;
|
||||
})}
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
<div className="analyzer__consumption-table-explanation">
|
||||
{t("consumptionExplanation", { maxSubsToUse })}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -57,3 +57,18 @@
|
||||
.analyzer__stat-card__value {
|
||||
font-size: var(--fonts-xs);
|
||||
}
|
||||
|
||||
.analyzer__consumption-table-container {
|
||||
width: 100%;
|
||||
padding: var(--s-3);
|
||||
background-color: var(--bg-darker);
|
||||
border-radius: var(--rounded);
|
||||
margin-block-start: var(--s-4);
|
||||
padding-block: var(--s-2);
|
||||
}
|
||||
|
||||
.analyzer__consumption-table-explanation {
|
||||
margin-top: var(--s-2);
|
||||
color: var(--text-lighter);
|
||||
font-size: var(--fonts-xxs);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,10 @@
|
||||
color: var(--theme-success);
|
||||
}
|
||||
|
||||
.bg-darker-important {
|
||||
background-color: var(--bg-darker) !important;
|
||||
}
|
||||
|
||||
.font-semi-bold {
|
||||
font-weight: var(--semi-bold);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
{
|
||||
"stat.specialPoints": "Points to special",
|
||||
"stat.specialLost": "Special lost when splatted"
|
||||
"stat.specialLost": "Special lost when splatted",
|
||||
"stat.consumption.bomb": "Bombs",
|
||||
"stat.consumption.NORMAL": "Shots",
|
||||
"stat.consumption.SWING": "Swings",
|
||||
"stat.consumption.VERTICAL_SWING": "Vertical swings",
|
||||
"stat.consumption.HORIZONTAL_SWING": "Horizontal swings",
|
||||
"stat.consumption.TAP_SHOT": "Tap shots",
|
||||
"stat.consumption.FULL_CHARGE": "Fully charged shots",
|
||||
"stat.consumption.SHIELD_LAUNCH": "Shield launches",
|
||||
"stat.consumption.DUALIE_ROLL": "Dodge Rolls",
|
||||
"consumptionExplanation": "This chart shows the amount of actions left to perform with main weapon after using sub 0-{{maxSubsToUse}} times. Max amount of consecutive subs to use with full ink tank is {{maxSubsToUse}}."
|
||||
}
|
||||
|
||||
@@ -109,10 +109,6 @@ function parametersToMainWeaponResult(
|
||||
InkConsumeFullCharge: params["WeaponParam"]?.["InkConsumeFullCharge"],
|
||||
InkConsumeMinCharge: params["WeaponParam"]?.["InkConsumeMinCharge"],
|
||||
InkConsume_WeaponSwingParam: params["WeaponSwingParam"]?.["InkConsume"],
|
||||
InkConsumeMaxPerFrame_WeaponRollParam:
|
||||
params["WeaponRollParam"]?.["InkConsumeMaxPerFrame"],
|
||||
InkConsumeMinPerFrame_WeaponRollParam:
|
||||
params["WeaponRollParam"]?.["InkConsumeMinPerFrame"],
|
||||
InkConsume_WeaponVerticalSwingParam:
|
||||
params["WeaponVerticalSwingParam"]?.["InkConsume"],
|
||||
InkConsume_WeaponWideSwingParam:
|
||||
@@ -131,13 +127,6 @@ function parametersToMainWeaponResult(
|
||||
params["spl__WeaponStringerParam"]?.["ChargeParam"]?.[
|
||||
"InkConsumeFullCharge"
|
||||
],
|
||||
InkConsumeMinCharge_ChargeParam:
|
||||
params["spl__WeaponSaberParam"]?.["ChargeParam"]?.[
|
||||
"InkConsumeMinCharge"
|
||||
] ??
|
||||
params["spl__WeaponStringerParam"]?.["ChargeParam"]?.[
|
||||
"InkConsumeMinCharge"
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user