This commit is contained in:
tresabhi committed 2026-10-11 15:44:42 -05:00
1 parent eb0c35d6c6
commit ae2839cb83
14 files changed
+452 -281

No files matched your search

@@ -1,60 +0,0 @@
import { TIER_ROMAN_NUMERALS } from "@blitzkit/core";
import { literals } from "@blitzkit/i18n/src/literals";
import { Flex, SegmentedControl, Text, type FlexProps } from "@radix-ui/themes";
import { useLocale } from "../../../../../../hooks/useLocale";
import { Duel } from "../../../../../../stores/duel";
import {
Tankopedia,
TankopediaRelativeAgainst,
} from "../../../../../../stores/tankopedia";
export function CompareOptions(props: FlexProps) {
const relativeAgainst = Tankopedia.use((state) => state.relativeAgainst);
const tank = Duel.use((state) => state.protagonist.tank);
const { strings } = useLocale();
return (
<Flex
align="center"
gap="2"
justify="center"
direction={{ initial: "column", sm: "row" }}
{...props}
>
<Text color="gray">
{strings.website.tools.tankopedia.characteristics.compare.label}
</Text>
<SegmentedControl.Root
variant="classic"
size="1"
value={`${relativeAgainst}`}
onValueChange={(value) => {
Tankopedia.mutate((draft) => {
draft.relativeAgainst = Number(value) as TankopediaRelativeAgainst;
});
}}
>
<SegmentedControl.Item value={`${TankopediaRelativeAgainst.Class}`}>
{literals(
strings.website.tools.tankopedia.characteristics.compare.class,
{
tier: TIER_ROMAN_NUMERALS[tank.tier],
class:
strings.common.tank_class_short_plural_lowercase[tank.class],
}
)}
</SegmentedControl.Item>
<SegmentedControl.Item value={`${TankopediaRelativeAgainst.Tier}`}>
{literals(
strings.website.tools.tankopedia.characteristics.compare.tier,
{ tier: TIER_ROMAN_NUMERALS[tank.tier] }
)}
</SegmentedControl.Item>
<SegmentedControl.Item value={`${TankopediaRelativeAgainst.All}`}>
{strings.website.tools.tankopedia.characteristics.compare.all}
</SegmentedControl.Item>
</SegmentedControl.Root>
</Flex>
);
}
@@ -1,185 +0,0 @@
import { createDefaultSkills } from "@blitzkit/core";
import { Flex, Progress, Text } from "@radix-ui/themes";
import { clamp } from "lodash-es";
import { memo, useMemo, type ComponentProps, type ReactNode } from "react";
import { api } from "../../../../../../api/dynamic";
import {
tankCharacteristics,
type TankCharacteristics,
} from "../../../../../../core/blitzkit/tankCharacteristics";
import { tankToDuelMember } from "../../../../../../core/blitzkit/tankToDuelMember";
import { useDelta } from "../../../../../../hooks/useDelta";
import { useLocale } from "../../../../../../hooks/useLocale";
import { Duel } from "../../../../../../stores/duel";
import {
Tankopedia,
TankopediaRelativeAgainst,
} from "../../../../../../stores/tankopedia";
import { Info, type InfoProps } from "./Info";
type InfoWithDeltaProps = Omit<InfoProps, "name"> & {
stats: TankCharacteristics;
noRanking?: boolean;
} & (
| {
value: keyof TankCharacteristics;
}
| {
value: (tank: TankCharacteristics) => number | undefined;
name: ReactNode;
}
);
const [
tankDefinitions,
provisionDefinitions,
equipmentDefinitions,
modelDefinitions,
skillDefinitions,
] = await Promise.all([
api.tanks(),
api.provisions(),
api.equipment(),
api.models(),
api.skills(),
]);
export const InfoWithDelta = memo<InfoWithDeltaProps>(
({ stats, indent, noRanking, deltaType, ...props }) => {
const { strings } = useLocale();
const relativeAgainst = Tankopedia.use((state) => state.relativeAgainst);
const uhWhatDoICallThisVariable =
typeof props.value === "function"
? props.value(stats)!
: (stats[props.value] as number);
const delta = useDelta(uhWhatDoICallThisVariable);
const protagonistTank = Duel.use((state) => state.protagonist.tank);
const shellIndex = Duel.use((state) =>
state.protagonist.gun.shells.indexOf(state.protagonist.shell),
);
const equipmentMatrix = Duel.use(
(state) => state.protagonist.equipmentMatrix,
);
const equalize = Duel.use((state) => state.equalize);
const others = useMemo(() => {
const defaultSkills = createDefaultSkills(skillDefinitions);
return Object.values(tankDefinitions.tanks)
.filter(
(tank) =>
(relativeAgainst === TankopediaRelativeAgainst.Class &&
tank.tier === protagonistTank.tier &&
tank.class === protagonistTank.class) ||
(relativeAgainst === TankopediaRelativeAgainst.Tier &&
tank.tier === protagonistTank.tier) ||
relativeAgainst === TankopediaRelativeAgainst.All,
)
.map((tank) => {
const member = tankToDuelMember(tank, provisionDefinitions);
if (member.gun.shells[shellIndex] === undefined) {
return undefined;
}
return tankCharacteristics(
{
applyDynamicArmor: false,
applyReactiveArmor: false,
applySpallLiner: false,
camouflage: member.camouflage,
consumables: member.consumables,
crewSkills: defaultSkills,
provisions: member.provisions,
engine: member.engine,
gun: member.gun,
equipmentMatrix: equipmentMatrix,
shell: member.gun.shells[shellIndex],
stockEngine: tank.engines[0],
stockGun: tank.turrets[0].guns[0],
stockTrack: tank.tracks[0],
stockTurret: tank.turrets[0],
tank,
track: member.track,
turret: member.turret,
assaultDistance: member.assaultDistance,
equalize,
},
{
equipmentDefinitions,
provisionDefinitions,
tankModelDefinition: modelDefinitions.models[tank.id],
},
);
})
.filter((tank) => {
if (tank === undefined) return false;
const othersValue =
typeof props.value === "function"
? props.value(tank)!
: (tank[props.value] as number);
return othersValue !== undefined;
}) as TankCharacteristics[];
}, [relativeAgainst, shellIndex, equipmentMatrix, equalize]);
const betterTanks = others.filter((tank) => {
const othersValue =
typeof props.value === "function"
? props.value(tank)!
: (tank[props.value] as number);
if (othersValue === undefined) return false;
return deltaType === "lowerIsBetter"
? othersValue < uhWhatDoICallThisVariable
: othersValue > uhWhatDoICallThisVariable;
});
const goodness = (others.length - betterTanks.length) / others.length;
let color: ComponentProps<typeof Progress>["color"];
if (goodness <= 0.25) color = "red";
else if (goodness <= 0.5) color = "orange";
else if (goodness <= 0.75) color = "yellow";
else color = "green";
return (
<Flex direction="column">
<Info
deltaType={deltaType}
indent={indent}
name={
"name" in props
? props.name
: strings.website.tools.tankopedia.characteristics.values[
props.value
]
}
delta={delta}
{...props}
>
{uhWhatDoICallThisVariable}
</Info>
{!noRanking && (
<Flex pl={indent ? "2" : "0"} align="center" gap="2">
<Progress
variant="soft"
size="1"
value={goodness * 100}
color={color}
style={{ height: "0.125rem", opacity: 0.5 }}
/>
<Text color="gray" size="1">
{clamp(betterTanks.length + 1, 1, others.length)} /{" "}
{others.length}
</Text>
</Flex>
)}
</Flex>
);
},
(a, b) =>
(typeof a.value === "function" ? a.value(a.stats) : a.stats[a.value]) ===
(typeof b.value === "function" ? b.value(b.stats) : b.stats[b.value]),
);