mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-04-24 15:08:44 -05:00
Analyzer UI initial
This commit is contained in:
parent
59b3d28e58
commit
73e3ddf007
|
|
@ -2,4 +2,7 @@ export type {
|
|||
DistanceDamage,
|
||||
MainWeaponParams,
|
||||
SubWeaponParams,
|
||||
Stat,
|
||||
} from "./types";
|
||||
|
||||
export { useAnalyzeBuild } from "./useAnalyzeBuild";
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ export type ParamsJson = {
|
|||
subWeapons: Record<SubWeaponId, SubWeaponParams>;
|
||||
};
|
||||
|
||||
interface Stat {
|
||||
export interface Stat {
|
||||
value: number;
|
||||
baseValue: number;
|
||||
modifiedBy: Ability;
|
||||
|
|
|
|||
28
app/modules/analyzer/useAnalyzeBuild.ts
Normal file
28
app/modules/analyzer/useAnalyzeBuild.ts
Normal 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,
|
||||
};
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
37
app/styles/analyzer.css
Normal 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);
|
||||
}
|
||||
3
public/locales/en/analyzer.json
Normal file
3
public/locales/en/analyzer.json
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"stat.specialPoints": "Special points"
|
||||
}
|
||||
2
types/react-i18next.d.ts
vendored
2
types/react-i18next.d.ts
vendored
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user