Analyzer UI initial

This commit is contained in:
Kalle 2022-09-10 21:06:19 +03:00
parent 59b3d28e58
commit 73e3ddf007
9 changed files with 195 additions and 11 deletions

View File

@ -2,4 +2,7 @@ export type {
DistanceDamage,
MainWeaponParams,
SubWeaponParams,
Stat,
} from "./types";
export { useAnalyzeBuild } from "./useAnalyzeBuild";

View File

@ -4,7 +4,12 @@ import type {
} from "~/modules/in-game-lists";
import type { AnalyzedBuild, StatFunctionInput } from "./types";
import invariant from "tiny-invariant";
import { buildToAbilityPoints, weaponParams } from "./utils";
import {
abilityPointsToEffect,
apFromMap,
buildToAbilityPoints,
weaponParams,
} from "./utils";
export function buildStats({
build,
@ -49,13 +54,25 @@ function shotsPerInkTank(
return {};
}
function specialPoint(
args: StatFunctionInput
): AnalyzedBuild["stats"]["specialPoint"] {
function specialPoint({
abilityPoints,
mainWeaponParams,
}: StatFunctionInput): AnalyzedBuild["stats"]["specialPoint"] {
const SPECIAL_POINT_ABILITY = "SCU";
const effect = abilityPointsToEffect({
abilityPoints: apFromMap({
abilityPoints: abilityPoints,
ability: SPECIAL_POINT_ABILITY,
}),
key: "IncreaseRt_Special",
weapon: mainWeaponParams,
});
return {
baseValue: args.mainWeaponParams.SpecialPoint,
modifiedBy: "SCU",
value: args.mainWeaponParams.SpecialPoint, // xxx:
baseValue: mainWeaponParams.SpecialPoint,
modifiedBy: SPECIAL_POINT_ABILITY,
value: Math.ceil(mainWeaponParams.SpecialPoint / effect),
};
}

View File

@ -80,7 +80,7 @@ export type ParamsJson = {
subWeapons: Record<SubWeaponId, SubWeaponParams>;
};
interface Stat {
export interface Stat {
value: number;
baseValue: number;
modifiedBy: Ability;

View File

@ -0,0 +1,28 @@
import * as React from "react";
import type {
BuildAbilitiesTupleWithUnknown,
MainWeaponId,
} from "../in-game-lists";
import { buildStats } from "./stats";
export function useAnalyzeBuild() {
const [build, setBuild] = React.useState<BuildAbilitiesTupleWithUnknown>([
["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"],
["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"],
["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"],
]);
const [weaponId, setWeaponId] = React.useState<MainWeaponId>(0);
const analyzed = React.useMemo(
() => buildStats({ build, weaponSplId: 0 }),
[build]
);
return {
build,
setBuild,
weaponId,
setWeaponId,
analyzed,
};
}

View File

@ -1,6 +1,6 @@
// xxx: rename file to something more accurate
import type { BuildAbilitiesTupleWithUnknown } from "../in-game-lists";
import type { Ability, BuildAbilitiesTupleWithUnknown } from "../in-game-lists";
import weaponParamsJson from "./weapon-params.json";
import abilityValuesJson from "./ability-values.json";
import type { AbilityPoints, MainWeaponParams, ParamsJson } from "./types";
@ -26,6 +26,16 @@ export function buildToAbilityPoints(build: BuildAbilitiesTupleWithUnknown) {
return result;
}
export function apFromMap({
abilityPoints,
ability,
}: {
abilityPoints: AbilityPoints;
ability: Ability;
}) {
return abilityPoints.get(ability) ?? 0;
}
function abilityValues({
key,
weapon,
@ -87,5 +97,7 @@ export function abilityPointsToEffect({
const percentage = calculateAbilityPointToPercent(abilityPoints) / 100.0;
const result = low + (high - low) * lerpN(slope, percentage);
return [result, lerpN(slope, percentage) * 100];
// xxx: is this needed?
//return [result, lerpN(slope, percentage) * 100];
return result;
}

View File

@ -1,7 +1,89 @@
import { type LinksFunction } from "@remix-run/node";
import { useTranslation } from "react-i18next";
import { AbilitiesSelector } from "~/components/AbilitiesSelector";
import { Ability } from "~/components/Ability";
import { WeaponCombobox } from "~/components/Combobox";
import { Main } from "~/components/Main";
import type { Stat } from "~/modules/analyzer";
import { useAnalyzeBuild } from "~/modules/analyzer";
import styles from "~/styles/analyzer.css";
export const links: LinksFunction = () => {
return [{ rel: "stylesheet", href: styles }];
};
export const handle = {
i18n: ["weapons", "analyzer"],
};
export default function BuildAnalyzerPage() {
const { t } = useTranslation("analyzer");
const { build, setBuild, weaponId, analyzed } = useAnalyzeBuild();
if (process.env.NODE_ENV === "production") return <Main>Coming soon :)</Main>;
return <Main>hellou</Main>;
return (
<Main>
<div className="analyzer__container">
<div className="stack lg items-center">
<WeaponCombobox inputName="weapon-1" initialWeaponId={weaponId} />
<AbilitiesSelector selectedAbilities={build} onChange={setBuild} />
</div>
<div>
<StatCategory title="Special">
<StatCollection
stats={[
{
title: t("stat.specialPoints"),
stat: analyzed.stats.specialPoint,
},
]}
/>
</StatCategory>
</div>
</div>
</Main>
);
}
function StatCategory({
title,
children,
}: {
title: string;
children: React.ReactNode;
}) {
return (
<details>
<summary className="analyzer__summary">{title}</summary>
{children}
</details>
);
}
function StatCollection({
stats,
}: {
stats: Array<{ title: string; stat: Stat }>;
}) {
return (
<div className="analyzer__stat-collection">
{stats.map(({ title, stat }) => (
<div key={title} className="analyzer__stat-card">
<h4 className="analyzer__stat-card__title">{title}</h4>
{stat.value !== stat.baseValue && (
<div className="analyzer__stat-card__value">
Current: {stat.value}
</div>
)}
<div className="analyzer__stat-card__value text-lighter">
Base: {stat.baseValue}
</div>
<div className="stack items-center mt-4">
<Ability ability={stat.modifiedBy} size="TINY" />
</div>
</div>
))}
</div>
);
}

37
app/styles/analyzer.css Normal file
View File

@ -0,0 +1,37 @@
.analyzer__container {
display: grid;
gap: var(--s-10);
grid-template-columns: 1fr 2fr;
}
.analyzer__summary {
background-color: var(--bg-lighter);
border-radius: var(--rounded);
font-size: var(--fonts-md);
font-weight: var(--bold);
padding-block: var(--s-2);
padding-inline: var(--s-3);
}
.analyzer__stat-collection {
display: flex;
flex-wrap: wrap;
gap: var(--s-2);
margin-block-start: var(--s-4);
}
.analyzer__stat-card {
width: max-content;
height: max-content;
padding: var(--s-2);
background-color: var(--bg-darker);
border-radius: var(--rounded);
}
.analyzer__stat-card__title {
font-size: var(--fonts-sm);
}
.analyzer__stat-card__value {
font-size: var(--fonts-xs);
}

View File

@ -0,0 +1,3 @@
{
"stat.specialPoints": "Special points"
}

View File

@ -10,6 +10,7 @@ import type calendar from "../public/locales/en/calendar.json";
import type weapons from "../public/locales/en/weapons.json";
import type gear from "../public/locales/en/gear.json";
import type builds from "../public/locales/en/builds.json";
import type analyzer from "../public/locales/en/analyzer.json";
declare module "react-i18next" {
interface CustomTypeOptions {
@ -25,6 +26,7 @@ declare module "react-i18next" {
weapons: typeof weapons;
gear: typeof gear;
builds: typeof builds;
analyzer: typeof analyzer;
};
}
}