KitCards for analyzer

This commit is contained in:
Kalle
2022-09-12 19:31:33 +03:00
parent 1328acd160
commit ef1b89675d
15 changed files with 105 additions and 25 deletions

View File

@@ -9,7 +9,7 @@ import type {
} from "~/modules/in-game-lists";
import type { BuildAbilitiesTuple } from "~/modules/in-game-lists/types";
import { databaseTimestampToDate } from "~/utils/dates";
import { gearImageUrl, modeImageUrl, weaponImageUrl } from "~/utils/urls";
import { gearImageUrl, modeImageUrl, mainWeaponImageUrl } from "~/utils/urls";
import { Ability } from "./Ability";
import { Button, LinkButton } from "./Button";
import { FormWithConfirm } from "./FormWithConfirm";
@@ -128,7 +128,7 @@ export function BuildCard({
{weapons.map((weaponSplId) => (
<div key={weaponSplId} className="build__weapon">
<Image
path={weaponImageUrl(weaponSplId)}
path={mainWeaponImageUrl(weaponSplId)}
alt={t(`weapons:${weaponSplId}` as any)}
title={t(`weapons:${weaponSplId}` as any)}
height={36}

View File

@@ -12,7 +12,7 @@ import {
shoesGearIds,
mainWeaponIds,
} from "~/modules/in-game-lists";
import { gearImageUrl, weaponImageUrl } from "~/utils/urls";
import { gearImageUrl, mainWeaponImageUrl } from "~/utils/urls";
import { Image } from "./Image";
const MAX_RESULTS_SHOWN = 6;
@@ -36,6 +36,7 @@ interface ComboboxProps<T> {
onChange?: (selectedOption?: ComboboxOption<T>) => void;
}
// xxx: should clear on click
export function Combobox<T extends Record<string, string | null | number>>({
options,
inputName,
@@ -201,7 +202,7 @@ export function WeaponCombobox({
const idToWeapon = (id: typeof mainWeaponIds[number]) => ({
value: String(id),
label: t(`MAIN_${id}`),
imgPath: weaponImageUrl(id),
imgPath: mainWeaponImageUrl(id),
});
return (

View File

@@ -1,4 +1,4 @@
import type { Ability } from "~/modules/in-game-lists";
import type { Ability, MainWeaponId } from "~/modules/in-game-lists";
import type allTags from "../routes/calendar/tags.json";
export interface User {
@@ -133,7 +133,7 @@ export interface Build {
export interface BuildWeapon {
buildId: number;
weaponSplId: number;
weaponSplId: MainWeaponId;
}
export type GearType = "HEAD" | "CLOTHES" | "SHOES";

View File

@@ -3,6 +3,7 @@ export type {
MainWeaponParams,
SubWeaponParams,
Stat,
AnalyzedBuild,
} from "./types";
export { useAnalyzeBuild } from "./useAnalyzeBuild";

View File

@@ -1,12 +1,13 @@
import type {
Ability,
MainWeaponId,
SpecialWeaponId,
SubWeaponId,
} from "~/modules/in-game-lists";
export interface MainWeaponParams {
subWeaponId: SubWeaponId;
specialWeaponId: number;
specialWeaponId: SpecialWeaponId;
internalName: string;
/** Replacing default values of the ability json for this specific weapon */
overwrites?: Record<string, Partial<Record<"High" | "Mid" | "Low", number>>>;
@@ -110,8 +111,8 @@ type InkConsumePercentageType =
export interface AnalyzedBuild {
weapon: {
subWeaponSplId: number;
specialWeaponSplId: number;
subWeaponSplId: SubWeaponId;
specialWeaponSplId: SpecialWeaponId;
};
stats: {
specialPoint: Stat;

View File

@@ -12,20 +12,20 @@ export function useAnalyzeBuild() {
["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"],
["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"],
]);
const [weaponId, setWeaponId] = React.useState<MainWeaponId>(
const [mainWeaponId, setMainWeaponId] = React.useState<MainWeaponId>(
mainWeaponIds[0]
);
const analyzed = React.useMemo(
() => buildStats({ build, weaponSplId: weaponId }),
[build, weaponId]
() => buildStats({ build, weaponSplId: mainWeaponId }),
[build, mainWeaponId]
);
return {
build,
setBuild,
weaponId,
setWeaponId,
mainWeaponId,
setMainWeaponId,
analyzed,
};
}

View File

@@ -1,6 +1,6 @@
export { stages } from "./stages";
export { modes, modesShort } from "./modes";
export { mainWeaponIds, subWeaponIds } from "./weapon-ids";
export { mainWeaponIds, subWeaponIds, specialWeaponIds } from "./weapon-ids";
export { headGearIds, clothesGearIds, shoesGearIds } from "./gear-ids";
export { abilitiesShort, abilities } from "./abilities";
export type {
@@ -12,4 +12,5 @@ export type {
BuildAbilitiesTupleWithUnknown,
MainWeaponId,
SubWeaponId,
SpecialWeaponId,
} from "./types";

View File

@@ -1,7 +1,11 @@
import type { abilities } from "./abilities";
import type { modes } from "./modes";
import type { stages } from "./stages";
import type { subWeaponIds, mainWeaponIds } from "./weapon-ids";
import type {
subWeaponIds,
mainWeaponIds,
specialWeaponIds,
} from "./weapon-ids";
export type ModeShort = typeof modes[number]["short"];
@@ -13,6 +17,7 @@ export type AbilityType = typeof abilities[number]["type"];
export type MainWeaponId = typeof mainWeaponIds[number];
export type SubWeaponId = typeof subWeaponIds[number];
export type SpecialWeaponId = typeof specialWeaponIds[number];
export type BuildAbilitiesTuple = [
head: [main: Ability, s1: Ability, s2: Ability, s3: Ability],

View File

@@ -8,3 +8,7 @@ export const mainWeaponIds = [
export const subWeaponIds = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
] as const;
export const specialWeaponIds = [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
] as const;

View File

@@ -6,8 +6,11 @@ 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 { MainWeaponId } from "~/modules/in-game-lists";
import styles from "~/styles/analyzer.css";
import { Image } from "~/components/Image";
import { specialWeaponImageUrl, subWeaponImageUrl } from "~/utils/urls";
export const links: LinksFunction = () => {
return [{ rel: "stylesheet", href: styles }];
@@ -19,7 +22,7 @@ export const handle = {
export default function BuildAnalyzerPage() {
const { t } = useTranslation("analyzer");
const { build, setBuild, weaponId, setWeaponId, analyzed } =
const { build, setBuild, mainWeaponId, setMainWeaponId, analyzed } =
useAnalyzeBuild();
if (process.env.NODE_ENV === "production") return <Main>Coming soon :)</Main>;
@@ -28,14 +31,16 @@ export default function BuildAnalyzerPage() {
<Main>
<div className="analyzer__container">
<div className="stack lg items-center">
<div>
<div className="stack sm items-center w-full">
<WeaponCombobox
inputName="weapon"
initialWeaponId={weaponId}
initialWeaponId={mainWeaponId}
onChange={(opt) =>
opt && setWeaponId(Number(opt.value) as MainWeaponId)
opt && setMainWeaponId(Number(opt.value) as MainWeaponId)
}
className="w-full-important"
/>
<KitCards analyzed={analyzed} />
</div>
<AbilitiesSelector selectedAbilities={build} onChange={setBuild} />
</div>
@@ -58,6 +63,33 @@ export default function BuildAnalyzerPage() {
);
}
function KitCards({ analyzed }: { analyzed: AnalyzedBuild }) {
const { t } = useTranslation("weapons");
return (
<div className="analyzer__kit-cards">
<div className="analyzer__kit-card">
<Image
path={subWeaponImageUrl(analyzed.weapon.subWeaponSplId)}
width={20}
height={20}
alt={t(`SUB_${analyzed.weapon.subWeaponSplId}`)}
/>
{t(`SUB_${analyzed.weapon.subWeaponSplId}`)}
</div>
<div className="analyzer__kit-card">
<Image
path={specialWeaponImageUrl(analyzed.weapon.specialWeaponSplId)}
width={20}
height={20}
alt={t(`SPECIAL_${analyzed.weapon.specialWeaponSplId}`)}
/>
{t(`SPECIAL_${analyzed.weapon.specialWeaponSplId}`)}
</div>
</div>
);
}
function StatCategory({
title,
children,

View File

@@ -28,6 +28,7 @@ import {
import type {
BuildAbilitiesTuple,
BuildAbilitiesTupleWithUnknown,
MainWeaponId,
} from "~/modules/in-game-lists/types";
import { parseRequestFormData } from "~/utils/remix";
import { modeImageUrl, userBuildsPage } from "~/utils/urls";
@@ -141,7 +142,7 @@ export const action: ActionFunction = async ({ request }) => {
clothesGearSplId: data["CLOTHES[value]"],
shoesGearSplId: data["SHOES[value]"],
modes: modesShort.filter((mode) => data[mode]),
weaponSplIds: data["weapon[value]"],
weaponSplIds: data["weapon[value]"] as Array<MainWeaponId>,
ownerId: user.id,
};
if (data.buildToEditId) {

View File

@@ -4,6 +4,26 @@
grid-template-columns: 1fr 2fr;
}
.analyzer__kit-cards {
display: grid;
color: var(--text-lighter);
font-size: var(--fonts-xxs);
font-weight: var(--semi-bold);
gap: var(--s-2);
grid-template-columns: 1fr 1fr;
}
.analyzer__kit-card {
display: flex;
align-items: center;
justify-content: center;
background-color: var(--theme-transparent);
border-radius: var(--rounded);
gap: var(--s-1-5);
padding-block: var(--s-1);
padding-inline: var(--s-1-5);
}
.analyzer__summary {
background-color: var(--bg-lighter);
border-radius: var(--rounded);

View File

@@ -38,6 +38,10 @@
width: 100%;
}
.w-full-important {
width: 100% !important;
}
.w-24 {
width: var(--s-24);
}

View File

@@ -1,6 +1,11 @@
import type { Badge, GearType } from "~/db/types";
import type { ModeShort } from "~/modules/in-game-lists";
import type { AbilityWithUnknown } from "~/modules/in-game-lists/types";
import type {
AbilityWithUnknown,
MainWeaponId,
SpecialWeaponId,
SubWeaponId,
} from "~/modules/in-game-lists/types";
export const PLUS_SERVER_DISCORD_URL = "https://discord.gg/FW4dKrY";
export const SENDOU_INK_DISCORD_URL = "https://discord.gg/sendou";
@@ -51,8 +56,12 @@ export const articlePreviewUrl = (slug: string) =>
export const navIconUrl = (navItem: string) => `/img/layout/${navItem}`;
export const gearImageUrl = (gearType: GearType, gearSplId: number) =>
`/img/gear/${gearType.toLowerCase()}/${gearSplId}`;
export const weaponImageUrl = (weaponSplId: number) =>
`/img/main-weapons/${weaponSplId}`;
export const mainWeaponImageUrl = (mainWeaponSplId: MainWeaponId) =>
`/img/main-weapons/${mainWeaponSplId}`;
export const subWeaponImageUrl = (subWeaponSplId: SubWeaponId) =>
`/img/sub-weapons/${subWeaponSplId}`;
export const specialWeaponImageUrl = (specialWeaponSplId: SpecialWeaponId) =>
`/img/special-weapons/${specialWeaponSplId}`;
export const abilityImageUrl = (ability: AbilityWithUnknown) =>
`/img/abilities/${ability}`;
export const modeImageUrl = (mode: ModeShort) => `/img/modes/${mode}`;

View File

@@ -6,6 +6,7 @@
// xxx: internal name can be deleted when to prod
// xxx: finish merging with create-weapon-json
import type { SpecialWeaponId } from "~/modules/in-game-lists";
import {
type SubWeaponId,
subWeaponIds,
@@ -200,7 +201,7 @@ function resolveSpecialWeaponId(weapon: MainWeapon) {
`Could not find special weapon for '${codeName}'`
);
return specialWeaponObj.Id;
return specialWeaponObj.Id as SpecialWeaponId;
}
const overwriteSchema = z.object({