diff --git a/packages/website-ue/src/core/tankopedia/characteristics.ts b/packages/website-ue/src/core/tankopedia/characteristics.ts index 7d2702fc5..b04c9f27f 100644 --- a/packages/website-ue/src/core/tankopedia/characteristics.ts +++ b/packages/website-ue/src/core/tankopedia/characteristics.ts @@ -3,50 +3,83 @@ import { TankAttributeChange_AttributeName, } from "@protos/blitz_static_tank_upgrade_single_stage"; -export type CharacteristicOutput = number | string | null; +export type CharacteristicOutput = number | null; -interface Characteristic { - value: (helpers: { - characteristic: (name: CharacteristicName) => CharacteristicOutput; - attribute: (name: TankAttributeChange_AttributeName) => number; - shell: (name: ShellUpgrageSingleChange_AttributeName) => number; - }) => CharacteristicOutput; -} +type Characteristic = (helpers: { + characteristic: (name: CharacteristicName) => CharacteristicOutput; + attribute: (name: TankAttributeChange_AttributeName) => number; + shell: (name: ShellUpgrageSingleChange_AttributeName) => number; +}) => CharacteristicOutput; export type CharacteristicName = keyof typeof characteristics; +export enum GunType { + Regular, + AutoLoader, + AutoReloader, +} + export const characteristics = { - gun_type: { - value({ attribute }) { - const isPump = attribute( - TankAttributeChange_AttributeName.ATTRIBUTE_NAME_IS_PUMP - ); + gun_type({ attribute }) { + const isPump = attribute( + TankAttributeChange_AttributeName.ATTRIBUTE_NAME_IS_PUMP + ); - if (isPump === 1) return "auto_reloader"; + if (isPump === 1) return GunType.AutoReloader; - const clipSize = attribute( - TankAttributeChange_AttributeName.ATTRIBUTE_NAME_CLIP_SIZE - ); + const clipSize = attribute( + TankAttributeChange_AttributeName.ATTRIBUTE_NAME_CLIP_SIZE + ); - if (clipSize === 1) return "regular"; + if (clipSize === 1) return GunType.Regular; - return "auto_loader"; - }, + return GunType.AutoLoader; }, - damage: { - value({ shell }) { - return shell( - ShellUpgrageSingleChange_AttributeName.ATTRIBUTE_NAME_ARMOR_DAMAGE - ); - }, + damage({ shell }) { + return shell( + ShellUpgrageSingleChange_AttributeName.ATTRIBUTE_NAME_ARMOR_DAMAGE + ); }, - dpm: { - value({ characteristic }) { - const damage = characteristic("damage"); + module_damage({ shell }) { + return shell( + ShellUpgrageSingleChange_AttributeName.ATTRIBUTE_NAME_MODULE_DAMAGE + ); + }, - return 1; - }, + penetration({ shell }) { + return shell( + ShellUpgrageSingleChange_AttributeName.ATTRIBUTE_NAME_PIERCING_POWER + ); + }, + + reload({ characteristic, attribute }) { + const gunType = characteristic("gun_type")!; + + if (gunType !== GunType.Regular) return null; + + return attribute( + TankAttributeChange_AttributeName.ATTRIBUTE_NAME_RELOAD_TIME + ); + }, + + dpm({ characteristic }) { + let dps: number; + const gunType = characteristic("gun_type")!; + const damage = characteristic("damage")!; + + switch (gunType) { + case GunType.Regular: { + const reload = characteristic("reload")!; + dps = damage / reload; + break; + } + + default: + throw new Error("Unknown gun type"); + } + + return 60 * dps; }, } satisfies Record; diff --git a/packages/website-ue/src/core/tankopedia/characteristicsGroups.ts b/packages/website-ue/src/core/tankopedia/characteristicsGroups.ts index 8ee6689e1..c0abe6a66 100644 --- a/packages/website-ue/src/core/tankopedia/characteristicsGroups.ts +++ b/packages/website-ue/src/core/tankopedia/characteristicsGroups.ts @@ -1,14 +1,28 @@ import type { CharacteristicName } from "./characteristics"; +export enum BetterDirection { + Higher, + Lower, +} + type CharacteristicsOrder = { group: string; - order: CharacteristicName[]; + order: { + name: CharacteristicName; + decimals?: number; + }[]; }[]; export const characteristicsGroups = [ { group: "firepower", - order: ["gun_type", "dpm"], + order: [ + { name: "gun_type" }, + { name: "dpm", decimals: 0 }, + { name: "damage" }, + { name: "module_damage" }, + { name: "penetration" }, + ], }, { group: "maneuverability", diff --git a/packages/website-ue/src/core/tankopedia/renderCharacteristics.ts b/packages/website-ue/src/core/tankopedia/computeCharacteristics.ts similarity index 81% rename from packages/website-ue/src/core/tankopedia/renderCharacteristics.ts rename to packages/website-ue/src/core/tankopedia/computeCharacteristics.ts index 2c74b9e16..0ff19d4e1 100644 --- a/packages/website-ue/src/core/tankopedia/renderCharacteristics.ts +++ b/packages/website-ue/src/core/tankopedia/computeCharacteristics.ts @@ -8,10 +8,11 @@ import { type CharacteristicName, type CharacteristicOutput, } from "./characteristics"; +import type { TankState } from "./tankState"; -export function renderCharacteristics( +export function computeCharacteristics( parameters: StageParameters, - shellId: number + state: TankState ) { const computed: Partial> = {}; @@ -34,7 +35,7 @@ export function renderCharacteristics( function shell(name: ShellUpgrageSingleChange_AttributeName) { const shell = parameters.shells_upgrades.find( - (shell) => shell.shell_id === name + (shell) => shell.shell_id === state.shell ); if (!shell) throw new Error(`Shell ${name} not found`); @@ -50,7 +51,9 @@ export function renderCharacteristics( for (const _name in characteristics) { const name = _name as CharacteristicName; - const _characteristic = characteristics[name]; - const value = _characteristic.value({ characteristic, attribute, shell }); + const value = characteristics[name]({ characteristic, attribute, shell }); + computed[name] = value; } + + return computed as Record; } diff --git a/packages/website-ue/src/core/tankopedia/tankState.ts b/packages/website-ue/src/core/tankopedia/tankState.ts new file mode 100644 index 000000000..acc81caa8 --- /dev/null +++ b/packages/website-ue/src/core/tankopedia/tankState.ts @@ -0,0 +1,5 @@ +export interface TankState { + stage: number; + + shell: number; +} diff --git a/packages/website-ue/src/pages/tanks/[slug]/_index.tsx b/packages/website-ue/src/pages/tanks/[slug]/_index.tsx index 76359aa40..ba5288c56 100644 --- a/packages/website-ue/src/pages/tanks/[slug]/_index.tsx +++ b/packages/website-ue/src/pages/tanks/[slug]/_index.tsx @@ -2,7 +2,8 @@ import { times } from "lodash-es"; import { Suspense, useMemo } from "react"; import { api } from "../../../core/api/dynamic"; import { aggregateStageParameters } from "../../../core/tankopedia/aggregateStageParameters"; -import { characteristics } from "../../../core/tankopedia/characteristics"; +import { characteristicsGroups } from "../../../core/tankopedia/characteristicsGroups"; +import { computeCharacteristics } from "../../../core/tankopedia/computeCharacteristics"; import { useAwait } from "../../../hooks/useAwait"; import { Tankopedia } from "../../../stores/tankopedia"; @@ -17,11 +18,19 @@ export function Page({ id }: { id: string }) { function Content({ id }: { id: string }) { const tank = useAwait(() => api.tank(id), `tank-${id}`); - const stage = Tankopedia.use((state) => state.stage); + const protagonist = Tankopedia.use((state) => state.protagonist); const parameters = useMemo( () => - aggregateStageParameters(tank.base_stats!, tank.upgrade_stages, stage), - [stage] + aggregateStageParameters( + tank.base_stats!, + tank.upgrade_stages, + protagonist.shell + ), + [protagonist] + ); + const characteristics = useMemo( + () => computeCharacteristics(parameters, protagonist), + [protagonist] ); return ( @@ -32,23 +41,35 @@ function Content({ id }: { id: string }) { key={index} onClick={() => { Tankopedia.mutate((draft) => { - draft.stage = index; + draft.protagonist.stage = index; }); }} > - Stage {index} {index === stage && "(selected)"} + Stage {index} {index === protagonist.stage && "(selected)"} ))} - {characteristicsOrder.map(({ group, order }) => ( + {characteristicsGroups.map(({ group, order }) => ( <>

{group}

- {order.map((name) => { + {order.map(({ name, decimals }) => { const characteristic = characteristics[name]; - return {}; + if (characteristic === null) return null; + + let rendered: string | number = characteristic; + + if (typeof rendered === "number") { + if (decimals !== undefined) rendered = rendered.toFixed(decimals); + } + + return ( +

+ {name}: {rendered} +

+ ); })} ))} diff --git a/packages/website-ue/src/stores/tankopedia.ts b/packages/website-ue/src/stores/tankopedia.ts index 5bcee7a5c..29e5a68d1 100644 --- a/packages/website-ue/src/stores/tankopedia.ts +++ b/packages/website-ue/src/stores/tankopedia.ts @@ -1,7 +1,10 @@ import { Varuna } from "varuna"; +import type { TankState } from "../core/tankopedia/tankState"; interface Tankopedia { - stage: number; + protagonist: TankState; } -export const Tankopedia = new Varuna({ stage: 0 }); +export const Tankopedia = new Varuna({ + protagonist: { stage: 0, shell: 0 }, +});