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