From 4097730803b56ce5ec6b4c72b9ea737914217191 Mon Sep 17 00:00:00 2001 From: tresabhi <43380238+tresabhi@users.noreply.github.com> Date: Wed, 11 Nov 2026 23:23:53 -0600 Subject: [PATCH] ranges --- packages/i18n/strings/en.json | 5 ++- .../src/components/Button/index.module.css | 2 +- .../src/components/TankSearch/index.tsx | 9 ++++ .../website/src/config/characteristics.ts | 45 +++++++++++++++++-- .../src/config/characteristicsGroups.ts | 18 ++++++-- .../src/hooks/useCharacteristicRenderer.tsx | 15 +++++++ .../src/tankopedia/calculateRankings.ts | 5 +++ .../src/tankopedia/sortableCharacteristics.ts | 1 + packages/website/src/types/characteristics.ts | 6 +++ 9 files changed, 98 insertions(+), 8 deletions(-) diff --git a/packages/i18n/strings/en.json b/packages/i18n/strings/en.json index 27f475170..21eb34910 100644 --- a/packages/i18n/strings/en.json +++ b/packages/i18n/strings/en.json @@ -37,7 +37,8 @@ "hp_s": "{value}hp/s", "m3": "{value}m³", "plus": "+{value}", - "fraction": "{numerator} / {denominator}" + "fraction": "{numerator} / {denominator}", + "interval": "{min} - {max}" }, "tanks": { @@ -154,7 +155,9 @@ "dpm_effective": "Effective by 60s", "dpm_peekaboo": "Peek-a-boo", "damage": "Damage", + "damage_range": "Range", "module_damage": "Module damage", + "module_damage_range": "Range", "reload": "Reload", "reloads": "Reloads", "clipping_potential": "Clipping potential", diff --git a/packages/website/src/components/Button/index.module.css b/packages/website/src/components/Button/index.module.css index 672ac5716..3dbfe4819 100644 --- a/packages/website/src/components/Button/index.module.css +++ b/packages/website/src/components/Button/index.module.css @@ -88,7 +88,7 @@ &[data-variant="surface"], &[data-variant="soft"] { - background-color: var(--accent-4); + background-color: var(--accent-a3); color: var(--gray-12); } diff --git a/packages/website/src/components/TankSearch/index.tsx b/packages/website/src/components/TankSearch/index.tsx index d0fedc80c..f9fb7ea1e 100644 --- a/packages/website/src/components/TankSearch/index.tsx +++ b/packages/website/src/components/TankSearch/index.tsx @@ -1,3 +1,4 @@ +import type { Vector2Tuple } from "three"; import { metaSortTank, SEARCH_KEYS } from "@blitzkit/core"; import { literals } from "@blitzkit/i18n/src/literals"; import type { TankDefinition } from "@blitzkit/protos"; @@ -134,6 +135,14 @@ export const TankSearch = memo( break; } + case CharacteristicType.Interval: { + const [low, high] = value as Vector2Tuple; + + sortValue = (low + high) / 2; + + break; + } + case CharacteristicType.NumberList: { const values = value as number[]; if (values.length === 0) { diff --git a/packages/website/src/config/characteristics.ts b/packages/website/src/config/characteristics.ts index f7d18ec65..8fe268d84 100644 --- a/packages/website/src/config/characteristics.ts +++ b/packages/website/src/config/characteristics.ts @@ -1,3 +1,4 @@ +import type { Vector2Tuple } from "three"; import { blitzkitToBlitzShellKind, isExplosive, @@ -19,6 +20,7 @@ import { */ const PEEKABOO_MIN_CYCLE = 10; +// characteristics prefixed with an underscore are compute time only; they are never rendered export const characteristics = { gun_type: { type: CharacteristicType.Enum, @@ -66,7 +68,6 @@ export const characteristics = { }, }, - // characteristics prefixed with an underscore are compute time only; they are never rendered _reload_coefficient: { type: CharacteristicType.Number, @@ -240,7 +241,35 @@ export const characteristics = { }, }, - // damage_range: unavailable, + // damage rolls within +-25% unless tungsten tips raise the lower end + _damage_spread: { + type: CharacteristicType.Interval, + + compute({ script }) { + let low = 0.75; + let high = 1.25; + + script("protagonist", "TungstenTip", (effect) => { + low = effect.minDamageFactor; + high = effect.maxDamageFactor; + }); + + return [low, high] satisfies Vector2Tuple; + }, + }, + + damage_range: { + type: CharacteristicType.Interval, + + compute({ characteristic }): Vector2Tuple { + const [low, high] = characteristic("_damage_spread") as Vector2Tuple; + + // damage already folds in the average of the spread + const base = (characteristic("damage") as number) / ((low + high) / 2); + + return [base * low, base * high]; + }, + }, module_damage: { type: CharacteristicType.Number, @@ -255,7 +284,17 @@ export const characteristics = { }, }, - // module_damage_range: unavailable, + module_damage_range: { + type: CharacteristicType.Interval, + + compute({ characteristic }): Vector2Tuple { + const [low, high] = characteristic("_damage_spread") as Vector2Tuple; + const base = + (characteristic("module_damage") as number) / ((low + high) / 2); + + return [base * low, base * high]; + }, + }, clip_size: { type: CharacteristicType.Number, diff --git a/packages/website/src/config/characteristicsGroups.ts b/packages/website/src/config/characteristicsGroups.ts index 884a0cc70..6855c7ce3 100644 --- a/packages/website/src/config/characteristicsGroups.ts +++ b/packages/website/src/config/characteristicsGroups.ts @@ -96,9 +96,21 @@ export const characteristicsGroups: CharacteristicsGroup[] = [ indent: true, }, { name: "damage", decimals: 0, units: "hp", localize: true }, - { name: "damage_range", decimals: 0, units: "hp", localize: true }, + { + name: "damage_range", + decimals: 0, + units: "hp", + localize: true, + indent: true, + }, { name: "module_damage", decimals: 0, units: "hp", localize: true }, - { name: "module_damage_range", decimals: 0, units: "hp", localize: true }, + { + name: "module_damage_range", + decimals: 0, + units: "hp", + localize: true, + indent: true, + }, { name: "reload", decimals: 1, @@ -236,7 +248,7 @@ export const characteristicsGroups: CharacteristicsGroup[] = [ ranking: CharacteristicRanking.LowerIsBetter, }, - { toy: "aim_time" }, + // { toy: "aim_time" }, ], }, diff --git a/packages/website/src/hooks/useCharacteristicRenderer.tsx b/packages/website/src/hooks/useCharacteristicRenderer.tsx index 9aab682de..b3e185d07 100644 --- a/packages/website/src/hooks/useCharacteristicRenderer.tsx +++ b/packages/website/src/hooks/useCharacteristicRenderer.tsx @@ -1,3 +1,4 @@ +import type { Vector2Tuple } from "three"; import { literalsArray } from "@blitzkit/i18n/src/literals"; import { Fragment, type ReactNode } from "react"; import { Text } from "../components/Text"; @@ -59,6 +60,20 @@ export function useCharacteristicRenderer() { case CharacteristicType.Number: return renderNumber(characteristic as number, config.units); + case CharacteristicType.Interval: { + const [low, high] = characteristic as Vector2Tuple; + const rendered = literalsArray(strings.units.interval, { + min: renderNumber(low, undefined), + max: renderNumber(high, undefined), + }); + + if (config.units === undefined) return rendered; + + return literalsArray(strings.units[config.units], { + value: {rendered}, + }); + } + case CharacteristicType.NumberList: { const values = (characteristic as number[]).map((value, index) => ( diff --git a/packages/website/src/tankopedia/calculateRankings.ts b/packages/website/src/tankopedia/calculateRankings.ts index 57587d001..c474f6f24 100644 --- a/packages/website/src/tankopedia/calculateRankings.ts +++ b/packages/website/src/tankopedia/calculateRankings.ts @@ -1,3 +1,4 @@ +import type { Vector2Tuple } from "three"; import { characteristics as characteristicDefinitions } from "../config/characteristics"; import { characteristicsGroups } from "../config/characteristicsGroups"; import { @@ -40,6 +41,10 @@ function rankingValue( case CharacteristicType.Number: return value as number; + case CharacteristicType.Interval: + const [a, b] = value as Vector2Tuple; + return (a + b) / 2; + case CharacteristicType.NumberList: { const values = value as number[]; diff --git a/packages/website/src/tankopedia/sortableCharacteristics.ts b/packages/website/src/tankopedia/sortableCharacteristics.ts index ddc2b356e..ff85ab1d3 100644 --- a/packages/website/src/tankopedia/sortableCharacteristics.ts +++ b/packages/website/src/tankopedia/sortableCharacteristics.ts @@ -8,6 +8,7 @@ import { const sortableCharacteristicTypes = new Set([ CharacteristicType.Number, CharacteristicType.NumberList, + CharacteristicType.Interval, ]); export const sortableCharacteristics = new Set(); diff --git a/packages/website/src/types/characteristics.ts b/packages/website/src/types/characteristics.ts index 9bf95fbc1..9f8853268 100644 --- a/packages/website/src/types/characteristics.ts +++ b/packages/website/src/types/characteristics.ts @@ -11,6 +11,7 @@ import type { TurretDefinition, } from "@blitzkit/protos"; import type { ReactNode } from "react"; +import type { Vector2Tuple } from "three"; import type { characteristics } from "../config/characteristics"; import type { toys } from "../config/toys"; import type { DuelSide } from "../hooks/useEquipment"; @@ -31,6 +32,10 @@ export type Characteristic = { type: CharacteristicType.NumberList; compute(context: CharacteristicContext): number[]; } + | { + type: CharacteristicType.Interval; + compute(context: CharacteristicContext): Vector2Tuple; + } ); export type CharacteristicScript = ( @@ -68,6 +73,7 @@ export enum CharacteristicType { Enum, Number, NumberList, + Interval, } export enum CharacteristicRanking {