fix: update all

This commit is contained in:
TrèsAbhi committed 2026-03-31 14:11:45 -05:00
1 parent bdea9fdab3
commit e2f50871ec
86 files changed
+1939 -1537

No files matched your search

@@ -24,7 +24,7 @@ export function Crew({ stats }: StatsAcceptorProps) {
provisionDefinitions.provisions[provision].crew
? total + provisionDefinitions.provisions[provision].crew! / 100
: total,
0
0,
) + (hasImprovedVentilation ? 0.08 : 0);
const commanderMastery = 1 + provisionCrewBonus;
@@ -71,7 +71,7 @@ export function Crew({ stats }: StatsAcceptorProps) {
decimals={0}
noRanking
value={() =>
(member.type === CrewType.COMMANDER
(member.type === CrewType.CREW_TYPE_COMMANDER
? commanderMastery
: commanderMastery * 1.1) * 100
}
@@ -93,7 +93,7 @@ export function Crew({ stats }: StatsAcceptorProps) {
? strings.website.tools.tankopedia.crew[
CREW_MEMBER_NAMES[sub]
]
: sub
: sub,
)
.join(", ")}
</Flex>
@@ -38,7 +38,7 @@ export function Firepower({
const selected = thisShell.id === shell.id;
return (
<Tooltip content={unwrap(thisShell.name)} key={thisShell.id}>
<Tooltip content={unwrap(thisShell.name!)} key={thisShell.id}>
<IconButton
color={selected ? undefined : "gray"}
variant={selected ? "solid" : "soft"}
@@ -59,7 +59,7 @@ export function Firepower({
}}
>
<img
alt={unwrap(thisShell.name)}
alt={unwrap(thisShell.name!)}
width={16}
height={16}
src={asset(`icons/shells/${thisShell.icon}.webp`)}
@@ -119,7 +119,7 @@ export function Firepower({
name={literals(
strings.website.tools.tankopedia.characteristics.values
.shell_index,
{ index: index + 1 }
{ index: index + 1 },
)}
decimals={2}
deltaType="lowerIsBetter"
@@ -191,7 +191,7 @@ export function Firepower({
name={literals(
strings.website.tools.tankopedia.characteristics.values
.dispersion_at_distance,
{ distance: 100 }
{ distance: 100 },
)}
/>
<InfoWithDelta
@@ -249,7 +249,7 @@ export function Firepower({
/>
<InfoWithDelta value="gunDepression" stats={stats} decimals={1} indent />
<InfoWithDelta stats={stats} decimals={1} indent value="gunElevation" />
{gunModelDefinition.pitch.front && (
{gunModelDefinition.pitch!.front && (
<>
<InfoWithDelta
stats={stats}
@@ -265,7 +265,7 @@ export function Firepower({
/>
</>
)}
{gunModelDefinition.pitch.back && (
{gunModelDefinition.pitch!.back && (
<>
<InfoWithDelta
stats={stats}
@@ -1,13 +1,13 @@
import type { QuicklimeEvent } from 'quicklime';
import { useEffect, useRef } from 'react';
import type { Group } from 'three';
import { awaitableModelDefinitions } from '../../../../../../../../core/awaitables/modelDefinitions';
import type { QuicklimeEvent } from "quicklime";
import { useEffect, useRef } from "react";
import type { Group } from "three";
import { awaitableModelDefinitions } from "../../../../../../../../core/awaitables/modelDefinitions";
import {
modelTransformEvent,
type ModelTransformEventData,
} from '../../../../../../../../core/blitzkit/modelTransform';
import { Duel } from '../../../../../../../../stores/duel';
import { ModelChunk } from './ModelChunk';
} from "../../../../../../../../core/blitzkit/modelTransform";
import { Duel } from "../../../../../../../../stores/duel";
import { ModelChunk } from "./ModelChunk";
const modelDefinitions = await awaitableModelDefinitions;
@@ -39,8 +39,8 @@ export function CanvasContent() {
}, []);
return (
<>
<group ref={hullWrapper} position={[0, 0, turretModel.gun_origin.z]}>
<group position={[0, 0, -turretModel.gun_origin.z]}>
<group ref={hullWrapper} position={[0, 0, turretModel.gun_origin!.z]}>
<group position={[0, 0, -turretModel.gun_origin!.z]}>
<ModelChunk only="body" />
</group>
</group>
@@ -1,13 +1,13 @@
import { mauveDark } from '@radix-ui/colors';
import { LineBasicMaterial, MeshBasicMaterial } from 'three';
import { awaitableModelDefinitions } from '../../../../../../../../core/awaitables/modelDefinitions';
import { jsxTree } from '../../../../../../../../core/blitzkit/jsxTree';
import { useModel } from '../../../../../../../../hooks/useModel';
import { Duel } from '../../../../../../../../stores/duel';
import { mauveDark } from "@radix-ui/colors";
import { LineBasicMaterial, MeshBasicMaterial } from "three";
import { awaitableModelDefinitions } from "../../../../../../../../core/awaitables/modelDefinitions";
import { jsxTree } from "../../../../../../../../core/blitzkit/jsxTree";
import { useModel } from "../../../../../../../../hooks/useModel";
import { Duel } from "../../../../../../../../stores/duel";
const modelDefinitions = await awaitableModelDefinitions;
const surfaceMaterial: Record<Props['only'], MeshBasicMaterial> = {
const surfaceMaterial: Record<Props["only"], MeshBasicMaterial> = {
gun: new MeshBasicMaterial({
color: mauveDark.mauve8,
toneMapped: false,
@@ -23,7 +23,7 @@ const surfaceMaterial: Record<Props['only'], MeshBasicMaterial> = {
opacity: 0.5,
}),
};
const outlineMaterial: Record<Props['only'], LineBasicMaterial> = {
const outlineMaterial: Record<Props["only"], LineBasicMaterial> = {
gun: new LineBasicMaterial({
color: mauveDark.mauve11,
toneMapped: false,
@@ -41,7 +41,7 @@ const outlineMaterial: Record<Props['only'], LineBasicMaterial> = {
};
interface Props {
only: 'gun' | 'turret' | 'body';
only: "gun" | "turret" | "body";
}
export function ModelChunk({ only }: Props) {
@@ -53,8 +53,8 @@ export function ModelChunk({ only }: Props) {
const turretModel = tankModel.turrets[turret.id];
const gunModel = turretModel.guns[gun.id];
const originZ = tankModel.turret_origin.z + turretModel.gun_origin.z;
const originY = tankModel.turret_origin.y + turretModel.gun_origin.y;
const originZ = tankModel.turret_origin!.z + turretModel.gun_origin!.z;
const originY = tankModel.turret_origin!.y + turretModel.gun_origin!.y;
const { gltf } = useModel(tank.id);
const nodes = Object.values(gltf.nodes);
@@ -64,25 +64,25 @@ export function ModelChunk({ only }: Props) {
{nodes.map((node) => {
const isCurrentMantlet =
node.name ===
`gun_${gunModel.model_id.toString().padStart(2, '0')}_mask`;
`gun_${gunModel.model_id.toString().padStart(2, "0")}_mask`;
const isCurrentGun =
node.name === `gun_${gunModel.model_id.toString().padStart(2, '0')}`;
node.name === `gun_${gunModel.model_id.toString().padStart(2, "0")}`;
const isGun = isCurrentGun || isCurrentMantlet;
const isCurrentTurret =
node.name ===
`turret_${turretModel.model_id.toString().padStart(2, '0')}`;
`turret_${turretModel.model_id.toString().padStart(2, "0")}`;
const isTurret = isCurrentTurret;
const isHull = node.name === 'hull';
const isWheel = node.name.startsWith('chassis_wheel_');
const isTrack = node.name.startsWith('chassis_track_');
const isHull = node.name === "hull";
const isWheel = node.name.startsWith("chassis_wheel_");
const isTrack = node.name.startsWith("chassis_track_");
const isBody = isHull || isWheel || isTrack;
if (
(only === 'gun' && !isGun) ||
(only === 'turret' && !isTurret) ||
(only === 'body' && !isBody)
(only === "gun" && !isGun) ||
(only === "turret" && !isTurret) ||
(only === "body" && !isBody)
) {
return null;
}
@@ -51,21 +51,21 @@ export function GunFlexibilityVisualizer({
const [yaw, setYaw] = useState(0);
const [pitch, setPitch] = useState(0);
const [minPitch, setMinPitch] = useState(
-degToRad(gunModel.pitch.front?.max ?? gunModel.pitch.max)
-degToRad(gunModel.pitch!.front?.max ?? gunModel.pitch!.max),
);
const [maxPitch, setMaxPitch] = useState(
-degToRad(gunModel.pitch.front?.min ?? gunModel.pitch.min)
-degToRad(gunModel.pitch!.front?.min ?? gunModel.pitch!.min),
);
const container = useRef<HTMLDivElement>(null);
const highlighter = useRef<HTMLDivElement>(null);
const pointer = useRef<HTMLDivElement>(null);
const p = gunModel.pitch;
const p = gunModel.pitch!;
const y = turretModel.yaw;
const f = p.front;
const b = p.back;
const t = gunModel.pitch.transition ?? DEFAULT_PITCH_TRANSITION;
const t = gunModel.pitch!.transition ?? DEFAULT_PITCH_TRANSITION;
let d = "";
@@ -223,12 +223,12 @@ export function GunFlexibilityVisualizer({
const hasImprovedVerticalStabilizer = hasEquipment(
122,
Duel.state.protagonist.tank.equipment_preset,
Duel.state.protagonist.equipmentMatrix
Duel.state.protagonist.equipmentMatrix,
);
const hasDownImprovedVerticalStabilizer = hasEquipment(
124,
Duel.state.protagonist.tank.equipment_preset,
Duel.state.protagonist.equipmentMatrix
Duel.state.protagonist.equipmentMatrix,
);
const min = applyPitchYawLimits(
@@ -237,7 +237,7 @@ export function GunFlexibilityVisualizer({
p,
y,
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer
hasDownImprovedVerticalStabilizer,
);
const max = applyPitchYawLimits(
Math.PI,
@@ -245,7 +245,7 @@ export function GunFlexibilityVisualizer({
p,
y,
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer
hasDownImprovedVerticalStabilizer,
);
yaw = min[1];
@@ -15,7 +15,7 @@ export function ShellSwitcher() {
const selected = thisShell.id === shell.id;
return (
<Tooltip content={unwrap(thisShell.name)} key={thisShell.id}>
<Tooltip content={unwrap(thisShell.name!)} key={thisShell.id}>
<IconButton
size="3"
color={selected ? undefined : "gray"}
@@ -36,7 +36,7 @@ export function ShellSwitcher() {
}}
>
<img
alt={unwrap(thisShell.name)}
alt={unwrap(thisShell.name!)}
style={{ width: "1.125rem", height: "1.125rem" }}
src={asset(`icons/shells/${thisShell.icon}.webp`)}
/>
@@ -26,13 +26,13 @@ export function Characteristics({ skeleton }: MaybeSkeletonComponentProps) {
const consumables = Duel.use((state) => state.protagonist.consumables);
const camouflage = Duel.use((state) => state.protagonist.camouflage);
const assaultDistance = Duel.use(
(state) => state.protagonist.assaultDistance
(state) => state.protagonist.assaultDistance,
);
const equipmentMatrix = Duel.use(
(state) => state.protagonist.equipmentMatrix
(state) => state.protagonist.equipmentMatrix,
);
const { tank, turret, gun, engine, track, shell } = Duel.use(
(state) => state.protagonist
(state) => state.protagonist,
);
const stockEngine = tank.engines[0];
const stockTrack = tank.tracks[0];
@@ -67,7 +67,7 @@ export function Characteristics({ skeleton }: MaybeSkeletonComponentProps) {
tankModelDefinition,
equipmentDefinitions: equipmentDefinitions,
provisionDefinitions: provisionDefinitions,
}
},
);
const hasImprovedVerticalStabilizer = useEquipment(122);
@@ -77,10 +77,10 @@ export function Characteristics({ skeleton }: MaybeSkeletonComponentProps) {
const [pitch, yaw] = applyPitchYawLimits(
modelTransformEvent.last!.pitch,
modelTransformEvent.last!.yaw,
gunModelDefinition.pitch,
gunModelDefinition.pitch!,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer
hasDownImprovedVerticalStabilizer,
);
modelTransformEvent.dispatch({ pitch, yaw });
@@ -43,11 +43,11 @@ interface ModuleWithUnlocks {
}
const moduleTypeTypeIconName: Record<ModuleType, string> = {
[ModuleType.VEHICLE]: "",
[ModuleType.ENGINE]: "engine",
[ModuleType.TRACKS]: "chassis",
[ModuleType.TURRET]: "turret",
[ModuleType.GUN]: "gun",
[ModuleType.MODULE_TYPE_VEHICLE]: "",
[ModuleType.MODULE_TYPE_ENGINE]: "engine",
[ModuleType.MODULE_TYPE_TRACKS]: "chassis",
[ModuleType.MODULE_TYPE_TURRET]: "turret",
[ModuleType.MODULE_TYPE_GUN]: "gun",
};
function ModuleButton({
@@ -63,7 +63,7 @@ function ModuleButton({
top: boolean;
onClick: () => void;
}) {
const isTank = unlock.type === ModuleType.VEHICLE;
const isTank = unlock.type === ModuleType.MODULE_TYPE_VEHICLE;
const { locale } = useLocale();
const button = (
@@ -91,7 +91,7 @@ function ModuleButton({
{TIER_ROMAN_NUMERALS[tier]}
</Text>
{unlock.cost.value > 0 && (
{unlock.cost!.value > 0 && (
<Flex
style={{
position: "absolute",
@@ -103,12 +103,12 @@ function ModuleButton({
gap={isTank ? "1" : undefined}
>
<Text size="1" color="gray">
{formatCompact(locale, unlock.cost.value)}
{formatCompact(locale, unlock.cost!.value)}
</Text>
<img
alt={unlock.cost.type}
src={asset(`icons/currencies/${unlock.cost.type}.webp`)}
alt={unlock.cost!.type}
src={asset(`icons/currencies/${unlock.cost!.type}.webp`)}
style={{
width: 12,
height: 12,
@@ -171,16 +171,16 @@ export function Modules() {
function setByUnlock(unlock: Unlock) {
Duel.mutate((draft) => {
if (unlock.type === ModuleType.TURRET) {
if (unlock.type === ModuleType.MODULE_TYPE_TURRET) {
draft.protagonist.turret = draft.protagonist.tank.turrets.find(
(turret) => turret.id === unlock.id
(turret) => turret.id === unlock.id,
)!;
// Check if current gun exists in new turret
const gunInNewTurret = draft.protagonist.turret.guns.find(
(gun) => gun.id === draft.protagonist.gun.id
(gun) => gun.id === draft.protagonist.gun.id,
);
if (gunInNewTurret) {
// Gun exists - get it FROM this turret
draft.protagonist.gun = gunInNewTurret;
@@ -190,9 +190,9 @@ export function Modules() {
draft.protagonist.gun = draft.protagonist.turret.guns.at(-1)!;
draft.protagonist.shell = draft.protagonist.gun.shells[0];
}
} else if (unlock.type === ModuleType.GUN) {
} else if (unlock.type === ModuleType.MODULE_TYPE_GUN) {
const gunInTurret = draft.protagonist.turret.guns.find(
(gun) => gun.id === unlock.id
(gun) => gun.id === unlock.id,
);
if (gunInTurret) {
draft.protagonist.gun = gunInTurret;
@@ -200,23 +200,23 @@ export function Modules() {
} else {
// TODO: warn somehow?
const suitableTurret = draft.protagonist.tank.turrets.find((turret) =>
turret.guns.some((gun) => gun.id === unlock.id)
turret.guns.some((gun) => gun.id === unlock.id),
)!;
const gunInSuitableTurret = suitableTurret.guns.find(
(gun) => gun.id === unlock.id
(gun) => gun.id === unlock.id,
)!;
draft.protagonist.turret = suitableTurret;
draft.protagonist.gun = gunInSuitableTurret;
draft.protagonist.shell = gunInSuitableTurret.shells[0];
}
} else if (unlock.type === ModuleType.ENGINE) {
} else if (unlock.type === ModuleType.MODULE_TYPE_ENGINE) {
draft.protagonist.engine = draft.protagonist.tank.engines.find(
(engine) => engine.id === unlock.id
(engine) => engine.id === unlock.id,
)!;
} else if (unlock.type === ModuleType.TRACKS) {
} else if (unlock.type === ModuleType.MODULE_TYPE_TRACKS) {
draft.protagonist.track = draft.protagonist.tank.tracks.find(
(track) => track.id === unlock.id
(track) => track.id === unlock.id,
)!;
}
});
@@ -231,19 +231,19 @@ export function Modules() {
const central = !first && !last;
let module: ModuleDefinition | TankDefinition | undefined = undefined;
if (unlock.type === ModuleType.TRACKS) {
if (unlock.type === ModuleType.MODULE_TYPE_TRACKS) {
module = tank.tracks.find((track) => track.id === unlock.id)!;
} else if (unlock.type === ModuleType.ENGINE) {
} else if (unlock.type === ModuleType.MODULE_TYPE_ENGINE) {
module = tank.engines.find((engine) => engine.id === unlock.id)!;
} else if (unlock.type === ModuleType.TURRET) {
} else if (unlock.type === ModuleType.MODULE_TYPE_TURRET) {
module = tank.turrets.find((turret) => turret.id === unlock.id)!;
} else if (unlock.type === ModuleType.GUN) {
} else if (unlock.type === ModuleType.MODULE_TYPE_GUN) {
module = tank.turrets
.find((turret) =>
turret.guns.some((gun) => gun.id === unlock.id)
turret.guns.some((gun) => gun.id === unlock.id),
)!
.guns.find((gun) => gun.id === unlock.id)!;
} else if (unlock.type === ModuleType.VEHICLE) {
} else if (unlock.type === ModuleType.MODULE_TYPE_VEHICLE) {
module = tankDefinitions.tanks[unlock.id];
}
@@ -257,7 +257,7 @@ export function Modules() {
gap="2"
position="relative"
>
{unlock.cost.value > 0 && (
{unlock.cost!.value > 0 && (
<>
{unlocks.length > 1 && (
<div
@@ -302,24 +302,24 @@ export function Modules() {
unlock={unlock}
tier={extractModule(module).tier}
selected={
(unlock.type === ModuleType.TURRET
(unlock.type === ModuleType.MODULE_TYPE_TURRET
? turret.id
: unlock.type === ModuleType.GUN
: unlock.type === ModuleType.MODULE_TYPE_GUN
? gun.id
: unlock.type === ModuleType.ENGINE
: unlock.type === ModuleType.MODULE_TYPE_ENGINE
? engine.id
: unlock.type === ModuleType.TRACKS
: unlock.type === ModuleType.MODULE_TYPE_TRACKS
? track.id
: -1) === unlock.id
}
top={
unlock.type === ModuleType.TURRET
unlock.type === ModuleType.MODULE_TYPE_TURRET
? extractModule(module).id == topTurret.id
: unlock.type === ModuleType.GUN
: unlock.type === ModuleType.MODULE_TYPE_GUN
? extractModule(module).id === topGun.id
: unlock.type === ModuleType.ENGINE
: unlock.type === ModuleType.MODULE_TYPE_ENGINE
? extractModule(module).id === topEngine.id
: unlock.type === ModuleType.TRACKS
: unlock.type === ModuleType.MODULE_TYPE_TRACKS
? extractModule(module).id === topTrack.id
: false
}
@@ -342,7 +342,7 @@ export function Modules() {
{(extractModule(module) as ModuleWithUnlocks).unlocks &&
tree(
type,
(extractModule(module) as ModuleWithUnlocks).unlocks!
(extractModule(module) as ModuleWithUnlocks).unlocks!,
)}
</Flex>
);
@@ -397,32 +397,32 @@ export function Modules() {
</Flex>
<Flex gap="2" wrap="wrap" gapY="6">
{tree(ModuleType.ENGINE, [
{tree(ModuleType.MODULE_TYPE_ENGINE, [
{
cost: { type: "xp", value: 0 },
id: engine0.id,
type: ModuleType.ENGINE,
type: ModuleType.MODULE_TYPE_ENGINE,
},
])}
{tree(ModuleType.TRACKS, [
{tree(ModuleType.MODULE_TYPE_TRACKS, [
{
cost: { type: "xp", value: 0 },
id: track0.id,
type: ModuleType.TRACKS,
type: ModuleType.MODULE_TYPE_TRACKS,
},
])}
{tree(ModuleType.TURRET, [
{tree(ModuleType.MODULE_TYPE_TURRET, [
{
cost: { type: "xp", value: 0 },
id: turret0.id,
type: ModuleType.TURRET,
type: ModuleType.MODULE_TYPE_TURRET,
},
])}
{tree(ModuleType.GUN, [
{tree(ModuleType.MODULE_TYPE_GUN, [
{
cost: { type: "xp", value: 0 },
id: gun0.id,
type: ModuleType.GUN,
type: ModuleType.MODULE_TYPE_GUN,
},
])}
</Flex>
@@ -41,7 +41,7 @@ export function GameModeSection() {
style={{
borderRadius: "var(--radius-3)",
background: `url(${asset(
`icons/game_mode_banners/${id}.webp`
`icons/game_mode_banners/${id}.webp`,
)})`,
backgroundSize: "cover",
backgroundPosition: "center",
@@ -51,7 +51,7 @@ export function GameModeSection() {
flexGrow="1"
style={{
background: `url(${asset(
`icons/game_mode_banners/${id}.webp`
`icons/game_mode_banners/${id}.webp`,
)})`,
backgroundSize: "cover",
backgroundPosition: "center",
@@ -71,7 +71,7 @@ export function GameModeSection() {
justify="center"
>
<Text size="4" weight="bold">
{unwrap(gameMode.name)}
{unwrap(gameMode.name!)}
</Text>
<Flex direction="column">
@@ -88,9 +88,9 @@ export function GameModeSection() {
objectFit: "contain",
}}
src={asset(`icons/provisions/${id}.webp`)}
alt={unwrap(provisions.name)}
alt={unwrap(provisions.name!)}
/>
{unwrap(provisions.name)}
{unwrap(provisions.name!)}
</Flex>
</Text>
);
@@ -109,9 +109,9 @@ export function GameModeSection() {
objectFit: "contain",
}}
src={asset(`icons/consumables/${id}.webp`)}
alt={unwrap(consumable.name)}
alt={unwrap(consumable.name!)}
/>
{unwrap(consumable.name)}
{unwrap(consumable.name!)}
</Flex>
</Text>
);
@@ -68,29 +68,29 @@ export function CustomShellButton() {
const modified = { ...customShell, type };
switch (type) {
case ShellType.AP: {
case ShellType.SHELL_TYPE_AP: {
modified.normalization = 5;
modified.ricochet = 70;
modified.explosion_radius = undefined;
break;
}
case ShellType.APCR: {
case ShellType.SHELL_TYPE_APCR: {
modified.normalization = 2;
modified.ricochet = 70;
modified.explosion_radius = undefined;
break;
}
case ShellType.HEAT: {
case ShellType.SHELL_TYPE_HEAT: {
modified.normalization = undefined;
modified.ricochet = undefined;
modified.explosion_radius = undefined;
}
case ShellType.HE: {
case ShellType.SHELL_TYPE_HE: {
modified.normalization = undefined;
modified.ricochet = undefined;
modified.explosion_radius =
gun.shells.find(
(shell) => shell.type === ShellType.HE
(shell) => shell.type === ShellType.SHELL_TYPE_HE,
)?.explosion_radius ?? 1;
break;
}
@@ -104,16 +104,16 @@ export function CustomShellButton() {
<Select.Trigger variant="classic" />
<Select.Content>
<Select.Item value={`${ShellType.AP}`}>
<Select.Item value={`${ShellType.SHELL_TYPE_AP}`}>
{strings.common.shells.ap}
</Select.Item>
<Select.Item value={`${ShellType.APCR}`}>
<Select.Item value={`${ShellType.SHELL_TYPE_APCR}`}>
{strings.common.shells.ap_cr}
</Select.Item>
<Select.Item value={`${ShellType.HEAT}`}>
<Select.Item value={`${ShellType.SHELL_TYPE_HEAT}`}>
{strings.common.shells.hc}
</Select.Item>
<Select.Item value={`${ShellType.HE}`}>
<Select.Item value={`${ShellType.SHELL_TYPE_HE}`}>
{strings.common.shells.he}
</Select.Item>
</Select.Content>
@@ -166,11 +166,11 @@ export function CustomShellButton() {
<TextField.Root
variant="classic"
style={{ maxWidth: "6rem" }}
defaultValue={customShell.penetration.near}
defaultValue={customShell.penetration!.near}
type="number"
onChange={(event: ChangeEvent<HTMLInputElement>) => {
Tankopedia.mutate((draft) => {
draft.customShell!.penetration.near =
draft.customShell!.penetration!.near =
event.target.valueAsNumber;
});
}}
@@ -182,7 +182,7 @@ export function CustomShellButton() {
(event.target as HTMLInputElement).blur();
} else if (event.key === "Escape") {
(event.target as HTMLInputElement).valueAsNumber =
customShell.penetration.near;
customShell.penetration!.near;
(event.target as HTMLInputElement).blur();
}
}}
@@ -193,8 +193,8 @@ export function CustomShellButton() {
</TextField.Root>
</Flex>
{(customShell.type === ShellType.AP ||
customShell.type === ShellType.APCR) && (
{(customShell.type === ShellType.SHELL_TYPE_AP ||
customShell.type === ShellType.SHELL_TYPE_APCR) && (
<>
<Flex align="center" justify="between" gap="4" py="1">
<Text>
@@ -264,7 +264,7 @@ export function CustomShellButton() {
</>
)}
{customShell.type === ShellType.HE && (
{customShell.type === ShellType.SHELL_TYPE_HE && (
<Flex align="center" justify="between" gap="4">
<Text>Explosion radius</Text>
<TextField.Root
@@ -38,7 +38,7 @@ export function QuickInputs() {
}
function handleTransformEvent(
event: QuicklimeEvent<ModelTransformEventData>
event: QuicklimeEvent<ModelTransformEventData>,
) {
syncInputValues(event.data);
}
@@ -77,7 +77,7 @@ export function QuickInputs() {
if (isNaN(value)) {
yawInput.current!.value = radToDeg(
modelTransformEvent.last!.yaw
modelTransformEvent.last!.yaw,
).toFixed(1);
return;
}
@@ -85,15 +85,15 @@ export function QuickInputs() {
const [pitch, yaw] = applyPitchYawLimits(
modelTransformEvent.last!.pitch,
degToRad(value),
gunModelDefinition.pitch,
gunModelDefinition.pitch!,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer
hasDownImprovedVerticalStabilizer,
);
modelTransformEvent.dispatch({ pitch, yaw });
yawInput.current!.value = radToDeg(
modelTransformEvent.last!.yaw
modelTransformEvent.last!.yaw,
).toFixed(1);
}}
onKeyDown={(event) => {
@@ -129,10 +129,10 @@ export function QuickInputs() {
const [pitch, yaw] = applyPitchYawLimits(
degToRad(value + initialGunPitch),
modelTransformEvent.last!.yaw,
gunModelDefinition.pitch,
gunModelDefinition.pitch!,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer
hasDownImprovedVerticalStabilizer,
);
modelTransformEvent.dispatch({ pitch, yaw });
pitchInput.current!.value = (
@@ -54,12 +54,12 @@ type OptionsProps = MaybeSkeletonComponentProps & {
export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
const hasCustomShell = Tankopedia.use(
(state) => state.customShell !== undefined
(state) => state.customShell !== undefined,
);
const requestedDisplay = Tankopedia.use((state) => state.requestedDisplay);
const isFullScreen = useFullScreen();
const advancedHighlighting = TankopediaPersistent.use(
(state) => state.advancedHighlighting
(state) => state.advancedHighlighting,
);
const fullScreenAvailable = useFullscreenAvailability(true);
const protagonistTank = Duel.use((state) => state.protagonist.tank);
@@ -102,8 +102,8 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
value: (
resolvePenetrationCoefficient(
hasCalibratedShells,
antagonistShell.type
) * antagonistShell.penetration.near
antagonistShell.type,
) * antagonistShell.penetration!.near
).toFixed(0),
})}
</Text>
@@ -203,7 +203,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
}}
>
<img
alt={unwrap(thisShell.name)}
alt={unwrap(thisShell.name!)}
src={asset(`icons/shells/${thisShell.icon}.webp`)}
style={{
width: "50%",
@@ -456,7 +456,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
<Dialog.Trigger>
<Button variant="solid" highContrast>
<Crosshair1Icon />
{unwrap(antagonistTank.name)}
{unwrap(antagonistTank.name!)}
<SmallTankIcon id={antagonistTank.id} size={16} />
</Button>
</Dialog.Trigger>
@@ -9,7 +9,7 @@ export function Title() {
const protagonist = Duel.use((state) => state.protagonist.tank);
const revealed = Tankopedia.use((state) => state.revealed);
const disturbed = Tankopedia.use((state) => state.disturbed);
const name = unwrap(protagonist.name);
const name = unwrap(protagonist.name!);
const fontSize = revealed
? disturbed
? "1.5rem"
@@ -53,24 +53,24 @@ export function Controls({
const protagonistGunModelDefinition =
protagonistTurretModelDefinition.guns[protagonistGun.id];
const protagonistHullOrigin = new Vector3(
protagonistTrackModelDefinition.origin.x,
protagonistTrackModelDefinition.origin.y,
-protagonistTrackModelDefinition.origin.z,
protagonistTrackModelDefinition.origin!.x,
protagonistTrackModelDefinition.origin!.y,
-protagonistTrackModelDefinition.origin!.z,
);
const protagonistTurretOrigin = new Vector3(
protagonistModelDefinition.turret_origin.x,
protagonistModelDefinition.turret_origin.y,
-protagonistModelDefinition.turret_origin.z,
protagonistModelDefinition.turret_origin!.x,
protagonistModelDefinition.turret_origin!.y,
-protagonistModelDefinition.turret_origin!.z,
);
const protagonistGunOrigin = new Vector3(
protagonistTurretModelDefinition.gun_origin.x,
protagonistTurretModelDefinition.gun_origin.y,
-protagonistTurretModelDefinition.gun_origin.z,
protagonistTurretModelDefinition.gun_origin!.x,
protagonistTurretModelDefinition.gun_origin!.y,
-protagonistTurretModelDefinition.gun_origin!.z,
);
const antagonistGunHeight =
protagonistTrackModelDefinition.origin.y +
antagonistModelDefinition.turret_origin.y +
antagonistTurretModelDefinition.gun_origin.y;
protagonistTrackModelDefinition.origin!.y +
antagonistModelDefinition.turret_origin!.y +
antagonistTurretModelDefinition.gun_origin!.y;
const disturbed = Tankopedia.use((state) => state.disturbed);
const doAutoRotate = autoRotate && !disturbed;
const gunHeight =
@@ -119,7 +119,7 @@ export function Controls({
const [pitch] = applyPitchYawLimits(
-Infinity,
0,
protagonistGunModelDefinition.pitch,
protagonistGunModelDefinition.pitch!,
protagonistTurretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer,
@@ -152,7 +152,7 @@ export function Controls({
const [pitch] = applyPitchYawLimits(
0,
0,
protagonistGunModelDefinition.pitch,
protagonistGunModelDefinition.pitch!,
protagonistTurretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer,
@@ -1,9 +1,9 @@
import { useEffect } from 'react';
import { degToRad } from 'three/src/math/MathUtils.js';
import { awaitableModelDefinitions } from '../../../../../../core/awaitables/modelDefinitions';
import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawLimits';
import { modelTransformEvent } from '../../../../../../core/blitzkit/modelTransform';
import { Duel } from '../../../../../../stores/duel';
import { useEffect } from "react";
import { degToRad } from "three/src/math/MathUtils.js";
import { awaitableModelDefinitions } from "../../../../../../core/awaitables/modelDefinitions";
import { applyPitchYawLimits } from "../../../../../../core/blitz/applyPitchYawLimits";
import { modelTransformEvent } from "../../../../../../core/blitzkit/modelTransform";
import { Duel } from "../../../../../../stores/duel";
const modelDefinitions = await awaitableModelDefinitions;
@@ -20,7 +20,7 @@ export function InitialAligner() {
const [pitch, yaw] = applyPitchYawLimits(
degToRad(8),
degToRad(25),
gunModel.pitch,
gunModel.pitch!,
turretModel.yaw,
);
@@ -134,12 +134,12 @@ export function TankModel() {
const hasImprovedVerticalStabilizer = hasEquipment(
122,
Duel.state.protagonist.tank.equipment_preset,
Duel.state.protagonist.equipmentMatrix
Duel.state.protagonist.equipmentMatrix,
);
const hasDownImprovedVerticalStabilizer = hasEquipment(
124,
Duel.state.protagonist.tank.equipment_preset,
Duel.state.protagonist.equipmentMatrix
Duel.state.protagonist.equipmentMatrix,
);
const boundingRect = canvas.getBoundingClientRect();
@@ -150,10 +150,10 @@ export function TankModel() {
modelTransformEvent.last!.pitch,
modelTransformEvent.last!.yaw +
delta.x * (Math.PI / boundingRect.width),
gunModelDefinition.pitch,
gunModelDefinition.pitch!,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer
hasDownImprovedVerticalStabilizer,
);
modelTransformEvent.dispatch({ pitch, yaw });
}
@@ -217,12 +217,12 @@ export function TankModel() {
const hasImprovedVerticalStabilizer = hasEquipment(
122,
Duel.state.protagonist.tank.equipment_preset,
Duel.state.protagonist.equipmentMatrix
Duel.state.protagonist.equipmentMatrix,
);
const hasDownImprovedVerticalStabilizer = hasEquipment(
124,
Duel.state.protagonist.tank.equipment_preset,
Duel.state.protagonist.equipmentMatrix
Duel.state.protagonist.equipmentMatrix,
);
const boundingRect = canvas.getBoundingClientRect();
delta.set(event.clientX, event.clientY).sub(position);
@@ -231,10 +231,10 @@ export function TankModel() {
[pitch, yaw] = applyPitchYawLimits(
pitch - delta.y * (Math.PI / boundingRect.height),
yaw + delta.x * (Math.PI / boundingRect.width),
gunModelDefinition.pitch,
gunModelDefinition.pitch!,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer
hasDownImprovedVerticalStabilizer,
);
modelTransformEvent.dispatch({ pitch, yaw });
}
@@ -37,9 +37,9 @@ export function TransitionSkeleton() {
const gunModelDefinition = turretModelDefinition.guns[gun.id];
const trackModelDefinition = tankModelDefinition.tracks[track.id];
const wrapper = useRef<Group>(null);
const hullOrigin = correctZYTuple(trackModelDefinition.origin);
const turretOrigin = correctZYTuple(tankModelDefinition.turret_origin);
const gunOrigin = correctZYTuple(turretModelDefinition.gun_origin);
const hullOrigin = correctZYTuple(trackModelDefinition.origin!);
const turretOrigin = correctZYTuple(tankModelDefinition.turret_origin!);
const gunOrigin = correctZYTuple(turretModelDefinition.gun_origin!);
useTankTransform(track, turret, turretContainer, gunContainer);
@@ -77,7 +77,7 @@ export function TransitionSkeleton() {
<group ref={turretContainer}>
{armorNodes.map((node) => {
const isCurrentTurret = node.name.startsWith(
`turret_${turretModelDefinition.model_id.toString().padStart(2, "0")}`
`turret_${turretModelDefinition.model_id.toString().padStart(2, "0")}`,
);
const isVisible = isCurrentTurret;
if (!isVisible) {
@@ -102,7 +102,7 @@ export function TransitionSkeleton() {
<group ref={gunContainer}>
{armorNodes.map((node) => {
const isCurrentGun = node.name.startsWith(
`gun_${gunModelDefinition.model_id.toString().padStart(2, "0")}`
`gun_${gunModelDefinition.model_id.toString().padStart(2, "0")}`,
);
const isVisible = isCurrentGun;
@@ -73,7 +73,7 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
const [pitch, yaw] = applyPitchYawLimits(
-Infinity,
0,
gunModelDefinition.pitch,
gunModelDefinition.pitch!,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer,
@@ -88,7 +88,7 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
const [pitch, yaw] = applyPitchYawLimits(
Infinity,
0,
gunModelDefinition.pitch,
gunModelDefinition.pitch!,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
);
@@ -102,7 +102,7 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
const [pitch, yaw] = applyPitchYawLimits(
0,
0,
gunModelDefinition.pitch,
gunModelDefinition.pitch!,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
);
@@ -124,7 +124,7 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
const [pitch, yaw] = applyPitchYawLimits(
modelTransformEvent.last!.pitch,
modelTransformEvent.last!.yaw,
gunModelDefinition.pitch,
gunModelDefinition.pitch!,
turretModelDefinition.yaw,
hasImprovedVerticalStabilizer,
);
@@ -22,13 +22,13 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
const thicknessRange = useMemo(() => {
const entries = Object.values(tankDefinitions.tanks);
const filtered = entries.filter(
(thisTank) => thisTank.tier === protagonist.tier
(thisTank) => thisTank.tier === protagonist.tier,
);
const value =
(filtered.reduce((accumulator, thisTank) => {
return (
accumulator +
thisTank.turrets.at(-1)!.guns.at(-1)!.shells[0].penetration.near
thisTank.turrets.at(-1)!.guns.at(-1)!.shells[0].penetration!.near
);
}, 0) /
filtered.length) *
@@ -99,11 +99,11 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
isFullScreen
? "100vh"
: disturbed
? "calc(100svh - 8rem)"
: {
initial: "28rem",
md: "calc(100svh - 14rem)",
}
? "calc(100svh - 8rem)"
: {
initial: "28rem",
md: "calc(100svh - 14rem)",
}
}
maxWidth={isFullScreen ? undefined : "120rem"}
flexGrow="1"
@@ -36,7 +36,7 @@ export function VoteCaster() {
</Dialog.Trigger>
<Dialog.Content maxWidth="23rem">
<Dialog.Title>Voting for {unwrap(tank.name)}</Dialog.Title>
<Dialog.Title>Voting for {unwrap(tank.name!)}</Dialog.Title>
<Flex direction="column" mt="4">
<Flex align="center" justify="between">
@@ -118,10 +118,10 @@ export function VoteCaster() {
survivability === null
? ""
: `survivability=${survivability}&`
}player=${wargaming.id}&token=${wargaming.token}`
}player=${wargaming.id}&token=${wargaming.token}`,
)
.then(
(response) => response.json() as Promise<BlitzkitResponse>
(response) => response.json() as Promise<BlitzkitResponse>,
)
.then((json) => {
setShowDialogue(false);
@@ -3,20 +3,20 @@ import {
TankPriceType,
TankType,
TIER_ROMAN_NUMERALS,
} from '@blitzkit/core';
import { Box, Flex } from '@radix-ui/themes';
import { Var } from '../../../core/radix/var';
import { useLocale } from '../../../hooks/useLocale';
import { App } from '../../../stores/app';
import { Duel } from '../../../stores/duel';
import { classIcons } from '../../ClassIcon';
import { CopyableCode } from '../../CopyableCode';
import { Listing } from './components/Listing';
} from "@blitzkit/core";
import { Box, Flex } from "@radix-ui/themes";
import { Var } from "../../../core/radix/var";
import { useLocale } from "../../../hooks/useLocale";
import { App } from "../../../stores/app";
import { Duel } from "../../../stores/duel";
import { classIcons } from "../../ClassIcon";
import { CopyableCode } from "../../CopyableCode";
import { Listing } from "./components/Listing";
const NATIONAL_BANNER_POSITION_OVERRIDES: Record<string, string> = {
germany: '50% 35%',
france: '50% 35%',
other: '50% 35%',
germany: "50% 35%",
france: "50% 35%",
other: "50% 35%",
};
export function MetaSection() {
@@ -30,10 +30,10 @@ export function MetaSection() {
<Box
style={{
background: `url(/assets/images/national-wallpapers/${protagonist.nation}.jpg)`,
backgroundRepeat: 'no-repeat',
backgroundSize: 'cover',
backgroundRepeat: "no-repeat",
backgroundSize: "cover",
backgroundPosition:
NATIONAL_BANNER_POSITION_OVERRIDES[protagonist.nation] ?? 'center',
NATIONAL_BANNER_POSITION_OVERRIDES[protagonist.nation] ?? "center",
}}
flexGrow="1"
maxWidth="120rem"
@@ -41,9 +41,9 @@ export function MetaSection() {
<Box
style={{
background: `url(${asset(`flags/scratched/${protagonist.nation}.webp`)})`,
backgroundRepeat: 'no-repeat',
backgroundSize: 'min(53rem, 80vw)',
backgroundPosition: '-8rem 50%',
backgroundRepeat: "no-repeat",
backgroundSize: "min(53rem, 80vw)",
backgroundPosition: "-8rem 50%",
}}
>
<Flex
@@ -51,19 +51,19 @@ export function MetaSection() {
align="center"
py="6rem"
style={{
background: `linear-gradient(${Var('black-a8')}, ${Var('black-a8')}, ${Var('gray-1')})`,
background: `linear-gradient(${Var("black-a8")}, ${Var("black-a8")}, ${Var("gray-1")})`,
}}
>
<Flex direction="column" align="center" gap="6">
<Flex
gap={{ initial: '2', sm: '8' }}
direction={{ initial: 'column', sm: 'row' }}
gap={{ initial: "2", sm: "8" }}
direction={{ initial: "column", sm: "row" }}
>
<Flex direction="column" width="100%" gap="2">
<Listing label={strings.website.tools.tankopedia.meta.nation}>
<Flex align="center" gap="1">
<img
style={{ width: '1em', height: '1em' }}
style={{ width: "1em", height: "1em" }}
alt={protagonist.nation}
src={asset(`flags/circle/${protagonist.nation}.webp`)}
/>
@@ -86,10 +86,10 @@ export function MetaSection() {
<Listing
label={strings.website.tools.tankopedia.meta.type}
color={
protagonist.type === TankType.COLLECTOR
? 'blue'
: protagonist.type === TankType.PREMIUM
? 'amber'
protagonist.type === TankType.TANK_TYPE_COLLECTOR
? "blue"
: protagonist.type === TankType.TANK_TYPE_PREMIUM
? "amber"
: undefined
}
>
@@ -117,7 +117,7 @@ export function MetaSection() {
label={strings.website.tools.tankopedia.meta.dev_name}
>
<CopyableCode
copy={devName}
copy={protagonist.dev_name}
variant="soft"
highContrast
color="gray"
@@ -126,37 +126,38 @@ export function MetaSection() {
</CopyableCode>
</Listing>
)}
{protagonist.type === TankType.PREMIUM && (
{protagonist.type === TankType.TANK_TYPE_PREMIUM && (
<Listing
label={strings.website.tools.tankopedia.meta.purchase}
>
<Flex align="center" gap="1">
{protagonist.price.value / 400}
{protagonist.price!.value / 400}
<img
style={{ width: '1em', height: '1em' }}
style={{ width: "1em", height: "1em" }}
alt="gold"
src={asset('icons/currencies/gold.webp')}
src={asset("icons/currencies/gold.webp")}
/>
</Flex>
</Listing>
)}
<Listing
label={
protagonist.type === TankType.RESEARCHABLE
protagonist.type === TankType.TANK_TYPE_RESEARCHABLE
? strings.website.tools.tankopedia.meta.purchase
: strings.website.tools.tankopedia.meta.restoration
}
>
<Flex align="center" gap="1">
{protagonist.price.value.toLocaleString(locale)}
{protagonist.price!.value.toLocaleString(locale)}
<img
style={{ width: '1em', height: '1em' }}
alt={TankPriceType[protagonist.price.type]}
style={{ width: "1em", height: "1em" }}
alt={TankPriceType[protagonist.price!.type]}
src={asset(
`icons/currencies/${
protagonist.price.type === TankPriceType.GOLD
? 'gold'
: 'silver'
protagonist.price!.type ===
TankPriceType.TANK_PRICE_TYPE_GOLD
? "gold"
: "silver"
}.webp`,
)}
/>
@@ -164,15 +165,16 @@ export function MetaSection() {
</Listing>
<Listing label={strings.website.tools.tankopedia.meta.sale}>
<Flex align="center" gap="1">
{(protagonist.price.value / 2).toLocaleString(locale)}
{(protagonist.price!.value / 2).toLocaleString(locale)}
<img
style={{ width: '1em', height: '1em' }}
alt={TankPriceType[protagonist.price.type]}
style={{ width: "1em", height: "1em" }}
alt={TankPriceType[protagonist.price!.type]}
src={asset(
`icons/currencies/${
protagonist.price.type === TankPriceType.GOLD
? 'gold'
: 'silver'
protagonist.price!.type ===
TankPriceType.TANK_PRICE_TYPE_GOLD
? "gold"
: "silver"
}.webp`,
)}
/>
@@ -188,9 +190,9 @@ export function MetaSection() {
.value as number
).toLocaleString(locale)}
<img
style={{ width: '1em', height: '1em' }}
style={{ width: "1em", height: "1em" }}
alt="xp"
src={asset('icons/currencies/xp.webp')}
src={asset("icons/currencies/xp.webp")}
/>
</Flex>
</Listing>
@@ -36,7 +36,7 @@ export function Node({ id, highlight, nextIds, skeleton }: NodeProps) {
? 0
: (tank.research_cost.research_cost_type!.value as number) /
xpMultiplier),
0
0,
);
const averages = averageDefinitions.averages[tank.id]?.mu;
const averageXp = averages ? averages.xp / averages.battles : undefined;
@@ -78,7 +78,7 @@ export function Node({ id, highlight, nextIds, skeleton }: NodeProps) {
}}
>
<img
alt={unwrap(tank.name)}
alt={unwrap(tank.name!)}
src={asset(`icons/tanks/big/${id}.webp`)}
width={64}
height={64}
@@ -92,7 +92,7 @@ export function Node({ id, highlight, nextIds, skeleton }: NodeProps) {
<Text color="gray" size="1">
{TIER_ROMAN_NUMERALS[tank.tier]}
</Text>
<Text wrap="nowrap">{unwrap(tank.name)}</Text>
<Text wrap="nowrap">{unwrap(tank.name!)}</Text>
</Flex>
<Flex gap="2" align="center">
<Text color="gray" size="1" wrap="nowrap">
@@ -122,7 +122,7 @@ export function Node({ id, highlight, nextIds, skeleton }: NodeProps) {
objectPosition: "center",
}}
/>
{formatCompact(locale, tank.price.value)}
{formatCompact(locale, tank.price!.value)}
</Flex>
</Text>
</Flex>
@@ -135,7 +135,7 @@ export function Node({ id, highlight, nextIds, skeleton }: NodeProps) {
? strings.website.tools.tankopedia.tech_tree.battle
: literals(
strings.website.tools.tankopedia.tech_tree.battles,
{ count: games }
{ count: games },
))}
</Text>
)}
@@ -26,14 +26,18 @@ export function TechTreeSection({ skeleton }: MaybeSkeletonComponentProps) {
function extend(line: Line): Line[] {
const root = tankDefinitions.tanks[line.at(-1)!];
if (root.ancestors === undefined || root.ancestors.length === 0 || root.tier === 1) {
if (
root.ancestors === undefined ||
root.ancestors.length === 0 ||
root.tier === 1
) {
return [line];
} else {
if (root.ancestors.length === 1 || root.tier === 2) {
line.push(
root.ancestors.find(
(ancestor) => !tankDefinitions.tanks[ancestor].deprecated
) ?? root.ancestors[0]
(ancestor) => !tankDefinitions.tanks[ancestor].deprecated,
) ?? root.ancestors[0],
);
return extend(line);
} else {
@@ -52,7 +56,7 @@ export function TechTreeSection({ skeleton }: MaybeSkeletonComponentProps) {
const [lineIndex, setLineIndex] = useState(0);
const line = useMemo(
() => (lines.length === 0 ? [master.id] : [...lines[lineIndex]].reverse()),
[master, lineIndex]
[master, lineIndex],
);
const totalXp = line.reduce(
(xp, id) =>
@@ -62,11 +66,11 @@ export function TechTreeSection({ skeleton }: MaybeSkeletonComponentProps) {
? 0
: (tankDefinitions.tanks[id].research_cost.research_cost_type!
.value as number)),
0
0,
);
const totalCredits = line.reduce(
(credits, id) => credits + tankDefinitions.tanks[id].price.value,
0
(credits, id) => credits + tankDefinitions.tanks[id].price!.value,
0,
);
useEffect(() => {
@@ -75,7 +79,7 @@ export function TechTreeSection({ skeleton }: MaybeSkeletonComponentProps) {
});
if (
master.type !== TankType.RESEARCHABLE ||
master.type !== TankType.TANK_TYPE_RESEARCHABLE ||
master.ancestors === undefined ||
master.ancestors.length === 0
) {