mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 18:57:03 +02:00
fix: some linting for new proto 3
This commit is contained in:
1 parent
79bc259565
commit
f14ca394b5
29 files changed
+522
-418
No files matched your search
@@ -24,8 +24,7 @@ export * from './rating';
|
||||
export * from './regions';
|
||||
export * from './regionSubdomainToRegion';
|
||||
export * from './regionToRegionSubdomain';
|
||||
export * from './resolveNearPenetration';
|
||||
export * from './searchClansAcrossRegions';
|
||||
export * from './searchCurrentRatingPlayers';
|
||||
export * from './searchPlayersAcrossRegions';
|
||||
export * from './toUniqueId';
|
||||
export * from './toUniqueId';
|
||||
@@ -1,5 +0,0 @@
|
||||
import { Vector2Tuple } from 'three';
|
||||
|
||||
export function resolveNearPenetration(penetration: number | Vector2Tuple) {
|
||||
return typeof penetration === 'number' ? penetration : penetration[0];
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { forEachRight } from 'lodash-es';
|
||||
import { GunDefinition, TurretDefinition } from './tankDefinitions';
|
||||
import { GunDefinition, TurretDefinition } from '../protos';
|
||||
|
||||
export function uniqueGuns(turrets: TurretDefinition[]) {
|
||||
const gunIds = new Set<number>();
|
||||
@@ -7,8 +7,8 @@ export function uniqueGuns(turrets: TurretDefinition[]) {
|
||||
|
||||
forEachRight(turrets, (turret) => {
|
||||
forEachRight(turret.guns, (gun) => {
|
||||
if (!gunIds.has(gun.id)) {
|
||||
gunIds.add(gun.id);
|
||||
if (!gunIds.has(gun.gunType!.value.base.id)) {
|
||||
gunIds.add(gun.gunType!.value.base.id);
|
||||
guns.push({ turret, gun });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -70,7 +70,7 @@ message EngineDefinition {
|
||||
required string name = 2;
|
||||
optional uint32 xp = 3;
|
||||
required Tier tier = 4;
|
||||
optional float fire_chance = 5;
|
||||
required float fire_chance = 5;
|
||||
required uint32 power = 6;
|
||||
required uint32 weight = 7;
|
||||
repeated Unlock unlocks = 8;
|
||||
@@ -98,8 +98,8 @@ message GunDefinition {
|
||||
}
|
||||
|
||||
message GunDefinitionRegular {
|
||||
required GunDefinitionBase gun = 1;
|
||||
required GunDefinitionRegularProperties regular = 2;
|
||||
required GunDefinitionBase base = 1;
|
||||
required GunDefinitionRegularProperties extension = 2;
|
||||
}
|
||||
|
||||
message GunDefinitionRegularProperties {
|
||||
@@ -107,8 +107,8 @@ message GunDefinitionRegularProperties {
|
||||
}
|
||||
|
||||
message GunDefinitionAutoLoader {
|
||||
required GunDefinitionBase gun = 1;
|
||||
required GunDefinitionAutoLoaderProperties auto_loader = 2;
|
||||
required GunDefinitionBase base = 1;
|
||||
required GunDefinitionAutoLoaderProperties extension = 2;
|
||||
}
|
||||
|
||||
message GunDefinitionAutoLoaderProperties {
|
||||
@@ -118,8 +118,8 @@ message GunDefinitionAutoLoaderProperties {
|
||||
}
|
||||
|
||||
message GunDefinitionAutoReloader {
|
||||
required GunDefinitionBase gun = 1;
|
||||
required GunDefinitionAutoReloaderProperties auto_reloader = 2;
|
||||
required GunDefinitionBase base = 1;
|
||||
required GunDefinitionAutoReloaderProperties extension = 2;
|
||||
}
|
||||
|
||||
message GunDefinitionAutoReloaderProperties {
|
||||
|
||||
@@ -12,7 +12,9 @@ interface BodyProps {
|
||||
|
||||
export function Body({ stats }: BodyProps) {
|
||||
const members = CompareEphemeral.use((state) => state.members);
|
||||
const hasNonRegularGun = members.some(({ gun }) => gun.type !== 'regular');
|
||||
const hasNonRegularGun = members.some(
|
||||
({ gun }) => gun.gunType!.$case !== 'regular',
|
||||
);
|
||||
const mutateCompareEphemeral = CompareEphemeral.useMutation();
|
||||
|
||||
return (
|
||||
@@ -29,35 +31,43 @@ export function Body({ stats }: BodyProps) {
|
||||
<StickyColumnHeaderCell key={key} top={137}>
|
||||
<Flex justify="center">
|
||||
<Flex>
|
||||
{gun.shells.map((thisShell, shellIndex) => (
|
||||
<IconButton
|
||||
color={thisShell.id === shell.id ? undefined : 'gray'}
|
||||
variant="soft"
|
||||
key={thisShell.id}
|
||||
style={{
|
||||
borderTopLeftRadius: shellIndex === 0 ? undefined : 0,
|
||||
borderBottomLeftRadius:
|
||||
shellIndex === 0 ? undefined : 0,
|
||||
borderTopRightRadius:
|
||||
shellIndex === gun.shells.length - 1 ? undefined : 0,
|
||||
borderBottomRightRadius:
|
||||
shellIndex === gun.shells.length - 1 ? undefined : 0,
|
||||
marginLeft: shellIndex === 0 ? 0 : -1,
|
||||
}}
|
||||
onClick={() => {
|
||||
mutateCompareEphemeral((draft) => {
|
||||
draft.members[index].shell = thisShell;
|
||||
});
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt={thisShell.name}
|
||||
width={16}
|
||||
height={16}
|
||||
src={asset(`icons/shells/${thisShell.icon}.webp`)}
|
||||
/>
|
||||
</IconButton>
|
||||
))}
|
||||
{gun.gunType!.value.gun.shells.map(
|
||||
(thisShell, shellIndex) => (
|
||||
<IconButton
|
||||
color={thisShell.id === shell.id ? undefined : 'gray'}
|
||||
variant="soft"
|
||||
key={thisShell.id}
|
||||
style={{
|
||||
borderTopLeftRadius: shellIndex === 0 ? undefined : 0,
|
||||
borderBottomLeftRadius:
|
||||
shellIndex === 0 ? undefined : 0,
|
||||
borderTopRightRadius:
|
||||
shellIndex ===
|
||||
gun.gunType!.value.gun.shells.length - 1
|
||||
? undefined
|
||||
: 0,
|
||||
borderBottomRightRadius:
|
||||
shellIndex ===
|
||||
gun.gunType!.value.gun.shells.length - 1
|
||||
? undefined
|
||||
: 0,
|
||||
marginLeft: shellIndex === 0 ? 0 : -1,
|
||||
}}
|
||||
onClick={() => {
|
||||
mutateCompareEphemeral((draft) => {
|
||||
draft.members[index].shell = thisShell;
|
||||
});
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt={thisShell.name}
|
||||
width={16}
|
||||
height={16}
|
||||
src={asset(`icons/shells/${thisShell.icon}.webp`)}
|
||||
/>
|
||||
</IconButton>
|
||||
),
|
||||
)}
|
||||
</Flex>
|
||||
</Flex>
|
||||
</StickyColumnHeaderCell>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import {
|
||||
asset,
|
||||
availableProvisions,
|
||||
consumableDefinitions,
|
||||
equipmentDefinitions,
|
||||
fetchConsumableDefinitions,
|
||||
fetchEquipmentDefinitions,
|
||||
fetchProvisionDefinitions,
|
||||
fetchSkillDefinitions,
|
||||
permanentSkills,
|
||||
provisionDefinitions,
|
||||
skillDefinitions,
|
||||
} from '@blitzkit/core';
|
||||
import { checkConsumableProvisionInclusivity } from '@blitzkit/core/src/blitzkit/checkConsumableProvisionInclusivity';
|
||||
import { ComponentPlaceholderIcon } from '@radix-ui/react-icons';
|
||||
@@ -31,10 +31,10 @@ interface CompareTableProps {
|
||||
stats: TankCharacteristics[];
|
||||
}
|
||||
|
||||
const awaitedSkillDefinitions = await skillDefinitions;
|
||||
const awaitedEquipmentDefinitions = await equipmentDefinitions;
|
||||
const awaitedConsumableDefinitions = await consumableDefinitions;
|
||||
const awaitedProvisionDefinitions = await provisionDefinitions;
|
||||
const skillDefinitions = await fetchSkillDefinitions();
|
||||
const equipmentDefinitions = await fetchEquipmentDefinitions();
|
||||
const consumableDefinitions = await fetchConsumableDefinitions();
|
||||
const provisionDefinitions = await fetchProvisionDefinitions();
|
||||
|
||||
export function CompareTable({ stats }: CompareTableProps) {
|
||||
const crewSkills = CompareEphemeral.use((state) => state.crewSkills);
|
||||
@@ -84,10 +84,10 @@ export function CompareTable({ stats }: CompareTableProps) {
|
||||
<Popover.Trigger>
|
||||
<Button variant="ghost" radius="large">
|
||||
<Flex direction="column" style={{ gap: 2 }}>
|
||||
{Object.entries(awaitedSkillDefinitions.classes).map(
|
||||
{Object.entries(skillDefinitions.classes).map(
|
||||
([tankClass, skills]) => (
|
||||
<Flex key={tankClass} style={{ gap: 2 }}>
|
||||
{skills.map((skill) => (
|
||||
{skills.skills.map((skill) => (
|
||||
<div
|
||||
key={skill}
|
||||
style={{
|
||||
@@ -169,14 +169,14 @@ export function CompareTable({ stats }: CompareTableProps) {
|
||||
index,
|
||||
) => {
|
||||
const equipmentPreset =
|
||||
awaitedEquipmentDefinitions.presets[tank.equipment];
|
||||
equipmentDefinitions.presets[tank.equipmentPreset];
|
||||
const provisionsList = availableProvisions(
|
||||
tank,
|
||||
gun,
|
||||
awaitedProvisionDefinitions,
|
||||
provisionDefinitions,
|
||||
);
|
||||
const consumablesList = Object.values(
|
||||
awaitedConsumableDefinitions,
|
||||
consumableDefinitions,
|
||||
).filter((consumable) =>
|
||||
checkConsumableProvisionInclusivity(consumable, tank, gun),
|
||||
);
|
||||
@@ -238,7 +238,8 @@ export function CompareTable({ stats }: CompareTableProps) {
|
||||
|
||||
member.turret = member.tank.turrets[0];
|
||||
member.gun = member.turret.guns[0];
|
||||
member.shell = member.gun.shells[0];
|
||||
member.shell =
|
||||
member.gun.gunType!.value.base.shells[0];
|
||||
member.engine = member.tank.engines[0];
|
||||
member.track = member.tank.tracks[0];
|
||||
});
|
||||
@@ -254,7 +255,8 @@ export function CompareTable({ stats }: CompareTableProps) {
|
||||
|
||||
member.turret = member.tank.turrets.at(-1)!;
|
||||
member.gun = member.turret.guns.at(-1)!;
|
||||
member.shell = member.gun.shells[0];
|
||||
member.shell =
|
||||
member.gun.gunType!.value.base.shells[0];
|
||||
member.engine = member.tank.engines.at(-1)!;
|
||||
member.track = member.tank.tracks.at(-1)!;
|
||||
});
|
||||
@@ -282,7 +284,8 @@ export function CompareTable({ stats }: CompareTableProps) {
|
||||
{provisions.map((provision, index) => (
|
||||
<img
|
||||
alt={
|
||||
awaitedProvisionDefinitions[provision].name
|
||||
provisionDefinitions.provisions[provision]
|
||||
.name
|
||||
}
|
||||
key={provision}
|
||||
src={asset(
|
||||
@@ -326,7 +329,7 @@ export function CompareTable({ stats }: CompareTableProps) {
|
||||
(provision) => provision.id,
|
||||
)}
|
||||
selected={provisions}
|
||||
disabled={tank.provisions === provisions.length}
|
||||
disabled={tank.maxProvisions === provisions.length}
|
||||
onChange={(provisions) => {
|
||||
mutateCompareEphemeral((draft) => {
|
||||
draft.members[index].provisions = provisions;
|
||||
@@ -462,8 +465,9 @@ export function CompareTable({ stats }: CompareTableProps) {
|
||||
{consumables.map((consumable, index) => (
|
||||
<img
|
||||
alt={
|
||||
awaitedConsumableDefinitions[consumable]
|
||||
.name
|
||||
consumableDefinitions.consumables[
|
||||
consumable
|
||||
].name
|
||||
}
|
||||
key={consumable}
|
||||
src={asset(
|
||||
@@ -510,7 +514,9 @@ export function CompareTable({ stats }: CompareTableProps) {
|
||||
draft.members[index].consumables = consumables;
|
||||
});
|
||||
}}
|
||||
disabled={tank.consumables === consumables.length}
|
||||
disabled={
|
||||
tank.maxConsumables === consumables.length
|
||||
}
|
||||
/>
|
||||
|
||||
<Flex justify="end" mt="4">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { provisionDefinitions } from '@blitzkit/core';
|
||||
import { fetchProvisionDefinitions } from '@blitzkit/core';
|
||||
import { PlusIcon, TrashIcon } from '@radix-ui/react-icons';
|
||||
import { Button, Dialog, Flex, SegmentedControl } from '@radix-ui/themes';
|
||||
import { tankToCompareMember } from '../../core/blitzkit/tankToCompareMember';
|
||||
@@ -14,7 +14,7 @@ interface ControlsProps {
|
||||
onAddTankDialogOpenChange: (open: boolean) => void;
|
||||
}
|
||||
|
||||
const awaitedProvisionDefinitions = await provisionDefinitions;
|
||||
const provisionDefinitions = await fetchProvisionDefinitions();
|
||||
|
||||
export function Controls({
|
||||
addTankDialogOpen,
|
||||
@@ -51,7 +51,7 @@ export function Controls({
|
||||
onSelect={(tank) => {
|
||||
mutateCompareEphemeral((draft) => {
|
||||
draft.members.push(
|
||||
tankToCompareMember(tank, awaitedProvisionDefinitions),
|
||||
tankToCompareMember(tank, provisionDefinitions),
|
||||
);
|
||||
draft.sorting = undefined;
|
||||
});
|
||||
@@ -61,7 +61,7 @@ export function Controls({
|
||||
mutateCompareEphemeral((draft) => {
|
||||
draft.members.push(
|
||||
...tanks.map((tank) =>
|
||||
tankToCompareMember(tank, awaitedProvisionDefinitions),
|
||||
tankToCompareMember(tank, provisionDefinitions),
|
||||
),
|
||||
);
|
||||
draft.sorting = undefined;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { provisionDefinitions } from '@blitzkit/core';
|
||||
import { fetchProvisionDefinitions } from '@blitzkit/core';
|
||||
import { ExternalLinkIcon, LoopIcon, TrashIcon } from '@radix-ui/react-icons';
|
||||
import { Dialog, Flex, IconButton } from '@radix-ui/themes';
|
||||
import Link from 'next/link';
|
||||
@@ -12,7 +12,7 @@ interface TankControlProps {
|
||||
id: number;
|
||||
}
|
||||
|
||||
const awaitedProvisionDefinitions = await provisionDefinitions;
|
||||
const provisionDefinitions = await fetchProvisionDefinitions();
|
||||
|
||||
export function TankControl({ index, id }: TankControlProps) {
|
||||
const [switchTankDialogOpen, setSwitchTankDialogOpen] = useState(false);
|
||||
@@ -58,7 +58,7 @@ export function TankControl({ index, id }: TankControlProps) {
|
||||
mutateCompareEphemeral((draft) => {
|
||||
draft.members[index] = tankToCompareMember(
|
||||
tank,
|
||||
awaitedProvisionDefinitions,
|
||||
provisionDefinitions,
|
||||
);
|
||||
draft.sorting = undefined;
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { EquipmentPreset } from '@blitzkit/core';
|
||||
import { Flex } from '@radix-ui/themes';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { chunk, cloneDeep } from 'lodash-es';
|
||||
import type { EquipmentMatrix } from '../stores/duel';
|
||||
import { EquipmentButton } from './ModuleButtons/EquipmentButton';
|
||||
|
||||
@@ -17,12 +17,12 @@ export function EquipmentManager({
|
||||
}: EquipmentManagerProps) {
|
||||
return (
|
||||
<Flex direction="column" gap="2">
|
||||
{preset.map((equipmentRow, rowIndex) => (
|
||||
{chunk(preset.slots, 3).map((equipmentRow, rowIndex) => (
|
||||
<Flex gap="2" key={rowIndex}>
|
||||
{equipmentRow.map((equipment, columnIndex) => (
|
||||
<Flex key={columnIndex}>
|
||||
<EquipmentButton
|
||||
equipment={equipment[0]}
|
||||
equipment={equipment.left}
|
||||
style={{
|
||||
borderTopRightRadius: 0,
|
||||
borderBottomRightRadius: 0,
|
||||
@@ -41,7 +41,7 @@ export function EquipmentManager({
|
||||
}}
|
||||
/>
|
||||
<EquipmentButton
|
||||
equipment={equipment[1]}
|
||||
equipment={equipment.right}
|
||||
style={{
|
||||
borderTopLeftRadius: 0,
|
||||
borderBottomLeftRadius: 0,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { asset, equipmentDefinitions } from '@blitzkit/core';
|
||||
import { asset, fetchEquipmentDefinitions } from '@blitzkit/core';
|
||||
import { GenericTankComponentButton } from './GenericTankComponentButton';
|
||||
import type { TankComponentButtonProps } from './TankComponentButton';
|
||||
|
||||
@@ -6,12 +6,12 @@ interface EquipmentButtonProps extends TankComponentButtonProps {
|
||||
equipment: number;
|
||||
}
|
||||
|
||||
const awaitedEquipmentDefinitions = await equipmentDefinitions;
|
||||
const equipmentDefinitions = await fetchEquipmentDefinitions();
|
||||
|
||||
export function EquipmentButton({ equipment, ...props }: EquipmentButtonProps) {
|
||||
return (
|
||||
<GenericTankComponentButton
|
||||
tooltip={awaitedEquipmentDefinitions.equipments[equipment].name}
|
||||
tooltip={equipmentDefinitions.equipments[equipment].name}
|
||||
icon={asset(`icons/equipment/${equipment}.webp`)}
|
||||
{...props}
|
||||
/>
|
||||
|
||||
@@ -60,7 +60,7 @@ export function ModuleManager({
|
||||
const draft = { ...modules };
|
||||
draft.turret = turret;
|
||||
draft.gun = draft.turret.guns.at(-1)!;
|
||||
draft.shell = draft.gun.shells[0];
|
||||
draft.shell = draft.gun.gunType!.value.base.shells[0];
|
||||
|
||||
onChange?.(draft);
|
||||
setTurretMenuOpen(false);
|
||||
@@ -81,7 +81,9 @@ export function ModuleManager({
|
||||
|
||||
<ModuleButton
|
||||
module="gun"
|
||||
discriminator={TIER_ROMAN_NUMERALS[modules.gun.tier]}
|
||||
discriminator={
|
||||
TIER_ROMAN_NUMERALS[modules.gun.gunType!.value.base.tier]
|
||||
}
|
||||
onClick={() => setGunMenuOpen(true)}
|
||||
/>
|
||||
|
||||
@@ -91,16 +93,18 @@ export function ModuleManager({
|
||||
</DropdownMenu.Trigger>
|
||||
|
||||
<DropdownMenu.Content>
|
||||
<DropdownMenu.RadioGroup value={`${modules.gun.id}`}>
|
||||
<DropdownMenu.RadioGroup
|
||||
value={`${modules.gun.gunType!.value.base.id}`}
|
||||
>
|
||||
{[...modules.turret.guns].reverse().map((gun) => {
|
||||
return (
|
||||
<DropdownMenu.RadioItem
|
||||
key={gun.id}
|
||||
value={`${gun.id}`}
|
||||
key={gun.gunType!.value.base.id}
|
||||
value={`${gun.gunType!.value.base.id}`}
|
||||
onClick={() => {
|
||||
const draft = { ...modules };
|
||||
draft.gun = gun;
|
||||
draft.shell = draft.gun.shells[0];
|
||||
draft.shell = draft.gun.gunType!.value.base.shells[0];
|
||||
|
||||
onChange?.(draft);
|
||||
setGunMenuOpen(false);
|
||||
@@ -109,9 +113,11 @@ export function ModuleManager({
|
||||
<ModuleButton
|
||||
variant="ghost"
|
||||
module="gun"
|
||||
discriminator={TIER_ROMAN_NUMERALS[gun.tier]}
|
||||
discriminator={
|
||||
TIER_ROMAN_NUMERALS[gun.gunType!.value.base.tier]
|
||||
}
|
||||
/>{' '}
|
||||
{gun.name}
|
||||
{gun.gunType!.value.base.name}
|
||||
</DropdownMenu.RadioItem>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import {
|
||||
asset,
|
||||
modelDefinitions,
|
||||
fetchModelDefinitions,
|
||||
normalizeBoundingBox,
|
||||
resolveDpm,
|
||||
resolveNearPenetration,
|
||||
tankIcon,
|
||||
TankType,
|
||||
unionBoundingBox,
|
||||
type TankDefinition,
|
||||
} from '@blitzkit/core';
|
||||
@@ -13,7 +13,6 @@ import { Flex, Link, Text } from '@radix-ui/themes';
|
||||
import { assignInlineVars } from '@vanilla-extract/dynamic';
|
||||
import { useMemo } from 'react';
|
||||
import { resolveReload } from '../../../core/blitzkit/resolveReload';
|
||||
import { useAwait } from '../../../hooks/useAwait';
|
||||
import { $tankopediaSort } from '../../../stores/tankopediaSort';
|
||||
import { classIcons } from '../../ClassIcon';
|
||||
import * as styles from './TankCard.css';
|
||||
@@ -23,8 +22,9 @@ interface TankCardProps {
|
||||
onSelect?: (tank: TankDefinition) => void;
|
||||
}
|
||||
|
||||
const modelDefinitions = await fetchModelDefinitions();
|
||||
|
||||
export function TankCard({ tank, onSelect }: TankCardProps) {
|
||||
const awaitedModelDefinitions = useAwait(modelDefinitions);
|
||||
const Icon = classIcons[tank.class];
|
||||
const tankopediaSort = useStore($tankopediaSort);
|
||||
const discriminator = useMemo(() => {
|
||||
@@ -32,25 +32,26 @@ export function TankCard({ tank, onSelect }: TankCardProps) {
|
||||
|
||||
const turret = tank.turrets.at(-1)!;
|
||||
const gun = turret.guns.at(-1)!;
|
||||
const shell0 = gun.shells[0];
|
||||
const shell1 = gun.shells[1];
|
||||
const shell0 = gun.gunType!.value.base.shells[0];
|
||||
const shell1 = gun.gunType!.value.base.shells[1];
|
||||
const tracks = tank.tracks.at(-1)!;
|
||||
const engine = tank.engines.at(-1)!;
|
||||
const tankModelDefinition = awaitedModelDefinitions[tank.id];
|
||||
const tankModelDefinition = modelDefinitions.models[tank.id];
|
||||
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
||||
const gunModelDefinition = turretModelDefinition.guns[gun.id];
|
||||
const gunModelDefinition =
|
||||
turretModelDefinition.guns[gun.gunType!.value.base.id];
|
||||
|
||||
switch (tankopediaSort.by) {
|
||||
case 'fire.aimTime':
|
||||
return gun.aimTime.toFixed(2);
|
||||
return gun.gunType!.value.base.aimTime.toFixed(2);
|
||||
case 'fire.caliber':
|
||||
return shell0.caliber.toFixed(0);
|
||||
case 'fire.damage':
|
||||
return shell0.damage.armor.toFixed(0);
|
||||
return shell0.armorDamage.toFixed(0);
|
||||
case 'fire.dispersionMoving':
|
||||
return tracks.dispersion.move.toFixed(3);
|
||||
return tracks.dispersionMove.toFixed(3);
|
||||
case 'fire.dispersionStill':
|
||||
return gun.dispersion.base.toFixed(3);
|
||||
return gun.gunType!.value.base.dispersionBase.toFixed(3);
|
||||
case 'fire.dpm':
|
||||
return resolveDpm(gun, shell0).toFixed(0);
|
||||
case 'fire.dpmPremium':
|
||||
@@ -58,27 +59,25 @@ export function TankCard({ tank, onSelect }: TankCardProps) {
|
||||
case 'fire.reload':
|
||||
return resolveReload(gun).toFixed(2);
|
||||
case 'fire.standardPenetration':
|
||||
return resolveNearPenetration(shell0.penetration).toFixed(0);
|
||||
return shell0.penetration.near.toFixed(0);
|
||||
case 'fire.premiumPenetration':
|
||||
return shell1
|
||||
? resolveNearPenetration(shell1.penetration).toFixed(0)
|
||||
: '--';
|
||||
return shell1 ? shell1.penetration.near.toFixed(0) : '--';
|
||||
case 'fire.shellVelocity':
|
||||
return shell0.speed.toFixed(0);
|
||||
return shell0.velocity.toFixed(0);
|
||||
case 'fire.gunDepression':
|
||||
return (
|
||||
gunModelDefinition.pitch.max +
|
||||
(tankModelDefinition.turretRotation?.pitch ?? 0)
|
||||
(tankModelDefinition.initialTurretRotation?.pitch ?? 0)
|
||||
).toFixed(1);
|
||||
case 'fire.gunElevation':
|
||||
return (
|
||||
-gunModelDefinition.pitch.min -
|
||||
(tankModelDefinition.turretRotation?.pitch ?? 0)
|
||||
(tankModelDefinition.initialTurretRotation?.pitch ?? 0)
|
||||
).toFixed(1);
|
||||
case 'maneuverability.forwardsSpeed':
|
||||
return tank.speed.forwards.toFixed(0);
|
||||
return tank.speedForwards.toFixed(0);
|
||||
case 'maneuverability.backwardsSpeed':
|
||||
return tank.speed.backwards.toFixed(0);
|
||||
return tank.speedBackwards.toFixed(0);
|
||||
case 'maneuverability.power':
|
||||
return engine.power.toFixed(0);
|
||||
case 'maneuverability.powerToWeight':
|
||||
@@ -88,7 +87,7 @@ export function TankCard({ tank, onSelect }: TankCardProps) {
|
||||
engine.weight +
|
||||
tracks.weight +
|
||||
turret.weight +
|
||||
gun.weight)
|
||||
gun.gunType!.value.base.weight)
|
||||
).toFixed(1);
|
||||
case 'maneuverability.weight':
|
||||
return (
|
||||
@@ -96,7 +95,7 @@ export function TankCard({ tank, onSelect }: TankCardProps) {
|
||||
engine.weight +
|
||||
tracks.weight +
|
||||
turret.weight +
|
||||
gun.weight) /
|
||||
gun.gunType!.value.base.weight) /
|
||||
1000
|
||||
).toFixed(1);
|
||||
case 'maneuverability.traverseSpeed':
|
||||
@@ -106,11 +105,15 @@ export function TankCard({ tank, onSelect }: TankCardProps) {
|
||||
case 'survivability.viewRange':
|
||||
return turret.viewRange.toFixed(0);
|
||||
case 'survivability.camouflageStill':
|
||||
return (tank.camouflage.still * 100).toFixed(0);
|
||||
return (tank.camouflageStill * 100).toFixed(0);
|
||||
case 'survivability.camouflageMoving':
|
||||
return (tank.camouflage.moving * 100).toFixed(0);
|
||||
return (tank.camouflageMoving * 100).toFixed(0);
|
||||
case 'survivability.camouflageShooting':
|
||||
return (tank.camouflage.still * gun.camouflageLoss * 100).toFixed(0);
|
||||
return (
|
||||
tank.camouflageStill *
|
||||
gun.gunType!.value.base.camouflageLoss *
|
||||
100
|
||||
).toFixed(0);
|
||||
case 'survivability.volume': {
|
||||
const dimensions = normalizeBoundingBox(
|
||||
unionBoundingBox(
|
||||
@@ -139,13 +142,13 @@ export function TankCard({ tank, onSelect }: TankCardProps) {
|
||||
tabIndex={onSelect ? 0 : undefined}
|
||||
size="1"
|
||||
color={
|
||||
tank.treeType === 'collector'
|
||||
tank.type === TankType.COLLECTOR
|
||||
? 'blue'
|
||||
: tank.treeType === 'premium'
|
||||
: tank.type === TankType.PREMIUM
|
||||
? 'amber'
|
||||
: 'gray'
|
||||
}
|
||||
highContrast={tank.treeType === 'researchable'}
|
||||
highContrast={tank.type === TankType.RESEARCHABLE}
|
||||
onClick={onSelect ? () => onSelect(tank) : undefined}
|
||||
className={styles.card}
|
||||
style={assignInlineVars({
|
||||
|
||||
@@ -182,11 +182,11 @@ export const TankSearch = memo<TankSearchProps>(
|
||||
(a, b) =>
|
||||
resolveDpm(
|
||||
a.turrets.at(-1)!.guns.at(-1)!,
|
||||
a.turrets.at(-1)!.guns.at(-1)!.gunType!.value.gun.shells[0],
|
||||
a.turrets.at(-1)!.guns.at(-1)!.gunType!.value.base.shells[0],
|
||||
) -
|
||||
resolveDpm(
|
||||
b.turrets.at(-1)!.guns.at(-1)!,
|
||||
b.turrets.at(-1)!.guns.at(-1)!.gunType!.value.gun.shells[0],
|
||||
b.turrets.at(-1)!.guns.at(-1)!.gunType!.value.base.shells[0],
|
||||
),
|
||||
);
|
||||
break;
|
||||
@@ -194,17 +194,17 @@ export const TankSearch = memo<TankSearchProps>(
|
||||
case 'fire.dpmPremium':
|
||||
sorted = filtered.sort(
|
||||
(a, b) =>
|
||||
(a.turrets.at(-1)!.guns.at(-1)!.gunType!.value.gun.shells[1]
|
||||
(a.turrets.at(-1)!.guns.at(-1)!.gunType!.value.base.shells[1]
|
||||
? resolveDpm(
|
||||
a.turrets.at(-1)!.guns.at(-1)!,
|
||||
a.turrets.at(-1)!.guns.at(-1)!.gunType!.value.gun
|
||||
a.turrets.at(-1)!.guns.at(-1)!.gunType!.value.base
|
||||
.shells[1],
|
||||
)
|
||||
: 0) -
|
||||
(b.turrets.at(-1)!.guns.at(-1)!.gunType!.value.gun.shells[1]
|
||||
(b.turrets.at(-1)!.guns.at(-1)!.gunType!.value.base.shells[1]
|
||||
? resolveDpm(
|
||||
b.turrets.at(-1)!.guns.at(-1)!,
|
||||
b.turrets.at(-1)!.guns.at(-1)!.gunType!.value.gun
|
||||
b.turrets.at(-1)!.guns.at(-1)!.gunType!.value.base
|
||||
.shells[1],
|
||||
)
|
||||
: 0),
|
||||
@@ -222,9 +222,9 @@ export const TankSearch = memo<TankSearchProps>(
|
||||
case 'fire.caliber':
|
||||
sorted = filtered.sort(
|
||||
(a, b) =>
|
||||
a.turrets.at(-1)!.guns.at(-1)!.gunType!.value.gun.shells[0]
|
||||
a.turrets.at(-1)!.guns.at(-1)!.gunType!.value.base.shells[0]
|
||||
.caliber -
|
||||
b.turrets.at(-1)!.guns.at(-1)!.gunType!.value.gun.shells[0]
|
||||
b.turrets.at(-1)!.guns.at(-1)!.gunType!.value.base.shells[0]
|
||||
.caliber,
|
||||
);
|
||||
break;
|
||||
@@ -232,9 +232,9 @@ export const TankSearch = memo<TankSearchProps>(
|
||||
case 'fire.standardPenetration':
|
||||
sorted = filtered.sort(
|
||||
(a, b) =>
|
||||
a.turrets.at(-1)!.guns.at(-1)!.gunType!.value.gun.shells[0]
|
||||
a.turrets.at(-1)!.guns.at(-1)!.gunType!.value.base.shells[0]
|
||||
.penetration.near -
|
||||
b.turrets.at(-1)!.guns.at(-1)!.gunType!.value.gun.shells[0]
|
||||
b.turrets.at(-1)!.guns.at(-1)!.gunType!.value.base.shells[0]
|
||||
.penetration.near,
|
||||
);
|
||||
break;
|
||||
@@ -242,9 +242,9 @@ export const TankSearch = memo<TankSearchProps>(
|
||||
case 'fire.premiumPenetration':
|
||||
sorted = filtered.sort(
|
||||
(a, b) =>
|
||||
(a.turrets.at(-1)!.guns.at(-1)!.gunType!.value.gun.shells[1]
|
||||
(a.turrets.at(-1)!.guns.at(-1)!.gunType!.value.base.shells[1]
|
||||
?.penetration.near ?? 0) -
|
||||
(b.turrets.at(-1)!.guns.at(-1)!.gunType!.value.gun.shells[1]
|
||||
(b.turrets.at(-1)!.guns.at(-1)!.gunType!.value.base.shells[1]
|
||||
?.penetration.near ?? 0),
|
||||
);
|
||||
break;
|
||||
@@ -252,9 +252,9 @@ export const TankSearch = memo<TankSearchProps>(
|
||||
case 'fire.damage':
|
||||
sorted = filtered.sort(
|
||||
(a, b) =>
|
||||
a.turrets.at(-1)!.guns.at(-1)!.gunType!.value.gun.shells[0]
|
||||
a.turrets.at(-1)!.guns.at(-1)!.gunType!.value.base.shells[0]
|
||||
.armorDamage -
|
||||
b.turrets.at(-1)!.guns.at(-1)!.gunType!.value.gun.shells[0]
|
||||
b.turrets.at(-1)!.guns.at(-1)!.gunType!.value.base.shells[0]
|
||||
.armorDamage,
|
||||
);
|
||||
break;
|
||||
@@ -262,9 +262,9 @@ export const TankSearch = memo<TankSearchProps>(
|
||||
case 'fire.shellVelocity':
|
||||
sorted = filtered.sort(
|
||||
(a, b) =>
|
||||
a.turrets.at(-1)!.guns.at(-1)!.gunType!.value.gun.shells[0]
|
||||
a.turrets.at(-1)!.guns.at(-1)!.gunType!.value.base.shells[0]
|
||||
.velocity -
|
||||
b.turrets.at(-1)!.guns.at(-1)!.gunType!.value.gun.shells[0]
|
||||
b.turrets.at(-1)!.guns.at(-1)!.gunType!.value.base.shells[0]
|
||||
.velocity,
|
||||
);
|
||||
break;
|
||||
@@ -272,17 +272,17 @@ export const TankSearch = memo<TankSearchProps>(
|
||||
case 'fire.aimTime':
|
||||
sorted = filtered.sort(
|
||||
(a, b) =>
|
||||
a.turrets.at(-1)!.guns.at(-1)!.gunType!.value.gun.aimTime -
|
||||
b.turrets.at(-1)!.guns.at(-1)!.gunType!.value.gun.aimTime,
|
||||
a.turrets.at(-1)!.guns.at(-1)!.gunType!.value.base.aimTime -
|
||||
b.turrets.at(-1)!.guns.at(-1)!.gunType!.value.base.aimTime,
|
||||
);
|
||||
break;
|
||||
|
||||
case 'fire.dispersionStill':
|
||||
sorted = filtered.sort(
|
||||
(a, b) =>
|
||||
a.turrets.at(-1)!.guns.at(-1)!.gunType!.value.gun
|
||||
a.turrets.at(-1)!.guns.at(-1)!.gunType!.value.base
|
||||
.dispersionBase -
|
||||
b.turrets.at(-1)!.guns.at(-1)!.gunType!.value.gun
|
||||
b.turrets.at(-1)!.guns.at(-1)!.gunType!.value.base
|
||||
.dispersionBase,
|
||||
);
|
||||
break;
|
||||
@@ -299,12 +299,12 @@ export const TankSearch = memo<TankSearchProps>(
|
||||
sorted = filtered.sort(
|
||||
(a, b) =>
|
||||
modelDefinitions.models[a.id].turrets[a.turrets.at(-1)!.id]
|
||||
.guns[a.turrets.at(-1)!.guns.at(-1)!.gunType!.value.gun.id]
|
||||
.guns[a.turrets.at(-1)!.guns.at(-1)!.gunType!.value.base.id]
|
||||
.pitch.max +
|
||||
(modelDefinitions.models[a.id].initialTurretRotation?.pitch ??
|
||||
0) -
|
||||
modelDefinitions.models[b.id].turrets[b.turrets.at(-1)!.id]
|
||||
.guns[b.turrets.at(-1)!.guns.at(-1)!.gunType!.value.gun.id]
|
||||
.guns[b.turrets.at(-1)!.guns.at(-1)!.gunType!.value.base.id]
|
||||
.pitch.max -
|
||||
(modelDefinitions.models[b.id].initialTurretRotation?.pitch ??
|
||||
0),
|
||||
@@ -315,12 +315,12 @@ export const TankSearch = memo<TankSearchProps>(
|
||||
sorted = filtered.sort(
|
||||
(a, b) =>
|
||||
modelDefinitions.models[b.id].turrets[b.turrets.at(-1)!.id]
|
||||
.guns[b.turrets.at(-1)!.guns.at(-1)!.gunType!.value.gun.id]
|
||||
.guns[b.turrets.at(-1)!.guns.at(-1)!.gunType!.value.base.id]
|
||||
.pitch.min +
|
||||
(modelDefinitions.models[b.id].initialTurretRotation?.pitch ??
|
||||
0) -
|
||||
modelDefinitions.models[a.id].turrets[a.turrets.at(-1)!.id]
|
||||
.guns[a.turrets.at(-1)!.guns.at(-1)!.gunType!.value.gun.id]
|
||||
.guns[a.turrets.at(-1)!.guns.at(-1)!.gunType!.value.base.id]
|
||||
.pitch.min -
|
||||
(modelDefinitions.models[a.id].initialTurretRotation?.pitch ??
|
||||
0),
|
||||
@@ -351,13 +351,13 @@ export const TankSearch = memo<TankSearchProps>(
|
||||
a.engines.at(-1)!.weight +
|
||||
a.tracks.at(-1)!.weight +
|
||||
a.turrets.at(-1)!.weight +
|
||||
a.turrets.at(-1)!.guns.at(-1)!.gunType!.value.gun.weight) -
|
||||
a.turrets.at(-1)!.guns.at(-1)!.gunType!.value.base.weight) -
|
||||
b.engines.at(-1)!.power /
|
||||
(b.weight +
|
||||
b.engines.at(-1)!.weight +
|
||||
b.tracks.at(-1)!.weight +
|
||||
b.turrets.at(-1)!.weight +
|
||||
b.turrets.at(-1)!.guns.at(-1)!.gunType!.value.gun.weight),
|
||||
b.turrets.at(-1)!.guns.at(-1)!.gunType!.value.base.weight),
|
||||
);
|
||||
break;
|
||||
|
||||
@@ -368,12 +368,12 @@ export const TankSearch = memo<TankSearchProps>(
|
||||
a.engines.at(-1)!.weight +
|
||||
a.tracks.at(-1)!.weight +
|
||||
a.turrets.at(-1)!.weight +
|
||||
a.turrets.at(-1)!.guns.at(-1)!.gunType!.value.gun.weight -
|
||||
a.turrets.at(-1)!.guns.at(-1)!.gunType!.value.base.weight -
|
||||
(b.weight +
|
||||
b.engines.at(-1)!.weight +
|
||||
b.tracks.at(-1)!.weight +
|
||||
b.turrets.at(-1)!.weight +
|
||||
b.turrets.at(-1)!.guns.at(-1)!.gunType!.value.gun.weight),
|
||||
b.turrets.at(-1)!.guns.at(-1)!.gunType!.value.base.weight),
|
||||
);
|
||||
break;
|
||||
|
||||
|
||||
+28
-21
@@ -2,10 +2,11 @@ import {
|
||||
asset,
|
||||
coefficient,
|
||||
CREW_MEMBER_NAMES,
|
||||
equipmentDefinitions,
|
||||
CrewType,
|
||||
fetchEquipmentDefinitions,
|
||||
fetchProvisionDefinitions,
|
||||
GUN_TYPE_NAMES,
|
||||
isExplosive,
|
||||
provisionDefinitions,
|
||||
resolvePenetrationCoefficient,
|
||||
} from '@blitzkit/core';
|
||||
import { AccessibilityIcon, InfoCircledIcon } from '@radix-ui/react-icons';
|
||||
@@ -34,8 +35,8 @@ import { Ad, AdType } from '../../../../Ad';
|
||||
import { Info } from './components/Info';
|
||||
import { InfoWithDelta } from './components/InfoWithDelta';
|
||||
|
||||
const awaitedEquipmentDefinitions = await equipmentDefinitions;
|
||||
const awaitedProvisionDefinitions = await provisionDefinitions;
|
||||
const equipmentDefinitions = await fetchEquipmentDefinitions();
|
||||
const provisionDefinitions = await fetchProvisionDefinitions();
|
||||
|
||||
export function Characteristics() {
|
||||
const mutateDuel = Duel.useMutation();
|
||||
@@ -53,8 +54,8 @@ export function Characteristics() {
|
||||
const provisionCrewBonus =
|
||||
provisions.reduce(
|
||||
(total, provision) =>
|
||||
awaitedProvisionDefinitions[provision].crew
|
||||
? total + awaitedProvisionDefinitions[provision].crew! / 100
|
||||
provisionDefinitions.provisions[provision].crew
|
||||
? total + provisionDefinitions.provisions[provision].crew! / 100
|
||||
: total,
|
||||
0,
|
||||
) + (hasImprovedVentilation ? 0.08 : 0);
|
||||
@@ -73,7 +74,8 @@ export function Characteristics() {
|
||||
const stockGun = stockTurret.guns[0];
|
||||
const tankModelDefinition = useTankModelDefinition();
|
||||
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
||||
const gunModelDefinition = turretModelDefinition.guns[gun.id];
|
||||
const gunModelDefinition =
|
||||
turretModelDefinition.guns[gun.gunType!.value.base.id];
|
||||
const stats = tankCharacteristics(
|
||||
{
|
||||
tank,
|
||||
@@ -98,8 +100,8 @@ export function Characteristics() {
|
||||
},
|
||||
{
|
||||
tankModelDefinition,
|
||||
equipmentDefinitions: awaitedEquipmentDefinitions,
|
||||
provisionDefinitions: awaitedProvisionDefinitions,
|
||||
equipmentDefinitions: equipmentDefinitions,
|
||||
provisionDefinitions: provisionDefinitions,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -146,7 +148,7 @@ export function Characteristics() {
|
||||
<Heading size="5">Firepower</Heading>
|
||||
|
||||
<Flex>
|
||||
{gun.shells.map((thisShell, shellIndex) => (
|
||||
{gun.gunType!.value.base.shells.map((thisShell, shellIndex) => (
|
||||
<Tooltip content={thisShell.name} key={thisShell.id}>
|
||||
<IconButton
|
||||
color={thisShell.id === shell.id ? undefined : 'gray'}
|
||||
@@ -155,9 +157,13 @@ export function Characteristics() {
|
||||
borderTopLeftRadius: shellIndex === 0 ? undefined : 0,
|
||||
borderBottomLeftRadius: shellIndex === 0 ? undefined : 0,
|
||||
borderTopRightRadius:
|
||||
shellIndex === gun.shells.length - 1 ? undefined : 0,
|
||||
shellIndex === gun.gunType!.value.base.shells.length - 1
|
||||
? undefined
|
||||
: 0,
|
||||
borderBottomRightRadius:
|
||||
shellIndex === gun.shells.length - 1 ? undefined : 0,
|
||||
shellIndex === gun.gunType!.value.base.shells.length - 1
|
||||
? undefined
|
||||
: 0,
|
||||
marginLeft: shellIndex === 0 ? 0 : -1,
|
||||
}}
|
||||
onClick={() => {
|
||||
@@ -177,11 +183,11 @@ export function Characteristics() {
|
||||
))}
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Info name="Gun type">{GUN_TYPE_NAMES[gun.type]}</Info>
|
||||
<Info name="Gun type">{GUN_TYPE_NAMES[gun.gunType!.$case]}</Info>
|
||||
<InfoWithDelta name="DPM" decimals={0} unit="hp / min">
|
||||
{stats.dpm}
|
||||
</InfoWithDelta>
|
||||
{gun.type === 'autoReloader' && (
|
||||
{gun.gunType!.$case === 'autoReloader' && (
|
||||
<InfoWithDelta
|
||||
decimals={0}
|
||||
indent
|
||||
@@ -191,10 +197,10 @@ export function Characteristics() {
|
||||
{stats.dpmEffective!}
|
||||
</InfoWithDelta>
|
||||
)}
|
||||
{gun.type !== 'regular' && (
|
||||
{gun.gunType!.$case !== 'regular' && (
|
||||
<InfoWithDelta name="Shells">{stats.shells}</InfoWithDelta>
|
||||
)}
|
||||
{gun.type === 'autoReloader' ? (
|
||||
{gun.gunType!.$case === 'autoReloader' ? (
|
||||
<>
|
||||
<Info indent name="Most optimal shell index">
|
||||
{stats.mostOptimalShellIndex! + 1}
|
||||
@@ -222,7 +228,8 @@ export function Characteristics() {
|
||||
{stats.shellReload!}
|
||||
</InfoWithDelta>
|
||||
)}
|
||||
{(gun.type === 'autoLoader' || gun.type === 'autoReloader') && (
|
||||
{(gun.gunType!.$case === 'autoLoader' ||
|
||||
gun.gunType!.$case === 'autoReloader') && (
|
||||
<InfoWithDelta
|
||||
indent
|
||||
decimals={2}
|
||||
@@ -244,8 +251,8 @@ export function Characteristics() {
|
||||
name={`At ${penetrationDistance}m`}
|
||||
>
|
||||
{lerp(
|
||||
shell.penetration[0],
|
||||
shell.penetration[1],
|
||||
shell.penetration.near,
|
||||
shell.penetration.far,
|
||||
(penetrationDistance *
|
||||
penetrationLossOverDistanceCoefficient) /
|
||||
500,
|
||||
@@ -293,7 +300,7 @@ export function Characteristics() {
|
||||
<InfoWithDelta name="Damage" unit="hp" decimals={0}>
|
||||
{stats.damage}
|
||||
</InfoWithDelta>
|
||||
{gun.type !== 'regular' && (
|
||||
{gun.gunType!.$case !== 'regular' && (
|
||||
<InfoWithDelta name="Clipping potential" indent>
|
||||
{stats.clipDamage!}
|
||||
</InfoWithDelta>
|
||||
@@ -471,7 +478,7 @@ export function Characteristics() {
|
||||
unit="%"
|
||||
decimals={0}
|
||||
>
|
||||
{(member.type === 'commander'
|
||||
{(member.type === CrewType.COMMANDER
|
||||
? commanderMastery
|
||||
: commanderMastery * 1.1) * 100}
|
||||
</InfoWithDelta>
|
||||
|
||||
+4
-5
@@ -1,6 +1,5 @@
|
||||
import {} from '@blitzkit/core';
|
||||
import { fetchConsumableDefinitions } from '@blitzkit/core';
|
||||
import { checkConsumableProvisionInclusivity } from '@blitzkit/core/src/blitzkit/checkConsumableProvisionInclusivity';
|
||||
import { consumableDefinitions } from '@blitzkit/core/src/blitzkit/consumableDefinitions';
|
||||
import { InfoCircledIcon } from '@radix-ui/react-icons';
|
||||
import {
|
||||
Button,
|
||||
@@ -15,13 +14,13 @@ import { Duel } from '../../../../stores/duel';
|
||||
import { ConsumablesManager } from '../../../ConsumablesManager';
|
||||
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
|
||||
|
||||
const awaitedConsumableDefinitions = await consumableDefinitions;
|
||||
const consumableDefinitions = await fetchConsumableDefinitions();
|
||||
|
||||
export function Consumables() {
|
||||
const mutateDuel = Duel.useMutation();
|
||||
const protagonist = Duel.use((state) => state.protagonist);
|
||||
const consumables = Duel.use((state) => state.protagonist.consumables);
|
||||
const consumablesList = Object.values(awaitedConsumableDefinitions).filter(
|
||||
const consumablesList = Object.values(consumableDefinitions).filter(
|
||||
(consumable) =>
|
||||
checkConsumableProvisionInclusivity(
|
||||
consumable,
|
||||
@@ -74,7 +73,7 @@ export function Consumables() {
|
||||
consumables={consumablesList}
|
||||
selected={consumables}
|
||||
cooldownBooster={cooldownBooster}
|
||||
disabled={protagonist.tank.consumables === consumables.length}
|
||||
disabled={protagonist.tank.maxConsumables === consumables.length}
|
||||
hasConsumableDeliverySystem={hasConsumableDeliverySystem}
|
||||
hasHighEndConsumables={hasHighEndConsumables}
|
||||
timers
|
||||
|
||||
+88
-55
@@ -1,11 +1,14 @@
|
||||
import {
|
||||
asset,
|
||||
EngineDefinition,
|
||||
fetchTankDefinitions,
|
||||
formatCompact,
|
||||
tankDefinitions,
|
||||
GunDefinition,
|
||||
ModuleType,
|
||||
tankIcon,
|
||||
TIER_ROMAN_NUMERALS,
|
||||
type ModuleDefinition,
|
||||
type ModuleType,
|
||||
TrackDefinition,
|
||||
TurretDefinition,
|
||||
type TankDefinition,
|
||||
type Tier,
|
||||
type Unlock,
|
||||
@@ -19,8 +22,25 @@ import {
|
||||
Link,
|
||||
Text,
|
||||
} from '@radix-ui/themes';
|
||||
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
|
||||
import { Duel } from '../../../../stores/duel';
|
||||
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
|
||||
|
||||
type ModuleDefinition =
|
||||
| TurretDefinition
|
||||
| GunDefinition
|
||||
| TrackDefinition
|
||||
| EngineDefinition;
|
||||
|
||||
function extractModule(raw: ModuleDefinition | TankDefinition) {
|
||||
if ('gunType' in raw) {
|
||||
return raw.gunType!.value.base;
|
||||
} else
|
||||
return raw as Exclude<ModuleDefinition | TankDefinition, GunDefinition>;
|
||||
}
|
||||
|
||||
interface ModuleWithUnlocks {
|
||||
unlocks: Unlock[];
|
||||
}
|
||||
|
||||
function ModuleButton({
|
||||
tier,
|
||||
@@ -35,7 +55,7 @@ function ModuleButton({
|
||||
top: boolean;
|
||||
onClick: () => void;
|
||||
}) {
|
||||
const isTank = unlock.type === 'vehicle';
|
||||
const isTank = unlock.type === ModuleType.VEHICLE;
|
||||
const button = (
|
||||
<IconButton
|
||||
size="4"
|
||||
@@ -89,7 +109,7 @@ function ModuleButton({
|
||||
)}
|
||||
|
||||
<img
|
||||
alt={unlock.type}
|
||||
alt={ModuleType[unlock.type]}
|
||||
src={
|
||||
isTank
|
||||
? tankIcon(unlock.id)
|
||||
@@ -111,7 +131,7 @@ function ModuleButton({
|
||||
);
|
||||
}
|
||||
|
||||
const awaitedTankDefinitions = await tankDefinitions;
|
||||
const tankDefinitions = await fetchTankDefinitions();
|
||||
|
||||
export function Modules() {
|
||||
const mutateDuel = Duel.useMutation();
|
||||
@@ -136,44 +156,48 @@ export function Modules() {
|
||||
|
||||
function setByUnlock(unlock: Unlock) {
|
||||
mutateDuel((draft) => {
|
||||
if (unlock.type === 'turret') {
|
||||
if (unlock.type === ModuleType.TURRET) {
|
||||
draft.protagonist.turret = draft.protagonist.tank.turrets.find(
|
||||
(turret) => turret.id === unlock.id,
|
||||
)!;
|
||||
|
||||
if (
|
||||
!draft.protagonist.turret.guns.some(
|
||||
(gun) => gun.id === draft.protagonist.gun.id,
|
||||
(gun) =>
|
||||
gun.gunType!.value.base.id ===
|
||||
draft.protagonist.gun.gunType!.value.base.id,
|
||||
)
|
||||
) {
|
||||
draft.protagonist.gun = draft.protagonist.turret.guns.at(-1)!;
|
||||
draft.protagonist.shell = draft.protagonist.gun.shells[0];
|
||||
draft.protagonist.shell =
|
||||
draft.protagonist.gun.gunType!.value.base.shells[0];
|
||||
}
|
||||
} else if (unlock.type === 'gun') {
|
||||
} else if (unlock.type === ModuleType.GUN) {
|
||||
const gunInTurret = draft.protagonist.turret.guns.find(
|
||||
(gun) => gun.id === unlock.id,
|
||||
(gun) => gun.gunType!.value.base.id === unlock.id,
|
||||
);
|
||||
if (gunInTurret) {
|
||||
draft.protagonist.gun = gunInTurret;
|
||||
draft.protagonist.shell = gunInTurret.shells[0];
|
||||
draft.protagonist.shell = gunInTurret.gunType!.value.base.shells[0];
|
||||
} 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.gunType!.value.base.id === unlock.id),
|
||||
)!;
|
||||
const gunInSuitableTurret = suitableTurret.guns.find(
|
||||
(gun) => gun.id === unlock.id,
|
||||
(gun) => gun.gunType!.value.base.id === unlock.id,
|
||||
)!;
|
||||
|
||||
draft.protagonist.turret = suitableTurret;
|
||||
draft.protagonist.gun = gunInSuitableTurret;
|
||||
draft.protagonist.shell = gunInSuitableTurret.shells[0];
|
||||
draft.protagonist.shell =
|
||||
gunInSuitableTurret.gunType!.value.base.shells[0];
|
||||
}
|
||||
} else if (unlock.type === 'engine') {
|
||||
} else if (unlock.type === ModuleType.ENGINE) {
|
||||
draft.protagonist.engine = draft.protagonist.tank.engines.find(
|
||||
(engine) => engine.id === unlock.id,
|
||||
)!;
|
||||
} else if (unlock.type === 'chassis') {
|
||||
} else if (unlock.type === ModuleType.TRACKS) {
|
||||
draft.protagonist.track = draft.protagonist.tank.tracks.find(
|
||||
(track) => track.id === unlock.id,
|
||||
)!;
|
||||
@@ -190,20 +214,22 @@ export function Modules() {
|
||||
const central = !first && !last;
|
||||
let module: ModuleDefinition | TankDefinition | undefined = undefined;
|
||||
|
||||
if (unlock.type === 'chassis') {
|
||||
if (unlock.type === ModuleType.TRACKS) {
|
||||
module = tank.tracks.find((track) => track.id === unlock.id)!;
|
||||
} else if (unlock.type === 'engine') {
|
||||
} else if (unlock.type === ModuleType.ENGINE) {
|
||||
module = tank.engines.find((engine) => engine.id === unlock.id)!;
|
||||
} else if (unlock.type === 'turret') {
|
||||
} else if (unlock.type === ModuleType.TURRET) {
|
||||
module = tank.turrets.find((turret) => turret.id === unlock.id)!;
|
||||
} else if (unlock.type === 'gun') {
|
||||
} else if (unlock.type === ModuleType.GUN) {
|
||||
module = tank.turrets
|
||||
.find((turret) =>
|
||||
turret.guns.some((gun) => gun.id === unlock.id),
|
||||
turret.guns.some(
|
||||
(gun) => gun.gunType!.value.base.id === unlock.id,
|
||||
),
|
||||
)!
|
||||
.guns.find((gun) => gun.id === unlock.id)!;
|
||||
} else if (unlock.type === 'vehicle') {
|
||||
module = awaitedTankDefinitions[unlock.id];
|
||||
.guns.find((gun) => gun.gunType!.value.base.id === unlock.id)!;
|
||||
} else if (unlock.type === ModuleType.VEHICLE) {
|
||||
module = tankDefinitions.tanks[unlock.id];
|
||||
}
|
||||
|
||||
if (module === undefined) return null;
|
||||
@@ -259,34 +285,36 @@ export function Modules() {
|
||||
|
||||
<ModuleButton
|
||||
unlock={unlock}
|
||||
tier={module.tier}
|
||||
tier={extractModule(module).tier}
|
||||
selected={
|
||||
(unlock.type === 'turret'
|
||||
(unlock.type === ModuleType.TURRET
|
||||
? turret.id
|
||||
: unlock.type === 'gun'
|
||||
? gun.id
|
||||
: unlock.type === 'engine'
|
||||
: unlock.type === ModuleType.GUN
|
||||
? gun.gunType!.value.base.id
|
||||
: unlock.type === ModuleType.ENGINE
|
||||
? engine.id
|
||||
: unlock.type === 'chassis'
|
||||
: unlock.type === ModuleType.TRACKS
|
||||
? track.id
|
||||
: -1) === unlock.id
|
||||
}
|
||||
top={
|
||||
unlock.type === 'turret'
|
||||
? module.id == topTurret.id
|
||||
: unlock.type === 'gun'
|
||||
? module.id === topGun.id
|
||||
: unlock.type === 'engine'
|
||||
? module.id === topEngine.id
|
||||
: unlock.type === 'chassis'
|
||||
? module.id === topTrack.id
|
||||
unlock.type === ModuleType.TURRET
|
||||
? extractModule(module).id == topTurret.id
|
||||
: unlock.type === ModuleType.GUN
|
||||
? extractModule(module).id ===
|
||||
topGun.gunType!.value.base.id
|
||||
: unlock.type === ModuleType.ENGINE
|
||||
? extractModule(module).id === topEngine.id
|
||||
: unlock.type === ModuleType.TRACKS
|
||||
? extractModule(module).id === topTrack.id
|
||||
: false
|
||||
}
|
||||
onClick={() => setByUnlock(unlock)}
|
||||
/>
|
||||
|
||||
{(module as ModuleDefinition).unlocks &&
|
||||
(module as ModuleDefinition).unlocks!.length > 1 && (
|
||||
{(extractModule(module) as ModuleWithUnlocks).unlocks &&
|
||||
(extractModule(module) as ModuleWithUnlocks).unlocks!.length >
|
||||
1 && (
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: 'currentcolor',
|
||||
@@ -297,8 +325,11 @@ export function Modules() {
|
||||
/>
|
||||
)}
|
||||
|
||||
{(module as ModuleDefinition).unlocks &&
|
||||
tree(type, (module as ModuleDefinition).unlocks!)}
|
||||
{(extractModule(module) as ModuleWithUnlocks).unlocks &&
|
||||
tree(
|
||||
type,
|
||||
(extractModule(module) as ModuleWithUnlocks).unlocks!,
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
})}
|
||||
@@ -320,7 +351,8 @@ export function Modules() {
|
||||
mutateDuel((draft) => {
|
||||
draft.protagonist.turret = draft.protagonist.tank.turrets[0];
|
||||
draft.protagonist.gun = draft.protagonist.turret.guns[0];
|
||||
draft.protagonist.shell = draft.protagonist.gun.shells[0];
|
||||
draft.protagonist.shell =
|
||||
draft.protagonist.gun.gunType!.value.base.shells[0];
|
||||
draft.protagonist.engine = draft.protagonist.tank.engines[0];
|
||||
draft.protagonist.track = draft.protagonist.tank.tracks[0];
|
||||
});
|
||||
@@ -335,7 +367,8 @@ export function Modules() {
|
||||
draft.protagonist.turret =
|
||||
draft.protagonist.tank.turrets.at(-1)!;
|
||||
draft.protagonist.gun = draft.protagonist.turret.guns.at(-1)!;
|
||||
draft.protagonist.shell = draft.protagonist.gun.shells[0];
|
||||
draft.protagonist.shell =
|
||||
draft.protagonist.gun.gunType!.value.base.shells[0];
|
||||
draft.protagonist.engine =
|
||||
draft.protagonist.tank.engines.at(-1)!;
|
||||
draft.protagonist.track =
|
||||
@@ -350,32 +383,32 @@ export function Modules() {
|
||||
</Flex>
|
||||
|
||||
<Flex gap="2" wrap="wrap" gapY="6">
|
||||
{tree('engine', [
|
||||
{tree(ModuleType.ENGINE, [
|
||||
{
|
||||
cost: { type: 'xp', value: 0 },
|
||||
id: engine0.id,
|
||||
type: 'engine',
|
||||
type: ModuleType.ENGINE,
|
||||
},
|
||||
])}
|
||||
{tree('chassis', [
|
||||
{tree(ModuleType.TRACKS, [
|
||||
{
|
||||
cost: { type: 'xp', value: 0 },
|
||||
id: track0.id,
|
||||
type: 'chassis',
|
||||
type: ModuleType.TRACKS,
|
||||
},
|
||||
])}
|
||||
{tree('turret', [
|
||||
{tree(ModuleType.TURRET, [
|
||||
{
|
||||
cost: { type: 'xp', value: 0 },
|
||||
id: turret0.id,
|
||||
type: 'turret',
|
||||
type: ModuleType.TURRET,
|
||||
},
|
||||
])}
|
||||
{tree('gun', [
|
||||
{tree(ModuleType.GUN, [
|
||||
{
|
||||
cost: { type: 'xp', value: 0 },
|
||||
id: gun0.id,
|
||||
type: 'gun',
|
||||
id: gun0.gunType!.value.base.id,
|
||||
type: ModuleType.GUN,
|
||||
},
|
||||
])}
|
||||
</Flex>
|
||||
|
||||
+4
-8
@@ -1,20 +1,16 @@
|
||||
import { availableProvisions, provisionDefinitions } from '@blitzkit/core';
|
||||
import { availableProvisions, fetchProvisionDefinitions } from '@blitzkit/core';
|
||||
import { Button, Flex, Heading } from '@radix-ui/themes';
|
||||
import { Duel } from '../../../../stores/duel';
|
||||
import { ProvisionsManager } from '../../../ProvisionsManager';
|
||||
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
|
||||
|
||||
const awaitedProvisionDefinitions = await provisionDefinitions;
|
||||
const provisionDefinitions = await fetchProvisionDefinitions();
|
||||
|
||||
export function Provisions() {
|
||||
const mutateDuel = Duel.useMutation();
|
||||
const { tank, gun } = Duel.use((state) => state.protagonist);
|
||||
const provisions = Duel.use((state) => state.protagonist.provisions);
|
||||
const provisionsList = availableProvisions(
|
||||
tank,
|
||||
gun,
|
||||
awaitedProvisionDefinitions,
|
||||
);
|
||||
const provisionsList = availableProvisions(tank, gun, provisionDefinitions);
|
||||
|
||||
return (
|
||||
<ConfigurationChildWrapper>
|
||||
@@ -36,7 +32,7 @@ export function Provisions() {
|
||||
<ProvisionsManager
|
||||
provisions={provisionsList.map((provision) => provision.id)}
|
||||
selected={provisions}
|
||||
disabled={tank.provisions === provisions.length}
|
||||
disabled={tank.maxProvisions === provisions.length}
|
||||
onChange={(provisions) => {
|
||||
mutateDuel((draft) => {
|
||||
draft.protagonist.provisions = provisions;
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
import { createDefaultSkills, skillDefinitions } from '@blitzkit/core';
|
||||
import { createDefaultSkills, fetchSkillDefinitions } from '@blitzkit/core';
|
||||
import { InfoCircledIcon } from '@radix-ui/react-icons';
|
||||
import {
|
||||
Button,
|
||||
@@ -13,7 +13,7 @@ import { TankopediaEphemeral } from '../../../../stores/tankopediaEphemeral';
|
||||
import { CrewSkillManager } from '../../../CrewSkillManager';
|
||||
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
|
||||
|
||||
const awaitedSkillDefinitions = await skillDefinitions;
|
||||
const skillDefinitions = await fetchSkillDefinitions();
|
||||
|
||||
export function Skills() {
|
||||
const skillLevels = TankopediaEphemeral.use((state) => state.skills);
|
||||
@@ -21,7 +21,7 @@ export function Skills() {
|
||||
|
||||
if (Object.keys(skillLevels).length === 0) {
|
||||
mutateTankopediaTemporary((draft) => {
|
||||
draft.skills = createDefaultSkills(awaitedSkillDefinitions);
|
||||
draft.skills = createDefaultSkills(skillDefinitions);
|
||||
});
|
||||
|
||||
return null;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import {
|
||||
asset,
|
||||
consumableDefinitions,
|
||||
gameDefinitions,
|
||||
provisionDefinitions,
|
||||
fetchConsumableDefinitions,
|
||||
fetchGameDefinitions,
|
||||
fetchProvisionDefinitions,
|
||||
} from '@blitzkit/core';
|
||||
import { Box, Flex, Heading, Text } from '@radix-ui/themes';
|
||||
import { Duel } from '../../stores/duel';
|
||||
|
||||
const awaitedGameDefinitions = await gameDefinitions;
|
||||
const awaitedConsumableDefinitions = await consumableDefinitions;
|
||||
const awaitedProvisionDefinitions = await provisionDefinitions;
|
||||
const gameDefinitions = await fetchGameDefinitions();
|
||||
const consumableDefinitions = await fetchConsumableDefinitions();
|
||||
const provisionDefinitions = await fetchProvisionDefinitions();
|
||||
|
||||
export function GameModeSection() {
|
||||
const tank = Duel.use((state) => state.protagonist.tank);
|
||||
@@ -24,7 +24,7 @@ export function GameModeSection() {
|
||||
<Flex justify="center" gap="4" wrap="wrap" px="4">
|
||||
{roles.map(([key, value]) => {
|
||||
const id = Number(key);
|
||||
const gameMode = awaitedGameDefinitions.gameModes[id];
|
||||
const gameMode = gameDefinitions.gameModes[id];
|
||||
|
||||
return (
|
||||
<Flex
|
||||
@@ -70,8 +70,8 @@ export function GameModeSection() {
|
||||
</Text>
|
||||
|
||||
<Flex direction="column">
|
||||
{awaitedGameDefinitions.roles[value].provisions.map((id) => {
|
||||
const provisions = awaitedProvisionDefinitions[id];
|
||||
{gameDefinitions.roles[value].provisions.map((id) => {
|
||||
const provisions = provisionDefinitions.provisions[id];
|
||||
|
||||
return (
|
||||
<Text color="gray" wrap="nowrap" key={id}>
|
||||
@@ -90,8 +90,8 @@ export function GameModeSection() {
|
||||
</Text>
|
||||
);
|
||||
})}
|
||||
{awaitedGameDefinitions.roles[value].consumables.map((id) => {
|
||||
const consumable = awaitedConsumableDefinitions[id];
|
||||
{gameDefinitions.roles[value].consumables.map((id) => {
|
||||
const consumable = consumableDefinitions.consumables[id];
|
||||
|
||||
return (
|
||||
<Text color="gray" wrap="nowrap" key={id}>
|
||||
|
||||
+2
-2
@@ -28,8 +28,8 @@ export function DynamicArmorSwitcher() {
|
||||
draft.protagonist.consumables =
|
||||
draft.protagonist.consumables.filter((id) => id !== 73);
|
||||
} else {
|
||||
if (draft.protagonist.consumables.length === tank.consumables) {
|
||||
draft.protagonist.consumables[tank.consumables - 1] = 73;
|
||||
if (draft.protagonist.consumables.length === tank.maxConsumables) {
|
||||
draft.protagonist.consumables[tank.maxConsumables - 1] = 73;
|
||||
} else {
|
||||
draft.protagonist.consumables.push(73);
|
||||
}
|
||||
|
||||
+5
-3
@@ -20,8 +20,9 @@ export function QuickInputs() {
|
||||
const tankModelDefinition = useTankModelDefinition();
|
||||
const turretModelDefinition =
|
||||
tankModelDefinition.turrets[protagonist.turret.id];
|
||||
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
|
||||
const initialGunPitch = tankModelDefinition.turretRotation?.pitch ?? 0;
|
||||
const gunModelDefinition =
|
||||
turretModelDefinition.guns[protagonist.gun.gunType!.value.base.id];
|
||||
const initialGunPitch = tankModelDefinition.initialTurretRotation?.pitch ?? 0;
|
||||
const hasImprovedVerticalStabilizer = useEquipment(122);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -43,7 +44,8 @@ export function QuickInputs() {
|
||||
if (!pitchInput.current || !yawInput.current) return;
|
||||
|
||||
pitchInput.current.value = (
|
||||
-radToDeg(pitch) + (tankModelDefinition.turretRotation?.pitch ?? 0)
|
||||
-radToDeg(pitch) +
|
||||
(tankModelDefinition.initialTurretRotation?.pitch ?? 0)
|
||||
).toFixed(1);
|
||||
|
||||
if (yaw === undefined) return;
|
||||
|
||||
+51
-37
@@ -1,7 +1,6 @@
|
||||
import {
|
||||
asset,
|
||||
imgur,
|
||||
resolveNearPenetration,
|
||||
resolvePenetrationCoefficient,
|
||||
TIER_ROMAN_NUMERALS,
|
||||
uniqueGuns,
|
||||
@@ -105,7 +104,7 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
||||
resolvePenetrationCoefficient(
|
||||
hasCalibratedShells,
|
||||
antagonistShell.type,
|
||||
) * resolveNearPenetration(antagonistShell.penetration)
|
||||
) * antagonistShell.penetration.near
|
||||
).toFixed(0)}
|
||||
mm
|
||||
</Text>
|
||||
@@ -130,7 +129,11 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
||||
mr="2"
|
||||
mb="1"
|
||||
>
|
||||
{TIER_ROMAN_NUMERALS[antagonistGun.tier]}
|
||||
{
|
||||
TIER_ROMAN_NUMERALS[
|
||||
antagonistGun.gunType!.value.base.tier
|
||||
]
|
||||
}
|
||||
</Text>
|
||||
|
||||
<img
|
||||
@@ -149,19 +152,25 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
||||
<Flex gap="2">
|
||||
{[...antagonistUniqueGuns]
|
||||
.reverse()
|
||||
.map(({ gun, turret }, index) => (
|
||||
.map(({ gun, turret }) => (
|
||||
<ModuleButton
|
||||
module="gun"
|
||||
key={gun.id}
|
||||
key={gun.gunType!.value.base.id}
|
||||
onClick={() =>
|
||||
mutateDuel((draft) => {
|
||||
draft.antagonist.turret = turret;
|
||||
draft.antagonist.gun = gun;
|
||||
draft.antagonist.shell = gun.shells[0];
|
||||
draft.antagonist.shell =
|
||||
gun.gunType!.value.base.shells[0];
|
||||
})
|
||||
}
|
||||
selected={gun.id === antagonistGun.id}
|
||||
discriminator={TIER_ROMAN_NUMERALS[gun.tier]}
|
||||
selected={
|
||||
gun.gunType!.value.base.id ===
|
||||
antagonistGun.gunType!.value.base.id
|
||||
}
|
||||
discriminator={
|
||||
TIER_ROMAN_NUMERALS[gun.gunType!.value.base.tier]
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</Flex>
|
||||
@@ -175,36 +184,40 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
||||
}}
|
||||
overflow="hidden"
|
||||
>
|
||||
{antagonistGun.shells.map((thisShell, shellIndex) => (
|
||||
<IconButton
|
||||
color={thisShell.id === antagonistShell.id ? undefined : 'gray'}
|
||||
variant="soft"
|
||||
key={thisShell.id}
|
||||
size={{ initial: '2', sm: '3' }}
|
||||
radius="none"
|
||||
style={{
|
||||
marginTop: shellIndex === 0 ? 0 : -1,
|
||||
}}
|
||||
onClick={() => {
|
||||
invalidate();
|
||||
mutateDuel((draft) => {
|
||||
draft.antagonist.shell = thisShell;
|
||||
});
|
||||
mutateTankopediaEphemeral((draft) => {
|
||||
draft.shot = undefined;
|
||||
});
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt={thisShell.name}
|
||||
src={asset(`icons/shells/${thisShell.icon}.webp`)}
|
||||
{antagonistGun.gunType!.value.base.shells.map(
|
||||
(thisShell, shellIndex) => (
|
||||
<IconButton
|
||||
color={
|
||||
thisShell.id === antagonistShell.id ? undefined : 'gray'
|
||||
}
|
||||
variant="soft"
|
||||
key={thisShell.id}
|
||||
size={{ initial: '2', sm: '3' }}
|
||||
radius="none"
|
||||
style={{
|
||||
width: '50%',
|
||||
height: '50%',
|
||||
marginTop: shellIndex === 0 ? 0 : -1,
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
))}
|
||||
onClick={() => {
|
||||
invalidate();
|
||||
mutateDuel((draft) => {
|
||||
draft.antagonist.shell = thisShell;
|
||||
});
|
||||
mutateTankopediaEphemeral((draft) => {
|
||||
draft.shot = undefined;
|
||||
});
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt={thisShell.name}
|
||||
src={asset(`icons/shells/${thisShell.icon}.webp`)}
|
||||
style={{
|
||||
width: '50%',
|
||||
height: '50%',
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
),
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
<Flex
|
||||
@@ -318,7 +331,8 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
||||
draft.antagonist.turret = tank.turrets.at(-1)!;
|
||||
draft.antagonist.gun =
|
||||
draft.antagonist.turret.guns.at(-1)!;
|
||||
draft.antagonist.shell = draft.antagonist.gun.shells[0];
|
||||
draft.antagonist.shell =
|
||||
draft.antagonist.gun.gunType!.value.base.shells[0];
|
||||
});
|
||||
setAntagonistSelectorOpen(false);
|
||||
}}
|
||||
|
||||
+6
-6
@@ -1,4 +1,4 @@
|
||||
import { modelDefinitions } from '@blitzkit/core';
|
||||
import { fetchModelDefinitions } from '@blitzkit/core';
|
||||
import { OrbitControls } from '@react-three/drei';
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
@@ -7,7 +7,6 @@ import { OrbitControls as OrbitControlsClass } from 'three-stdlib';
|
||||
import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawLimits';
|
||||
import { hasEquipment } from '../../../../../../core/blitzkit/hasEquipment';
|
||||
import { Pose, poseEvent } from '../../../../../../core/blitzkit/pose';
|
||||
import { useAwait } from '../../../../../../hooks/useAwait';
|
||||
import { Duel } from '../../../../../../stores/duel';
|
||||
import { TankopediaEphemeral } from '../../../../../../stores/tankopediaEphemeral';
|
||||
|
||||
@@ -18,20 +17,21 @@ const poseDistances: Record<Pose, number> = {
|
||||
[Pose.Default]: -1,
|
||||
};
|
||||
|
||||
const modelDefinitions = await fetchModelDefinitions();
|
||||
|
||||
export function Controls() {
|
||||
const duelStore = Duel.useStore();
|
||||
const tankopediaEphemeralStore = TankopediaEphemeral.useStore();
|
||||
const camera = useThree((state) => state.camera);
|
||||
const canvas = useThree((state) => state.gl.domElement);
|
||||
const orbitControls = useRef<OrbitControlsClass>(null);
|
||||
const awaitedModelDefinitions = useAwait(modelDefinitions);
|
||||
const protagonist = Duel.use((state) => state.protagonist);
|
||||
const antagonist = Duel.use((state) => state.antagonist);
|
||||
const protagonistModelDefinition =
|
||||
awaitedModelDefinitions[protagonist.tank.id];
|
||||
modelDefinitions.models[protagonist.tank.id];
|
||||
const protagonistTrackModelDefinition =
|
||||
awaitedModelDefinitions[protagonist.tank.id].tracks[protagonist.track.id];
|
||||
const antagonistModelDefinition = awaitedModelDefinitions[antagonist.tank.id];
|
||||
modelDefinitions.models[protagonist.tank.id].tracks[protagonist.track.id];
|
||||
const antagonistModelDefinition = modelDefinitions.models[antagonist.tank.id];
|
||||
const protagonistTurretModelDefinition =
|
||||
protagonistModelDefinition.turrets[protagonist.turret.id];
|
||||
const antagonistTurretModelDefinition =
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import {
|
||||
CrewType,
|
||||
degressiveStat,
|
||||
isExplosive,
|
||||
normalizeBoundingBox,
|
||||
progressiveStat,
|
||||
resolveDpm,
|
||||
resolveNearPenetration,
|
||||
resolvePenetrationCoefficient,
|
||||
ShellType,
|
||||
sum,
|
||||
TankClass,
|
||||
unionBoundingBox,
|
||||
type EngineDefinition,
|
||||
type EquipmentDefinitions,
|
||||
@@ -75,18 +77,17 @@ export function tankCharacteristics(
|
||||
provisionDefinitions: ProvisionDefinitions;
|
||||
},
|
||||
) {
|
||||
const presetRows = equipmentDefinitions.presets[tank.equipment];
|
||||
const preset = equipmentDefinitions.presets[tank.equipmentPreset];
|
||||
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
||||
const gunModelDefinition = turretModelDefinition.guns[gun.id];
|
||||
const gunModelDefinition =
|
||||
turretModelDefinition.guns[gun.gunType!.value.base.id];
|
||||
|
||||
function equipment(id: number) {
|
||||
return presetRows.some((equipmentRow, rowIndex) => {
|
||||
return equipmentRow.some((equipment, columnIndex) => {
|
||||
const choice = equipmentMatrix[rowIndex][columnIndex];
|
||||
if (choice === 0) return false;
|
||||
const equipped = equipment[choice === -1 ? 0 : 1];
|
||||
return equipped === id;
|
||||
});
|
||||
return preset.slots.some((slot, index) => {
|
||||
const choice = equipmentMatrix[Math.floor(index / 3)][index % 3];
|
||||
if (choice === 0) return false;
|
||||
const equipped = slot[choice === -1 ? 'left' : 'right'];
|
||||
return equipped === id;
|
||||
});
|
||||
}
|
||||
function consumable(id: number) {
|
||||
@@ -128,37 +129,37 @@ export function tankCharacteristics(
|
||||
const hasGearOil = provision(44);
|
||||
const hasImprovedGearOil = provision(45);
|
||||
|
||||
const gunDefaultPitch = tankModelDefinition.turretRotation?.pitch ?? 0;
|
||||
const gunDefaultPitch = tankModelDefinition.initialTurretRotation?.pitch ?? 0;
|
||||
const provisionCrewBonus =
|
||||
provisions.reduce(
|
||||
(total, provision) =>
|
||||
provisionDefinitions[provision].crew
|
||||
? total + provisionDefinitions[provision].crew! / 100
|
||||
provisionDefinitions.provisions[provision].crew
|
||||
? total + provisionDefinitions.provisions[provision].crew! / 100
|
||||
: total,
|
||||
0,
|
||||
) + (hasImprovedVentilation ? 0.08 : 0);
|
||||
const commanderMastery = crewMastery + provisionCrewBonus;
|
||||
const loaderMastery =
|
||||
commanderMastery *
|
||||
(tank.crew.some(({ type }) => type === 'loader') ? 1.1 : 1.05);
|
||||
(tank.crew.some(({ type }) => type === CrewType.LOADER) ? 1.1 : 1.05);
|
||||
const gunnerMastery =
|
||||
commanderMastery *
|
||||
(tank.crew.some(({ type }) => type === 'gunner') ? 1.1 : 1.05);
|
||||
(tank.crew.some(({ type }) => type === CrewType.GUNNER) ? 1.1 : 1.05);
|
||||
const driverMastery =
|
||||
commanderMastery *
|
||||
(tank.crew.some(({ type }) => type === 'driver') ? 1.1 : 1.05);
|
||||
(tank.crew.some(({ type }) => type === CrewType.DRIVER) ? 1.1 : 1.05);
|
||||
const intraClipCoefficient = coefficient([hasShellReloadBoost, -0.3]);
|
||||
const damageCoefficient =
|
||||
coefficient([hasTungsten, 0.15]) *
|
||||
coefficient(
|
||||
[applyReactiveArmor, -0.27],
|
||||
[applyDynamicArmor, -0.1],
|
||||
[applySpallLiner && shell.type === 'he', -0.2],
|
||||
[applySpallLiner && shell.type === ShellType.HE, -0.2],
|
||||
);
|
||||
const reloadCoefficient =
|
||||
(coefficient([hasGunRammer, -0.05]) *
|
||||
coefficient([true, degressiveStat(loaderMastery)])) /
|
||||
coefficient([hasAdrenaline && gun.type === 'regular', 0.17]);
|
||||
coefficient([hasAdrenaline && gun.gunType!.$case === 'regular', 0.17]);
|
||||
const penetrationCoefficient = coefficient([
|
||||
hasCalibratedShells,
|
||||
resolvePenetrationCoefficient(true, shell.type) - 1,
|
||||
@@ -207,7 +208,9 @@ export function tankCharacteristics(
|
||||
const enginePowerCoefficient = coefficient(
|
||||
[
|
||||
hasEngineAccelerator,
|
||||
tank.class === 'heavyTank' || tank.class === 'AT-SPG' ? 0.05 : 0.04,
|
||||
tank.class === TankClass.HEAVY || tank.class === TankClass.TANK_DESTROYER
|
||||
? 0.05
|
||||
: 0.04,
|
||||
],
|
||||
[hasReducedEnginePowerBoost, 0.1],
|
||||
[hasEnginePowerBoost, 0.2],
|
||||
@@ -235,11 +238,11 @@ export function tankCharacteristics(
|
||||
const viewRangeCoefficient =
|
||||
coefficient([
|
||||
hasImprovedOptics,
|
||||
tank.class === 'heavyTank'
|
||||
tank.class === TankClass.HEAVY
|
||||
? 0.06
|
||||
: tank.class === 'mediumTank'
|
||||
: tank.class === TankClass.MEDIUM
|
||||
? 0.08
|
||||
: tank.class === 'lightTank'
|
||||
: tank.class === TankClass.LIGHT
|
||||
? 0.11
|
||||
: 0.04,
|
||||
]) * coefficient([true, progressiveStat(commanderMastery)]);
|
||||
@@ -260,20 +263,20 @@ export function tankCharacteristics(
|
||||
true,
|
||||
coefficient([
|
||||
camouflage,
|
||||
tank.class === 'AT-SPG'
|
||||
tank.class === TankClass.TANK_DESTROYER
|
||||
? 0.04
|
||||
: tank.class === 'heavyTank'
|
||||
: tank.class === TankClass.HEAVY
|
||||
? 0.03
|
||||
: 0.02,
|
||||
]),
|
||||
],
|
||||
[
|
||||
hasCamouflageNet,
|
||||
tank.class === 'heavyTank'
|
||||
tank.class === TankClass.HEAVY
|
||||
? 0.02
|
||||
: tank.class === 'mediumTank'
|
||||
: tank.class === TankClass.MEDIUM
|
||||
? 0.04
|
||||
: tank.class === 'lightTank'
|
||||
: tank.class === TankClass.LIGHT
|
||||
? 0.07
|
||||
: 0.1,
|
||||
],
|
||||
@@ -284,9 +287,9 @@ export function tankCharacteristics(
|
||||
coefficient(
|
||||
[
|
||||
camouflage,
|
||||
tank.class === 'AT-SPG'
|
||||
tank.class === TankClass.TANK_DESTROYER
|
||||
? 0.04
|
||||
: tank.class === 'heavyTank'
|
||||
: tank.class === TankClass.HEAVY
|
||||
? 0.03
|
||||
: 0.02,
|
||||
],
|
||||
@@ -295,11 +298,11 @@ export function tankCharacteristics(
|
||||
],
|
||||
[
|
||||
hasCamouflageNet,
|
||||
tank.class === 'heavyTank'
|
||||
tank.class === TankClass.HEAVY
|
||||
? 0.04
|
||||
: tank.class === 'mediumTank'
|
||||
: tank.class === TankClass.MEDIUM
|
||||
? 0.08
|
||||
: tank.class === 'lightTank'
|
||||
: tank.class === TankClass.LIGHT
|
||||
? 0.14
|
||||
: 0.2,
|
||||
],
|
||||
@@ -312,14 +315,18 @@ export function tankCharacteristics(
|
||||
),
|
||||
);
|
||||
const weightKg =
|
||||
tank.weight + engine.weight + track.weight + turret.weight + gun.weight;
|
||||
tank.weight +
|
||||
engine.weight +
|
||||
track.weight +
|
||||
turret.weight +
|
||||
gun.gunType!.value.base.weight;
|
||||
const weightTons = weightKg / 1000;
|
||||
const stockWeight =
|
||||
tank.weight +
|
||||
stockEngine.weight +
|
||||
stockTrack.weight +
|
||||
stockTurret.weight +
|
||||
stockGun.weight;
|
||||
stockGun.gunType!.value.base.weight;
|
||||
const resolvedEnginePower = engine.power * enginePowerCoefficient;
|
||||
const dpm = resolveDpm(
|
||||
gun,
|
||||
@@ -329,78 +336,100 @@ export function tankCharacteristics(
|
||||
intraClipCoefficient,
|
||||
);
|
||||
const intraClip =
|
||||
gun.type === 'regular' ? undefined : gun.intraClip * intraClipCoefficient;
|
||||
gun.gunType?.$case === 'regular'
|
||||
? undefined
|
||||
: gun.gunType!.value.extension.intraClip * intraClipCoefficient;
|
||||
const mostOptimalShellIndex =
|
||||
gun.type === 'autoReloader'
|
||||
? gun.reload.reduce<null | { index: number; reload: number }>(
|
||||
(current, reloadRaw, index) => {
|
||||
const reload =
|
||||
reloadRaw * reloadCoefficient + (index > 0 ? intraClip! : 0);
|
||||
gun.gunType!.$case === 'autoReloader'
|
||||
? gun.gunType!.value.extension.shellReloads.reduce<null | {
|
||||
index: number;
|
||||
reload: number;
|
||||
}>((current, reloadRaw, index) => {
|
||||
const reload =
|
||||
reloadRaw * reloadCoefficient + (index > 0 ? intraClip! : 0);
|
||||
|
||||
if (current === null || reload < current.reload) {
|
||||
return { index, reload };
|
||||
}
|
||||
return current;
|
||||
},
|
||||
null,
|
||||
)!.index
|
||||
if (current === null || reload < current.reload) {
|
||||
return { index, reload };
|
||||
}
|
||||
return current;
|
||||
}, null)!.index
|
||||
: undefined;
|
||||
const damage = shell.damage.armor * damageCoefficient;
|
||||
const damage = shell.armorDamage * damageCoefficient;
|
||||
const dpmEffective =
|
||||
gun.type === 'autoReloader'
|
||||
? gun.reload[0] > gun.reload[1] + gun.intraClip
|
||||
? ((damageCoefficient * shell.damage.armor) /
|
||||
(gun.reload.at(-1)! * reloadCoefficient +
|
||||
gun.intraClip * intraClipCoefficient)) *
|
||||
gun.gunType!.$case === 'autoReloader'
|
||||
? gun.gunType!.value.extension.shellReloads[0] >
|
||||
gun.gunType!.value.extension.shellReloads[1] +
|
||||
gun.gunType!.value.extension.intraClip
|
||||
? ((damageCoefficient * shell.armorDamage) /
|
||||
(gun.gunType!.value.extension.shellReloads.at(-1)! *
|
||||
reloadCoefficient +
|
||||
gun.gunType!.value.extension.intraClip * intraClipCoefficient)) *
|
||||
(60 -
|
||||
(gun.reload.slice(0, -1).length - 1) *
|
||||
gun.intraClip *
|
||||
(gun.gunType!.value.extension.shellReloads.slice(0, -1).length -
|
||||
1) *
|
||||
gun.gunType!.value.extension.intraClip *
|
||||
intraClipCoefficient) +
|
||||
damageCoefficient *
|
||||
shell.damage.armor *
|
||||
gun.reload.slice(0, -1).length
|
||||
: ((damageCoefficient * shell.damage.armor) /
|
||||
(gun.reload[0] * reloadCoefficient +
|
||||
gun.intraClip * intraClipCoefficient)) *
|
||||
shell.armorDamage *
|
||||
gun.gunType!.value.extension.shellReloads.slice(0, -1).length
|
||||
: ((damageCoefficient * shell.armorDamage) /
|
||||
(gun.gunType!.value.extension.shellReloads[0] * reloadCoefficient +
|
||||
gun.gunType!.value.extension.intraClip * intraClipCoefficient)) *
|
||||
(60 -
|
||||
(gun.reload.slice(1).length - 1) *
|
||||
gun.intraClip *
|
||||
(gun.gunType!.value.extension.shellReloads.slice(1).length - 1) *
|
||||
gun.gunType!.value.extension.intraClip *
|
||||
intraClipCoefficient) +
|
||||
damageCoefficient * shell.damage.armor * gun.reload.slice(1).length
|
||||
damageCoefficient *
|
||||
shell.armorDamage *
|
||||
gun.gunType!.value.extension.shellReloads.slice(1).length
|
||||
: undefined;
|
||||
const shells = gun.type === 'regular' ? 1 : gun.count;
|
||||
const shells =
|
||||
gun.gunType!.$case === 'regular'
|
||||
? 1
|
||||
: gun.gunType!.value.extension.shellCount;
|
||||
const shellReloads =
|
||||
gun.type === 'autoReloader'
|
||||
? gun.reload.map((reload) => reload * reloadCoefficient)
|
||||
gun.gunType!.$case === 'autoReloader'
|
||||
? gun.gunType!.value.extension.shellReloads.map(
|
||||
(reload) => reload * reloadCoefficient,
|
||||
)
|
||||
: undefined;
|
||||
const shellReload =
|
||||
gun.type === 'autoReloader' ? undefined : gun.reload * reloadCoefficient;
|
||||
gun.gunType!.$case === 'autoReloader'
|
||||
? undefined
|
||||
: (gun.gunType!.$case === 'regular'
|
||||
? gun.gunType!.value.extension.reload
|
||||
: gun.gunType!.value.extension.clipReload) * reloadCoefficient;
|
||||
const caliber = shell.caliber;
|
||||
const penetration =
|
||||
resolveNearPenetration(shell.penetration) * penetrationCoefficient;
|
||||
const clipDamage = gun.type === 'regular' ? undefined : gun.count * damage;
|
||||
const moduleDamage = shell.damage.module * damageCoefficient;
|
||||
const penetration = shell.penetration.near * penetrationCoefficient;
|
||||
const clipDamage =
|
||||
gun.gunType!.$case === 'regular'
|
||||
? undefined
|
||||
: gun.gunType!.value.extension.shellCount * damage;
|
||||
const moduleDamage = shell.moduleDamage * damageCoefficient;
|
||||
const explosionRadius = shell.explosionRadius;
|
||||
const shellVelocity = shell.speed * shellVelocityCoefficient;
|
||||
const aimTime = gun.aimTime * aimTimeCoefficient;
|
||||
const dispersion = gun.dispersion.base * dispersionStillCoefficient;
|
||||
const shellVelocity = shell.velocity * shellVelocityCoefficient;
|
||||
const aimTime = gun.gunType!.value.base.aimTime * aimTimeCoefficient;
|
||||
const dispersion =
|
||||
gun.gunType!.value.base.dispersionBase * dispersionStillCoefficient;
|
||||
const maxDispersion =
|
||||
gun.dispersion.base *
|
||||
gun.gunType!.value.base.dispersionBase *
|
||||
Math.sqrt(
|
||||
((gun.dispersion.traverse * turret.traverseSpeed) ** 2 +
|
||||
(track.dispersion.traverse * track.traverseSpeed) ** 2 +
|
||||
track.dispersion.move * tank.speed.forwards) **
|
||||
((gun.gunType!.value.base.dispersionTraverse * turret.traverseSpeed) **
|
||||
2 +
|
||||
(track.traverseSpeed * track.traverseSpeed) ** 2 +
|
||||
track.dispersionMove * tank.speedForwards) **
|
||||
2,
|
||||
);
|
||||
const dispersionMoving = track.dispersion.move * dispersionMovingCoefficient;
|
||||
const dispersionMoving = track.dispersionMove * dispersionMovingCoefficient;
|
||||
const dispersionHullTraversing =
|
||||
track.dispersion.traverse * dispersionHullTraverseCoefficient;
|
||||
track.traverseSpeed * dispersionHullTraverseCoefficient;
|
||||
const dispersionTurretTraversing =
|
||||
gun.dispersion.traverse * dispersionTurretTraverseCoefficient;
|
||||
gun.gunType!.value.base.dispersionTraverse *
|
||||
dispersionTurretTraverseCoefficient;
|
||||
const dispersionShooting =
|
||||
gun.dispersion.shot * dispersionAfterShotCoefficient;
|
||||
gun.gunType!.value.base.dispersionShot * dispersionAfterShotCoefficient;
|
||||
const dispersionGunDamaged =
|
||||
gun.dispersion.damaged * dispersionDamagedCoefficient;
|
||||
gun.gunType!.value.base.dispersionDamaged * dispersionDamagedCoefficient;
|
||||
const gunDepression =
|
||||
gunModelDefinition.pitch.max +
|
||||
gunDefaultPitch +
|
||||
@@ -421,17 +450,17 @@ export function tankCharacteristics(
|
||||
? -turretModelDefinition.yaw.min
|
||||
: undefined;
|
||||
const azimuthRight = turretModelDefinition.yaw?.max;
|
||||
const speedForwards = tank.speed.forwards + speedForwardsSum;
|
||||
const speedBackwards = tank.speed.backwards + speedBackwardsSum;
|
||||
const speedForwards = tank.speedForwards + speedForwardsSum;
|
||||
const speedBackwards = tank.speedBackwards + speedBackwardsSum;
|
||||
const enginePower = resolvedEnginePower;
|
||||
const hardTerrainCoefficientRaw =
|
||||
track.resistance.hard * resistanceHardCoefficient;
|
||||
track.resistanceHard * resistanceHardCoefficient;
|
||||
const hardTerrainCoefficient = 100 / hardTerrainCoefficientRaw;
|
||||
const mediumTerrainCoefficientRaw =
|
||||
track.resistance.medium * resistanceMediumCoefficient;
|
||||
track.resistanceMedium * resistanceMediumCoefficient;
|
||||
const mediumTerrainCoefficient = 100 / mediumTerrainCoefficientRaw;
|
||||
const softTerrainCoefficientRaw =
|
||||
track.resistance.soft * resistanceSoftCoefficient;
|
||||
track.resistanceSoft * resistanceSoftCoefficient;
|
||||
const softTerrainCoefficient = 100 / softTerrainCoefficientRaw;
|
||||
const powerToWeightRatioHardTerrain =
|
||||
resolvedEnginePower / weightTons / hardTerrainCoefficientRaw;
|
||||
@@ -445,37 +474,39 @@ export function tankCharacteristics(
|
||||
(resolvedEnginePower / stockEngine.power) *
|
||||
track.traverseSpeed *
|
||||
hullTraverseCoefficient *
|
||||
(track.resistance.hard / hardTerrainCoefficientRaw) *
|
||||
(track.resistanceHard / hardTerrainCoefficientRaw) *
|
||||
(stockWeight / weightKg);
|
||||
const hullTraverseMediumTerrain =
|
||||
(resolvedEnginePower / stockEngine.power) *
|
||||
track.traverseSpeed *
|
||||
hullTraverseCoefficient *
|
||||
(track.resistance.hard / mediumTerrainCoefficientRaw) *
|
||||
(track.resistanceHard / mediumTerrainCoefficientRaw) *
|
||||
(stockWeight / weightKg);
|
||||
const hullTraverseSoftTerrain =
|
||||
(resolvedEnginePower / stockEngine.power) *
|
||||
track.traverseSpeed *
|
||||
hullTraverseCoefficient *
|
||||
(track.resistance.hard / softTerrainCoefficientRaw) *
|
||||
(track.resistanceHard / softTerrainCoefficientRaw) *
|
||||
(stockWeight / weightKg);
|
||||
const health = (tank.health + turret.health) * healthCoefficient;
|
||||
const fireChance = engine.fireChance * fireChanceCoefficient;
|
||||
const viewRange = turret.viewRange * viewRangeCoefficient;
|
||||
const camouflageStill = tank.camouflage.still * camouflageCoefficientStill;
|
||||
const camouflageStill = tank.camouflageStill * camouflageCoefficientStill;
|
||||
const camouflageMoving =
|
||||
tank.class === 'lightTank'
|
||||
? tank.camouflage.still * camouflageCoefficientStill
|
||||
: tank.camouflage.moving * camouflageCoefficientMoving;
|
||||
tank.class === TankClass.LIGHT
|
||||
? tank.camouflageStill * camouflageCoefficientStill
|
||||
: tank.camouflageMoving * camouflageCoefficientMoving;
|
||||
const camouflageShootingStill =
|
||||
tank.camouflage.still * gun.camouflageLoss * camouflageCoefficientStill;
|
||||
tank.camouflageStill *
|
||||
gun.gunType!.value.base.camouflageLoss *
|
||||
camouflageCoefficientStill;
|
||||
const camouflageShootingMoving =
|
||||
(tank.class === 'lightTank'
|
||||
? tank.camouflage.still * camouflageCoefficientStill
|
||||
: tank.camouflage.moving * camouflageCoefficientMoving) *
|
||||
gun.camouflageLoss;
|
||||
(tank.class === TankClass.LIGHT
|
||||
? tank.camouflageStill * camouflageCoefficientStill
|
||||
: tank.camouflageMoving * camouflageCoefficientMoving) *
|
||||
gun.gunType!.value.base.camouflageLoss;
|
||||
const camouflageCaughtOnFire =
|
||||
tank.camouflage.onFire * tank.camouflage.still * camouflageCoefficientStill;
|
||||
tank.camouflageOnFire * tank.camouflageStill * camouflageCoefficientStill;
|
||||
const width = size[2];
|
||||
const height = size[0];
|
||||
const length = size[1];
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import {
|
||||
BlitzkitResponse,
|
||||
TankVotes,
|
||||
unwrapBlitzkitResponse,
|
||||
type BlitzkitResponse,
|
||||
type TankVotes,
|
||||
} from '@blitzkit/core';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import * as App from '../stores/app';
|
||||
import { $wargamingLogin } from '../stores/wargamingLogin';
|
||||
|
||||
const cache: Record<string, Promise<TankVotes> | TankVotes> = {};
|
||||
|
||||
export function useTankVotes(id: number) {
|
||||
const wargaming = App.use((state) => state.logins.wargaming);
|
||||
const [votes, setVotes] = useState<TankVotes | null>(null);
|
||||
const wargaming = useStore($wargamingLogin);
|
||||
|
||||
useEffect(() => {
|
||||
if (!wargaming.token) return;
|
||||
|
||||
if (id in cache) {
|
||||
Promise.all([cache[id]]).then(([votes]) => setVotes(votes));
|
||||
} else {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
equipmentDefinitions,
|
||||
modelDefinitions,
|
||||
provisionDefinitions,
|
||||
fetchEquipmentDefinitions,
|
||||
fetchModelDefinitions,
|
||||
fetchProvisionDefinitions,
|
||||
} from '@blitzkit/core';
|
||||
import { PlusIcon } from '@radix-ui/react-icons';
|
||||
import { Box, Flex, Heading, IconButton, Text } from '@radix-ui/themes';
|
||||
@@ -14,9 +14,9 @@ import { tankCharacteristics } from '../../../core/blitzkit/tankCharacteristics'
|
||||
import { CompareEphemeral } from '../../../stores/compareEphemeral';
|
||||
import { ComparePersistent } from '../../../stores/comparePersistent';
|
||||
|
||||
const awaitedModelDefinitions = await modelDefinitions;
|
||||
const awaitedEquipmentDefinitions = await equipmentDefinitions;
|
||||
const awaitedProvisionDefinitions = await provisionDefinitions;
|
||||
const modelDefinitions = await fetchModelDefinitions();
|
||||
const equipmentDefinitions = await fetchEquipmentDefinitions();
|
||||
const provisionDefinitions = await fetchProvisionDefinitions();
|
||||
|
||||
export function Page() {
|
||||
return (
|
||||
@@ -60,9 +60,9 @@ function Content() {
|
||||
),
|
||||
},
|
||||
{
|
||||
equipmentDefinitions: awaitedEquipmentDefinitions,
|
||||
provisionDefinitions: awaitedProvisionDefinitions,
|
||||
tankModelDefinition: awaitedModelDefinitions[thisMember.tank.id],
|
||||
equipmentDefinitions: equipmentDefinitions,
|
||||
provisionDefinitions: provisionDefinitions,
|
||||
tankModelDefinition: modelDefinitions.models[thisMember.tank.id],
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
@@ -102,8 +102,8 @@ export type EmbedConfigItem = (
|
||||
pad?: boolean;
|
||||
};
|
||||
|
||||
export type EmbedState = Record<string, EmbedConfigItem['default']>;
|
||||
export type EmbedStateStore = Record<string, EmbedConfigItem['default']>;
|
||||
|
||||
export const { Provider, use, useMutation, useStore } = createContextualStore(
|
||||
(init: EmbedState) => create<EmbedState>()(() => init),
|
||||
export const EmbedState = createContextualStore((init: EmbedStateStore) =>
|
||||
create<EmbedStateStore>()(() => init),
|
||||
);
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { TextProps } from '@radix-ui/themes';
|
||||
import type { EmbedConfig } from '.';
|
||||
import { EmbedState, type EmbedConfig, type ExtractEmbedConfigTypes } from '.';
|
||||
import type { EmbedConfigItemType, EmbedItemType } from './constants';
|
||||
|
||||
export function useEmbedStateCurry<Config extends EmbedConfig>() {
|
||||
function useState<Key extends keyof Config>(key: Key) {
|
||||
return EmbedState.use((state) => {
|
||||
return (state as EmbedState.ExtractEmbedConfigTypes<Config>)[key];
|
||||
return (state as ExtractEmbedConfigTypes<Config>)[key];
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { TankClass, Tier, TreeType } from '@blitzkit/core';
|
||||
import type { TankClass, TankType, Tier } from '@blitzkit/core';
|
||||
import { map } from 'nanostores';
|
||||
|
||||
export interface TankFilters {
|
||||
tiers: Tier[];
|
||||
nations: string[];
|
||||
classes: TankClass[];
|
||||
types: TreeType[];
|
||||
types: TankType[];
|
||||
testing: 'include' | 'exclude' | 'only';
|
||||
search?: string;
|
||||
searching: boolean;
|
||||
|
||||
Reference in new issue
Block a user