diff --git a/packages/website/src/config/characteristics.ts b/packages/website/src/config/characteristics.ts
index e8b28f067..27e08c4db 100644
--- a/packages/website/src/config/characteristics.ts
+++ b/packages/website/src/config/characteristics.ts
@@ -101,6 +101,24 @@ export const characteristics = {
},
},
+ reloads: {
+ type: CharacteristicType.NumberList,
+
+ should_render({ gun }) {
+ return gun.gun_type!.$case === "auto_reloader";
+ },
+
+ compute({ gun, characteristic }) {
+ if (gun.gun_type!.$case !== "auto_reloader") return [];
+
+ const reloadCoefficient = characteristic("_reload_coefficient") as number;
+
+ return gun.gun_type!.value.shell_reloads.map(
+ (reload) => reload * reloadCoefficient,
+ );
+ },
+ },
+
intra_clip: {
type: CharacteristicType.Number,
@@ -195,8 +213,6 @@ export const characteristics = {
// module_damage_range: unavailable,
- // reloads: unavailable,
-
clip_size: {
type: CharacteristicType.Number,
diff --git a/packages/website/src/config/characteristicsGroups.ts b/packages/website/src/config/characteristicsGroups.ts
index f7fd37c1f..40a11270e 100644
--- a/packages/website/src/config/characteristicsGroups.ts
+++ b/packages/website/src/config/characteristicsGroups.ts
@@ -87,7 +87,12 @@ export const characteristicsGroups: CharacteristicsGroup[] = [
units: "s",
ranking: CharacteristicRanking.LowerIsBetter,
},
- { name: "reloads", decimals: 1, units: "s" },
+ {
+ name: "reloads",
+ decimals: 1,
+ units: "s",
+ ranking: CharacteristicRanking.LowerIsBetter,
+ },
{ toy: "reload" },
],
diff --git a/packages/website/src/hooks/useCharacteristicRenderer.tsx b/packages/website/src/hooks/useCharacteristicRenderer.tsx
index cc153f9a8..9aab682de 100644
--- a/packages/website/src/hooks/useCharacteristicRenderer.tsx
+++ b/packages/website/src/hooks/useCharacteristicRenderer.tsx
@@ -1,10 +1,12 @@
import { literalsArray } from "@blitzkit/i18n/src/literals";
-import { type ReactNode } from "react";
+import { Fragment, type ReactNode } from "react";
import { Text } from "../components/Text";
+import { characteristics } from "../config/characteristics";
import type {
CharacteristicRenderConfig,
CharacteristicReturnType,
} from "../types/characteristics";
+import { CharacteristicType } from "../types/characteristics";
import { useLocale } from "./useLocale";
import { useStrings } from "./useStrings";
@@ -21,39 +23,61 @@ export function useCharacteristicRenderer() {
if (config.render)
return config.render({ output: characteristic, strings });
- if (typeof characteristic === "number") {
- if (Number.isFinite(characteristic)) {
- let value: ReactNode = characteristic;
+ function renderNumber(
+ value: number,
+ units: CharacteristicRenderConfig["units"],
+ ): ReactNode {
+ if (!Number.isFinite(value)) return `${value < 0 ? "-" : ""}∞`;
- if (config.decimals !== undefined) {
- value *= 10 ** config.decimals;
- value = Math.round(value);
- value /= 10 ** config.decimals;
- }
+ let rendered: ReactNode = value;
- if (config.localize) {
- value = value.toLocaleString(locale);
- } else {
- value = value.toFixed(config.decimals);
- }
-
- if (config.units !== undefined) {
- value = literalsArray(strings.units[config.units], {
- value: {value},
- });
- }
-
- return value;
+ if (config.decimals !== undefined) {
+ rendered *= 10 ** config.decimals;
+ rendered = Math.round(rendered);
+ rendered /= 10 ** config.decimals;
}
- return `${characteristic < 0 ? "-" : ""}∞`;
+ if (config.localize) {
+ rendered = rendered.toLocaleString(locale);
+ } else {
+ rendered = rendered.toFixed(config.decimals);
+ }
+
+ if (units !== undefined) {
+ rendered = literalsArray(strings.units[units], {
+ value: {rendered},
+ });
+ }
+
+ return rendered;
}
- if (typeof characteristic === "string") {
- return characteristic;
- }
+ switch (characteristics[config.name].type) {
+ case CharacteristicType.Enum:
+ return characteristic as string;
- throw new Error("Unknown characteristic type");
+ case CharacteristicType.Number:
+ return renderNumber(characteristic as number, config.units);
+
+ case CharacteristicType.NumberList: {
+ const values = (characteristic as number[]).map((value, index) => (
+
+ {index > 0 && ", "}
+ {renderNumber(value, undefined)}
+
+ ));
+
+ if (config.units === undefined) return values;
+
+ return (
+ <>
+ {literalsArray(strings.units[config.units], {
+ value: {values},
+ })}
+ >
+ );
+ }
+ }
}
return renderCharacteristic;
diff --git a/packages/website/src/tankopedia/calculateRankings.ts b/packages/website/src/tankopedia/calculateRankings.ts
index 6d0f3d037..bf26b823b 100644
--- a/packages/website/src/tankopedia/calculateRankings.ts
+++ b/packages/website/src/tankopedia/calculateRankings.ts
@@ -1,8 +1,11 @@
+import { characteristics as characteristicDefinitions } from "../config/characteristics";
import { characteristicsGroups } from "../config/characteristicsGroups";
import {
CharacteristicRanking as CharacteristicRankingDirection,
+ CharacteristicType,
type CharacteristicName,
type CharacteristicRenderConfig,
+ type CharacteristicReturnType,
type ComputedCharacteristics,
} from "../types/characteristics";
@@ -24,6 +27,35 @@ export interface CharacteristicRank {
score: number;
}
+function rankingValue(
+ name: CharacteristicName,
+ value?: CharacteristicReturnType,
+) {
+ if (value === undefined) return;
+
+ switch (characteristicDefinitions[name].type) {
+ case CharacteristicType.Enum:
+ return;
+
+ case CharacteristicType.Number:
+ return value as number;
+
+ case CharacteristicType.NumberList: {
+ const values = value as number[];
+
+ if (values.length === 0) return;
+
+ let total = 0;
+
+ for (const current of values) {
+ total += current;
+ }
+
+ return total / values.length;
+ }
+ }
+}
+
export function calculateRankings(
characteristics: ComputedCharacteristics,
comparisons: ComputedCharacteristics[],
@@ -31,7 +63,9 @@ export function calculateRankings(
const rankings = new Map();
for (const [name, value] of characteristics) {
- if (typeof value !== "number" || name[0] === "_") continue;
+ const currentValue = rankingValue(name, value);
+
+ if (currentValue === undefined || name[0] === "_") continue;
const config = characteristicConfigs.get(name)!;
@@ -41,13 +75,13 @@ export function calculateRankings(
config.ranking === CharacteristicRankingDirection.LowerIsBetter;
const values = comparisons
- .map((comparison) => comparison.get(name))
- .filter((other) => typeof other === "number");
+ .map((comparison) => rankingValue(name, comparison.get(name)))
+ .filter((other) => other !== undefined);
if (values.length === 0) continue;
const betterCount = values.filter((other) =>
- lowerIsBetter ? other < value : other > value,
+ lowerIsBetter ? other < currentValue : other > currentValue,
).length;
rankings.set(name, {
diff --git a/packages/website/src/types/characteristics.ts b/packages/website/src/types/characteristics.ts
index a74da634e..46e155e15 100644
--- a/packages/website/src/types/characteristics.ts
+++ b/packages/website/src/types/characteristics.ts
@@ -27,6 +27,10 @@ export type Characteristic = {
type: CharacteristicType.Number;
compute(context: CharacteristicContext): number;
}
+ | {
+ type: CharacteristicType.NumberList;
+ compute(context: CharacteristicContext): number[];
+ }
);
export type CharacteristicScript = (
@@ -63,6 +67,7 @@ export type CrewStatRole = "commander" | "loader" | "gunner" | "driver";
export enum CharacteristicType {
Enum,
Number,
+ NumberList,
}
export enum CharacteristicRanking {