Add peekaboo DPM stat to Tankopedia firepower

Adds a **Peekaboo DPM** entry nested under DPM for single-shot guns, estimating damage per minute when only firing after resetting camouflage (or waiting out the reload, whichever is slower), assuming at least 10 seconds between shots.

- Shots per minute are capped at `60/10` for reloads faster than 10s, and fall back to `60/reload` otherwise
- The result is multiplied by the currently selected shell's damage and rounded
- An (i) icon next to the label opens a popover explaining the calculation
- The row is hidden for auto-loader/auto-reloader guns
- No percentile ranking bar is shown, since no dataset exists yet for this new stat
This commit is contained in:
Mark Greene committed 2026-09-25 09:22:08 +12:00
1 parent 84f950ade4
commit 281e4abec7
3 files changed
+50 -1

No files matched your search

+2
View File
@@ -631,6 +631,8 @@
"shellRicochet": "Ricochet",
"dpm": "DPM",
"dpmEffective": "Effective at 60s",
"peekabooDpm": "Peekaboo DPM",
"peekabooDpmInfo": "Damage per minute assuming you only fire after resetting camouflage (or the reload finishes, if it's slower) \u2014 at least 10 seconds between shots.",
"shells": "Shells",
"mostOptimalShellIndex": "Most optimal shell",
"shellReloads": "Reloads",
@@ -1,6 +1,14 @@
import { alias, isExplosive } from "@blitzkit/core";
import { literals } from "@blitzkit/i18n";
import { Flex, Heading, IconButton, Tooltip } from "@radix-ui/themes";
import { InfoCircledIcon } from "@radix-ui/react-icons";
import {
Flex,
Heading,
IconButton,
Popover,
Text,
Tooltip,
} from "@radix-ui/themes";
import { useLocale } from "../../../../../../hooks/useLocale";
import { useTankModelDefinition } from "../../../../../../hooks/useTankModelDefinition";
import { Duel } from "../../../../../../stores/duel";
@@ -77,6 +85,40 @@ export function Firepower({
{strings.common.gun_types[gun.gun_type!.$case]}
</Info>
<InfoWithDelta stats={stats} decimals={0} value="dpm" />
{gun.gun_type!.$case === "regular" && (
<InfoWithDelta
stats={stats}
indent
decimals={0}
noRanking
name={
<Flex align="center" gap="1" display="inline-flex">
{
strings.website.tools.tankopedia.characteristics.values
.peekabooDpm
}
<Popover.Root>
<Popover.Trigger>
<IconButton variant="ghost" size="1">
<InfoCircledIcon />
</IconButton>
</Popover.Trigger>
<Popover.Content style={{ maxWidth: 280 }}>
<Text size="2">
{
strings.website.tools.tankopedia.characteristics.values
.peekabooDpmInfo
}
</Text>
</Popover.Content>
</Popover.Root>
</Flex>
}
value={(tank) => tank.peekabooDpm}
/>
)}
<InfoWithDelta stats={stats} decimals={0} value="damage" />
{gun.gun_type!.$case !== "regular" && (
<InfoWithDelta stats={stats} indent decimals={0} value="clipDamage" />
@@ -418,6 +418,10 @@ export function tankCharacteristics(
: (gun.gun_type!.$case === "regular"
? gun.gun_type!.value.reload
: gun.gun_type!.value.clip_reload) * reloadCoefficient;
const peekabooDpm =
gun.gun_type!.$case === "regular"
? Math.round(damage * (60 / Math.max(shellReload!, 10)))
: undefined;
const caliber = shell.caliber;
const penetration = shell.penetration!.near * penetrationCoefficient;
const clipDamage =
@@ -545,6 +549,7 @@ export function tankCharacteristics(
shellRicochet,
dpm,
dpmEffective,
peekabooDpm,
shells,
mostOptimalShellIndex,
shellReloads,