mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 03:59:50 +02:00
Merge branch 'dev'
This commit is contained in:
commit
23ea72bf40
44 files changed
+1138
-1517
No files matched your search
@@ -30,6 +30,7 @@ env:
|
|||||||
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
|
PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }}
|
||||||
PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }}
|
PUBLIC_BRANCH: ${{ secrets.PUBLIC_BRANCH }}
|
||||||
PUBLIC_GUIDE_WEBSITE: ${{ secrets.PUBLIC_GUIDE_WEBSITE }}
|
PUBLIC_GUIDE_WEBSITE: ${{ secrets.PUBLIC_GUIDE_WEBSITE }}
|
||||||
|
SERVICING: ${{ secrets.SERVICING }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|||||||
@@ -197,15 +197,10 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
|
|||||||
|
|
||||||
turret.guns.forEach((gun) => {
|
turret.guns.forEach((gun) => {
|
||||||
if (
|
if (
|
||||||
gun.gun_type!.value.base.research_cost !== undefined &&
|
gun.research_cost !== undefined &&
|
||||||
typeof gun.gun_type!.value.base.research_cost
|
typeof gun.research_cost.research_cost_type!.value === 'number'
|
||||||
.research_cost_type!.value === 'number'
|
|
||||||
) {
|
) {
|
||||||
gunXps.set(
|
gunXps.set(gun.id, gun.research_cost.research_cost_type!.value);
|
||||||
gun.gun_type!.value.base.id,
|
|
||||||
gun.gun_type!.value.base.research_cost.research_cost_type!
|
|
||||||
.value,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
GunDefinition,
|
GunDefinition,
|
||||||
GunDefinitionAutoReloaderProperties,
|
GunDefinitionAutoReloader,
|
||||||
ShellDefinition,
|
ShellDefinition,
|
||||||
} from '../protos';
|
} from '../protos';
|
||||||
|
|
||||||
@@ -15,34 +15,46 @@ export function resolveDpm(
|
|||||||
let dps: number;
|
let dps: number;
|
||||||
|
|
||||||
if (gun.gun_type!.$case === 'regular') {
|
if (gun.gun_type!.$case === 'regular') {
|
||||||
dps = alpha / (reloadCoefficient * gun.gun_type!.value.extension.reload);
|
dps = alpha / (reloadCoefficient * gun.gun_type!.value.reload);
|
||||||
} else if (gun.gun_type!.$case === 'auto_loader') {
|
} else if (gun.gun_type!.$case === 'auto_loader') {
|
||||||
dps =
|
const damage = alpha * gun.gun_type!.value.shell_count;
|
||||||
(alpha * gun.gun_type!.value.extension.shell_count) /
|
let time = gun.gun_type!.value.clip_reload * reloadCoefficient;
|
||||||
(gun.gun_type!.value.extension.clip_reload * reloadCoefficient +
|
|
||||||
(gun.gun_type!.value.extension.shell_count - 1) *
|
|
||||||
gun.gun_type!.value.extension.intra_clip *
|
|
||||||
intraClipCoefficient);
|
|
||||||
} else {
|
|
||||||
const mostOptimalShell =
|
|
||||||
gun.gun_type!.value.extension.shell_reloads.reduce<null | {
|
|
||||||
index: number;
|
|
||||||
reload: number;
|
|
||||||
}>((current, reloadRaw, index) => {
|
|
||||||
const reload =
|
|
||||||
reloadRaw * reloadCoefficient +
|
|
||||||
(index > 0
|
|
||||||
? (
|
|
||||||
gun.gun_type!.value
|
|
||||||
.extension as GunDefinitionAutoReloaderProperties
|
|
||||||
).intra_clip * intraClipCoefficient
|
|
||||||
: 0);
|
|
||||||
|
|
||||||
if (current === null || reload < current.reload) {
|
if (gun.burst) {
|
||||||
return { index, reload };
|
time +=
|
||||||
}
|
(gun.gun_type!.value.shell_count / gun.burst.count - 1) *
|
||||||
return current;
|
gun.gun_type!.value.intra_clip *
|
||||||
}, null)!;
|
intraClipCoefficient;
|
||||||
|
|
||||||
|
time +=
|
||||||
|
(gun.gun_type!.value.shell_count / gun.burst.count) *
|
||||||
|
(gun.burst.count - 1) *
|
||||||
|
gun.burst.interval;
|
||||||
|
} else {
|
||||||
|
time +=
|
||||||
|
(gun.gun_type!.value.shell_count - 1) *
|
||||||
|
gun.gun_type!.value.intra_clip *
|
||||||
|
intraClipCoefficient;
|
||||||
|
}
|
||||||
|
|
||||||
|
dps = damage / time;
|
||||||
|
} else {
|
||||||
|
const mostOptimalShell = gun.gun_type!.value.shell_reloads.reduce<null | {
|
||||||
|
index: number;
|
||||||
|
reload: number;
|
||||||
|
}>((current, reloadRaw, index) => {
|
||||||
|
const reload =
|
||||||
|
reloadRaw * reloadCoefficient +
|
||||||
|
(index > 0
|
||||||
|
? (gun.gun_type!.value as GunDefinitionAutoReloader).intra_clip *
|
||||||
|
intraClipCoefficient
|
||||||
|
: 0);
|
||||||
|
|
||||||
|
if (current === null || reload < current.reload) {
|
||||||
|
return { index, reload };
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}, null)!;
|
||||||
|
|
||||||
dps = alpha / mostOptimalShell?.reload;
|
dps = alpha / mostOptimalShell?.reload;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.gun_type!.value.base.id)) {
|
if (!gunIds.has(gun.id)) {
|
||||||
gunIds.add(gun.gun_type!.value.base.id);
|
gunIds.add(gun.id);
|
||||||
guns.push({ turret, gun });
|
guns.push({ turret, gun });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -108,58 +108,47 @@ message GunDefinition {
|
|||||||
GunDefinitionAutoLoader auto_loader = 2;
|
GunDefinitionAutoLoader auto_loader = 2;
|
||||||
GunDefinitionAutoReloader auto_reloader = 3;
|
GunDefinitionAutoReloader auto_reloader = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
required uint32 id = 4;
|
||||||
|
required float rotation_speed = 5;
|
||||||
|
optional ResearchCost research_cost = 6;
|
||||||
|
required uint32 weight = 7;
|
||||||
|
required I18nString name = 8;
|
||||||
|
required uint32 tier = 9;
|
||||||
|
repeated ShellDefinition shells = 10;
|
||||||
|
required float camouflage_loss = 11;
|
||||||
|
required float aim_time = 12;
|
||||||
|
required float dispersion_base = 13;
|
||||||
|
required float dispersion_traverse = 14;
|
||||||
|
required float dispersion_shot = 15;
|
||||||
|
required float dispersion_damaged = 16;
|
||||||
|
repeated Unlock unlocks = 17;
|
||||||
|
required uint32 shell_capacity = 18;
|
||||||
|
optional AssaultRanges assault_ranges = 19;
|
||||||
|
optional Burst burst = 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
message GunDefinitionRegular {
|
message GunDefinitionRegular {
|
||||||
required GunDefinitionBase base = 1;
|
|
||||||
required GunDefinitionRegularProperties extension = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message GunDefinitionRegularProperties {
|
|
||||||
required float reload = 1;
|
required float reload = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message GunDefinitionAutoLoader {
|
message GunDefinitionAutoLoader {
|
||||||
required GunDefinitionBase base = 1;
|
|
||||||
required GunDefinitionAutoLoaderProperties extension = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message GunDefinitionAutoLoaderProperties {
|
|
||||||
required float clip_reload = 1;
|
required float clip_reload = 1;
|
||||||
required float intra_clip = 2;
|
required float intra_clip = 2;
|
||||||
required float shell_count = 3;
|
required float shell_count = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message GunDefinitionAutoReloader {
|
message Burst {
|
||||||
required GunDefinitionBase base = 1;
|
required uint32 count = 1;
|
||||||
required GunDefinitionAutoReloaderProperties extension = 2;
|
required float interval = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message GunDefinitionAutoReloaderProperties {
|
message GunDefinitionAutoReloader {
|
||||||
repeated float shell_reloads = 1;
|
repeated float shell_reloads = 1;
|
||||||
required float intra_clip = 2;
|
required float intra_clip = 2;
|
||||||
required float shell_count = 3;
|
required float shell_count = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message GunDefinitionBase {
|
|
||||||
required uint32 id = 1;
|
|
||||||
required float rotation_speed = 2;
|
|
||||||
optional ResearchCost research_cost = 3;
|
|
||||||
required uint32 weight = 4;
|
|
||||||
required I18nString name = 5;
|
|
||||||
required uint32 tier = 6;
|
|
||||||
repeated ShellDefinition shells = 7;
|
|
||||||
required float camouflage_loss = 8;
|
|
||||||
required float aim_time = 9;
|
|
||||||
required float dispersion_base = 10;
|
|
||||||
required float dispersion_traverse = 11;
|
|
||||||
required float dispersion_shot = 12;
|
|
||||||
required float dispersion_damaged = 13;
|
|
||||||
repeated Unlock unlocks = 14;
|
|
||||||
required uint32 shell_capacity = 15;
|
|
||||||
optional AssaultRanges assault_ranges = 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
message AssaultRanges {
|
message AssaultRanges {
|
||||||
repeated AssaultRange ranges = 1;
|
repeated AssaultRange ranges = 1;
|
||||||
repeated ShellType types = 2;
|
repeated ShellType types = 2;
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
@@ -1096,8 +1096,8 @@
|
|||||||
"dpmEffective": "Effective at 60s",
|
"dpmEffective": "Effective at 60s",
|
||||||
"shells": "Shells",
|
"shells": "Shells",
|
||||||
"mostOptimalShellIndex": "Most optimal shell",
|
"mostOptimalShellIndex": "Most optimal shell",
|
||||||
"shellReloads": "Shell reloads",
|
"shellReloads": "Reloads",
|
||||||
"shellReload": "Shell reload",
|
"shellReload": "Reload",
|
||||||
"intraClip": "Intra-clip",
|
"intraClip": "Intra-clip",
|
||||||
"caliber": "Caliber",
|
"caliber": "Caliber",
|
||||||
"penetration": "Penetration",
|
"penetration": "Penetration",
|
||||||
@@ -1155,7 +1155,9 @@
|
|||||||
"shellRange": "Shell range",
|
"shellRange": "Shell range",
|
||||||
"shellCapacity": "Shell capacity",
|
"shellCapacity": "Shell capacity",
|
||||||
"gunType": "Gun type",
|
"gunType": "Gun type",
|
||||||
"penetrationAt250m": "Penetration at 250m"
|
"penetrationAt250m": "Penetration at 250m",
|
||||||
|
"burstShells": "Burst shell count",
|
||||||
|
"burstInterShell": "Burst inter-shell"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import {
|
|||||||
EquipmentSlot,
|
EquipmentSlot,
|
||||||
GameDefinitions,
|
GameDefinitions,
|
||||||
GunDefinition,
|
GunDefinition,
|
||||||
GunDefinitionBase,
|
|
||||||
I18nString,
|
I18nString,
|
||||||
MapDefinitions,
|
MapDefinitions,
|
||||||
ModelDefinitions,
|
ModelDefinitions,
|
||||||
@@ -274,6 +273,7 @@ interface VehicleDefinitions {
|
|||||||
pumpGunMode?: boolean;
|
pumpGunMode?: boolean;
|
||||||
pumpGunReloadTimes?: string;
|
pumpGunReloadTimes?: string;
|
||||||
clip?: { count: number; rate: number };
|
clip?: { count: number; rate: number };
|
||||||
|
burst?: { count: number; rate: number };
|
||||||
models: { undamaged: string };
|
models: { undamaged: string };
|
||||||
extras?: {
|
extras?: {
|
||||||
trayShell?: {
|
trayShell?: {
|
||||||
@@ -1304,7 +1304,7 @@ export async function definitions() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const base = {
|
tankDefinitions.tanks[tankId].turrets[turretIndex].guns.push({
|
||||||
id: gunId,
|
id: gunId,
|
||||||
weight: gunListEntry.weight,
|
weight: gunListEntry.weight,
|
||||||
rotation_speed: gunListEntry.rotationSpeed,
|
rotation_speed: gunListEntry.rotationSpeed,
|
||||||
@@ -1324,41 +1324,38 @@ export async function definitions() {
|
|||||||
unlocks: resolveUnlocks(gun.unlocks),
|
unlocks: resolveUnlocks(gun.unlocks),
|
||||||
shell_capacity: gun.maxAmmo ?? gunListEntry.maxAmmo,
|
shell_capacity: gun.maxAmmo ?? gunListEntry.maxAmmo,
|
||||||
assault_ranges,
|
assault_ranges,
|
||||||
} satisfies GunDefinitionBase;
|
burst:
|
||||||
|
gun.burst && gun.burst.count > 1
|
||||||
tankDefinitions.tanks[tankId].turrets[turretIndex].guns.push({
|
? {
|
||||||
|
count: gun.burst.count,
|
||||||
|
interval: 60 / gun.burst.rate,
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
gun_type:
|
gun_type:
|
||||||
gunType === 'regular'
|
gunType === 'regular'
|
||||||
? {
|
? {
|
||||||
$case: 'regular',
|
$case: 'regular',
|
||||||
value: {
|
value: {
|
||||||
base,
|
reload: gun.reloadTime,
|
||||||
extension: { reload: gun.reloadTime },
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
: gunType === 'autoReloader'
|
: gunType === 'autoReloader'
|
||||||
? {
|
? {
|
||||||
$case: 'auto_reloader',
|
$case: 'auto_reloader',
|
||||||
value: {
|
value: {
|
||||||
base,
|
intra_clip: 60 / gun.clip!.rate,
|
||||||
extension: {
|
shell_count: gunClipCount,
|
||||||
intra_clip: 60 / gun.clip!.rate,
|
shell_reloads: gun
|
||||||
shell_count: gunClipCount,
|
.pumpGunReloadTimes!.split(' ')
|
||||||
shell_reloads: gun
|
.map(Number),
|
||||||
.pumpGunReloadTimes!.split(' ')
|
|
||||||
.map(Number),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
$case: 'auto_loader',
|
$case: 'auto_loader',
|
||||||
value: {
|
value: {
|
||||||
base,
|
intra_clip: 60 / gun.clip!.rate,
|
||||||
extension: {
|
clip_reload: gun.reloadTime,
|
||||||
intra_clip: 60 / gun.clip!.rate,
|
shell_count: gunClipCount,
|
||||||
clip_reload: gun.reloadTime,
|
|
||||||
shell_count: gunClipCount,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} satisfies GunDefinition);
|
} satisfies GunDefinition);
|
||||||
@@ -1407,7 +1404,7 @@ export async function definitions() {
|
|||||||
|
|
||||||
tankDefinitions.tanks[tankId].turrets[turretIndex].guns[
|
tankDefinitions.tanks[tankId].turrets[turretIndex].guns[
|
||||||
gunIndex
|
gunIndex
|
||||||
].gun_type!.value.base!.shells.push({
|
].shells.push({
|
||||||
id: shellId,
|
id: shellId,
|
||||||
name: shellName,
|
name: shellName,
|
||||||
velocity: gunShellEntry.speed,
|
velocity: gunShellEntry.speed,
|
||||||
@@ -1495,9 +1492,7 @@ export async function definitions() {
|
|||||||
turret.research_cost = turretXps.get(turret.id);
|
turret.research_cost = turretXps.get(turret.id);
|
||||||
|
|
||||||
Object.values(turret.guns).forEach((gunRaw) => {
|
Object.values(turret.guns).forEach((gunRaw) => {
|
||||||
gunRaw.gun_type!.value.base!.research_cost = gunXps.get(
|
gunRaw.research_cost = gunXps.get(gunRaw.id);
|
||||||
gunRaw.gun_type!.value.base!.id,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -100,8 +100,7 @@ export function ArmorPlateDisplay() {
|
|||||||
const { protagonist } = duelStore.getState();
|
const { protagonist } = duelStore.getState();
|
||||||
const tank = draft.model;
|
const tank = draft.model;
|
||||||
const turret = tank.turrets[protagonist.turret.id];
|
const turret = tank.turrets[protagonist.turret.id];
|
||||||
const gun =
|
const gun = turret.guns[protagonist.gun.id];
|
||||||
turret.guns[protagonist.gun.gun_type!.value.base.id];
|
|
||||||
const track = tank.tracks[protagonist.track.id];
|
const track = tank.tracks[protagonist.track.id];
|
||||||
|
|
||||||
if (highlightArmor.name.startsWith('hull_')) {
|
if (highlightArmor.name.startsWith('hull_')) {
|
||||||
@@ -154,12 +153,8 @@ export function ArmorPlateDisplay() {
|
|||||||
const turret = tank.turrets[protagonist.turret.id];
|
const turret = tank.turrets[protagonist.turret.id];
|
||||||
const initialTurret =
|
const initialTurret =
|
||||||
initialTank.turrets[protagonist.turret.id];
|
initialTank.turrets[protagonist.turret.id];
|
||||||
const gun =
|
const gun = turret.guns[protagonist.gun.id];
|
||||||
turret.guns[protagonist.gun.gun_type!.value.base.id];
|
const initialGun = initialTurret.guns[protagonist.gun.id];
|
||||||
const initialGun =
|
|
||||||
initialTurret.guns[
|
|
||||||
protagonist.gun.gun_type!.value.base.id
|
|
||||||
];
|
|
||||||
const track = tank.tracks[protagonist.track.id];
|
const track = tank.tracks[protagonist.track.id];
|
||||||
const initialTrack =
|
const initialTrack =
|
||||||
initialTank.tracks[protagonist.track.id];
|
initialTank.tracks[protagonist.track.id];
|
||||||
|
|||||||
@@ -24,8 +24,7 @@ export const PrimaryArmorScene = memo(() => {
|
|||||||
const tankModelDefinition = useTankModelDefinition();
|
const tankModelDefinition = useTankModelDefinition();
|
||||||
const trackModelDefinition = tankModelDefinition.tracks[track.id];
|
const trackModelDefinition = tankModelDefinition.tracks[track.id];
|
||||||
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
||||||
const gunModelDefinition =
|
const gunModelDefinition = turretModelDefinition.guns[gun.id];
|
||||||
turretModelDefinition.guns[gun.gun_type!.value.base.id];
|
|
||||||
const hullOrigin = correctZYTuple(trackModelDefinition.origin);
|
const hullOrigin = correctZYTuple(trackModelDefinition.origin);
|
||||||
const turretOrigin = correctZYTuple(tankModelDefinition.turret_origin);
|
const turretOrigin = correctZYTuple(tankModelDefinition.turret_origin);
|
||||||
const gunOrigin = correctZYTuple(turretModelDefinition.gun_origin);
|
const gunOrigin = correctZYTuple(turretModelDefinition.gun_origin);
|
||||||
|
|||||||
@@ -36,8 +36,7 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
|
|||||||
const tankModelDefinition = useTankModelDefinition();
|
const tankModelDefinition = useTankModelDefinition();
|
||||||
const trackModelDefinition = tankModelDefinition.tracks[track.id];
|
const trackModelDefinition = tankModelDefinition.tracks[track.id];
|
||||||
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
||||||
const gunModelDefinition =
|
const gunModelDefinition = turretModelDefinition.guns[gun.id];
|
||||||
turretModelDefinition.guns[gun.gun_type!.value.base.id];
|
|
||||||
const hullOrigin = correctZYTuple(trackModelDefinition.origin);
|
const hullOrigin = correctZYTuple(trackModelDefinition.origin);
|
||||||
const turretOrigin = correctZYTuple(tankModelDefinition.turret_origin);
|
const turretOrigin = correctZYTuple(tankModelDefinition.turret_origin);
|
||||||
const gunOrigin = correctZYTuple(turretModelDefinition.gun_origin);
|
const gunOrigin = correctZYTuple(turretModelDefinition.gun_origin);
|
||||||
|
|||||||
@@ -42,8 +42,7 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
|||||||
const tankModelDefinition = useTankModelDefinition();
|
const tankModelDefinition = useTankModelDefinition();
|
||||||
const trackModelDefinition = tankModelDefinition.tracks[track.id];
|
const trackModelDefinition = tankModelDefinition.tracks[track.id];
|
||||||
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
||||||
const gunModelDefinition =
|
const gunModelDefinition = turretModelDefinition.guns[gun.id];
|
||||||
turretModelDefinition.guns[gun.gun_type!.value.base.id];
|
|
||||||
const hullOrigin = correctZYTuple(trackModelDefinition.origin);
|
const hullOrigin = correctZYTuple(trackModelDefinition.origin);
|
||||||
const turretOrigin = correctZYTuple(tankModelDefinition.turret_origin);
|
const turretOrigin = correctZYTuple(tankModelDefinition.turret_origin);
|
||||||
const gunOrigin = correctZYTuple(turretModelDefinition.gun_origin);
|
const gunOrigin = correctZYTuple(turretModelDefinition.gun_origin);
|
||||||
|
|||||||
@@ -33,43 +33,35 @@ export function Body({ stats }: BodyProps) {
|
|||||||
<StickyColumnHeaderCell key={key} top={137}>
|
<StickyColumnHeaderCell key={key} top={137}>
|
||||||
<Flex justify="center">
|
<Flex justify="center">
|
||||||
<Flex>
|
<Flex>
|
||||||
{gun.gun_type!.value.base.shells.map(
|
{gun.shells.map((thisShell, shellIndex) => (
|
||||||
(thisShell, shellIndex) => (
|
<IconButton
|
||||||
<IconButton
|
color={thisShell.id === shell.id ? undefined : 'gray'}
|
||||||
color={thisShell.id === shell.id ? undefined : 'gray'}
|
variant="soft"
|
||||||
variant="soft"
|
key={thisShell.id}
|
||||||
key={thisShell.id}
|
style={{
|
||||||
style={{
|
borderTopLeftRadius: shellIndex === 0 ? undefined : 0,
|
||||||
borderTopLeftRadius: shellIndex === 0 ? undefined : 0,
|
borderBottomLeftRadius:
|
||||||
borderBottomLeftRadius:
|
shellIndex === 0 ? undefined : 0,
|
||||||
shellIndex === 0 ? undefined : 0,
|
borderTopRightRadius:
|
||||||
borderTopRightRadius:
|
shellIndex === gun.shells.length - 1 ? undefined : 0,
|
||||||
shellIndex ===
|
borderBottomRightRadius:
|
||||||
gun.gun_type!.value.base.shells.length - 1
|
shellIndex === gun.shells.length - 1 ? undefined : 0,
|
||||||
? undefined
|
marginLeft: shellIndex === 0 ? 0 : -1,
|
||||||
: 0,
|
}}
|
||||||
borderBottomRightRadius:
|
onClick={() => {
|
||||||
shellIndex ===
|
mutateCompareEphemeral((draft) => {
|
||||||
gun.gun_type!.value.base.shells.length - 1
|
draft.members[index].shell = thisShell;
|
||||||
? undefined
|
});
|
||||||
: 0,
|
}}
|
||||||
marginLeft: shellIndex === 0 ? 0 : -1,
|
>
|
||||||
}}
|
<img
|
||||||
onClick={() => {
|
alt={unwrap(thisShell.name)}
|
||||||
mutateCompareEphemeral((draft) => {
|
width={16}
|
||||||
draft.members[index].shell = thisShell;
|
height={16}
|
||||||
});
|
src={asset(`icons/shells/${thisShell.icon}.webp`)}
|
||||||
}}
|
/>
|
||||||
>
|
</IconButton>
|
||||||
<img
|
))}
|
||||||
alt={unwrap(thisShell.name)}
|
|
||||||
width={16}
|
|
||||||
height={16}
|
|
||||||
src={asset(`icons/shells/${thisShell.icon}.webp`)}
|
|
||||||
/>
|
|
||||||
</IconButton>
|
|
||||||
),
|
|
||||||
)}
|
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
</StickyColumnHeaderCell>
|
</StickyColumnHeaderCell>
|
||||||
|
|||||||
@@ -250,8 +250,7 @@ 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.shell = member.gun.shells[0];
|
||||||
member.gun.gun_type!.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];
|
||||||
});
|
});
|
||||||
@@ -267,8 +266,7 @@ 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.shell = member.gun.shells[0];
|
||||||
member.gun.gun_type!.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)!;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -62,7 +62,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.gun_type!.value.base.shells[0];
|
draft.shell = draft.gun.shells[0];
|
||||||
|
|
||||||
onChange?.(draft);
|
onChange?.(draft);
|
||||||
setTurretMenuOpen(false);
|
setTurretMenuOpen(false);
|
||||||
@@ -83,9 +83,7 @@ export function ModuleManager({
|
|||||||
|
|
||||||
<ModuleButton
|
<ModuleButton
|
||||||
module="gun"
|
module="gun"
|
||||||
discriminator={
|
discriminator={TIER_ROMAN_NUMERALS[modules.gun.tier]}
|
||||||
TIER_ROMAN_NUMERALS[modules.gun.gun_type!.value.base.tier]
|
|
||||||
}
|
|
||||||
onClick={() => setGunMenuOpen(true)}
|
onClick={() => setGunMenuOpen(true)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -95,18 +93,16 @@ export function ModuleManager({
|
|||||||
</DropdownMenu.Trigger>
|
</DropdownMenu.Trigger>
|
||||||
|
|
||||||
<DropdownMenu.Content>
|
<DropdownMenu.Content>
|
||||||
<DropdownMenu.RadioGroup
|
<DropdownMenu.RadioGroup value={`${modules.gun.id}`}>
|
||||||
value={`${modules.gun.gun_type!.value.base.id}`}
|
|
||||||
>
|
|
||||||
{[...modules.turret.guns].reverse().map((gun) => {
|
{[...modules.turret.guns].reverse().map((gun) => {
|
||||||
return (
|
return (
|
||||||
<DropdownMenu.RadioItem
|
<DropdownMenu.RadioItem
|
||||||
key={gun.gun_type!.value.base.id}
|
key={gun.id}
|
||||||
value={`${gun.gun_type!.value.base.id}`}
|
value={`${gun.id}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const draft = { ...modules };
|
const draft = { ...modules };
|
||||||
draft.gun = gun;
|
draft.gun = gun;
|
||||||
draft.shell = draft.gun.gun_type!.value.base.shells[0];
|
draft.shell = draft.gun.shells[0];
|
||||||
|
|
||||||
onChange?.(draft);
|
onChange?.(draft);
|
||||||
setGunMenuOpen(false);
|
setGunMenuOpen(false);
|
||||||
@@ -115,11 +111,9 @@ export function ModuleManager({
|
|||||||
<ModuleButton
|
<ModuleButton
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
module="gun"
|
module="gun"
|
||||||
discriminator={
|
discriminator={TIER_ROMAN_NUMERALS[gun.tier]}
|
||||||
TIER_ROMAN_NUMERALS[gun.gun_type!.value.base.tier]
|
|
||||||
}
|
|
||||||
/>{' '}
|
/>{' '}
|
||||||
{unwrap(gun.gun_type!.value.base.name)}
|
{unwrap(gun.name)}
|
||||||
</DropdownMenu.RadioItem>
|
</DropdownMenu.RadioItem>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
import { Heading, Link, Text } from '@radix-ui/themes';
|
||||||
|
import { PageWrapper } from './PageWrapper';
|
||||||
|
---
|
||||||
|
|
||||||
|
<PageWrapper align="center" justify="center" gap="0">
|
||||||
|
<Heading>BlitzKit down for servicing</Heading>
|
||||||
|
<Text color="gray">
|
||||||
|
<Link color="blue" href="https://discord.gg/nDt7AjGJQH">
|
||||||
|
Join the official Discord server
|
||||||
|
</Link>
|
||||||
|
for updates.
|
||||||
|
</Text>
|
||||||
|
</PageWrapper>
|
||||||
@@ -25,19 +25,18 @@ export function TankSearchCard({ tank, onSelect }: TankSearchCardProps) {
|
|||||||
|
|
||||||
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.gun_type!.value.base.shells[0];
|
const shell0 = gun.shells[0];
|
||||||
const shell1 = gun.gun_type!.value.base.shells[1];
|
const shell1 = gun.shells[1];
|
||||||
const shell2 = gun.gun_type!.value.base.shells[2];
|
const shell2 = gun.shells[2];
|
||||||
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 = modelDefinitions.models[tank.id];
|
const tankModelDefinition = modelDefinitions.models[tank.id];
|
||||||
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
||||||
const gunModelDefinition =
|
const gunModelDefinition = turretModelDefinition.guns[gun.id];
|
||||||
turretModelDefinition.guns[gun.gun_type!.value.base.id];
|
|
||||||
|
|
||||||
switch (tankopediaSort.by) {
|
switch (tankopediaSort.by) {
|
||||||
case 'fire.aimTime':
|
case 'fire.aimTime':
|
||||||
return gun.gun_type!.value.base.aim_time.toFixed(2);
|
return gun.aim_time.toFixed(2);
|
||||||
case 'fire.caliber':
|
case 'fire.caliber':
|
||||||
return shell0.caliber.toFixed(0);
|
return shell0.caliber.toFixed(0);
|
||||||
case 'fire.damage':
|
case 'fire.damage':
|
||||||
@@ -47,7 +46,7 @@ export function TankSearchCard({ tank, onSelect }: TankSearchCardProps) {
|
|||||||
case 'fire.dispersionMoving':
|
case 'fire.dispersionMoving':
|
||||||
return tracks.dispersion_move.toFixed(3);
|
return tracks.dispersion_move.toFixed(3);
|
||||||
case 'fire.dispersionStill':
|
case 'fire.dispersionStill':
|
||||||
return gun.gun_type!.value.base.dispersion_base.toFixed(3);
|
return gun.dispersion_base.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':
|
||||||
@@ -63,7 +62,7 @@ export function TankSearchCard({ tank, onSelect }: TankSearchCardProps) {
|
|||||||
case 'fire.shellVelocity':
|
case 'fire.shellVelocity':
|
||||||
return shell0.velocity.toFixed(0);
|
return shell0.velocity.toFixed(0);
|
||||||
case 'fire.shellCapacity':
|
case 'fire.shellCapacity':
|
||||||
return gun.gun_type!.value.base.shell_capacity.toFixed(0);
|
return gun.shell_capacity.toFixed(0);
|
||||||
case 'fire.shellRange':
|
case 'fire.shellRange':
|
||||||
return shell0.range.toFixed(0);
|
return shell0.range.toFixed(0);
|
||||||
case 'fire.gunDepression':
|
case 'fire.gunDepression':
|
||||||
@@ -89,7 +88,7 @@ export function TankSearchCard({ tank, onSelect }: TankSearchCardProps) {
|
|||||||
engine.weight +
|
engine.weight +
|
||||||
tracks.weight +
|
tracks.weight +
|
||||||
turret.weight +
|
turret.weight +
|
||||||
gun.gun_type!.value.base.weight)
|
gun.weight)
|
||||||
).toFixed(1);
|
).toFixed(1);
|
||||||
case 'maneuverability.weight':
|
case 'maneuverability.weight':
|
||||||
return (
|
return (
|
||||||
@@ -97,7 +96,7 @@ export function TankSearchCard({ tank, onSelect }: TankSearchCardProps) {
|
|||||||
engine.weight +
|
engine.weight +
|
||||||
tracks.weight +
|
tracks.weight +
|
||||||
turret.weight +
|
turret.weight +
|
||||||
gun.gun_type!.value.base.weight) /
|
gun.weight) /
|
||||||
1000
|
1000
|
||||||
).toFixed(1);
|
).toFixed(1);
|
||||||
case 'maneuverability.traverseSpeed':
|
case 'maneuverability.traverseSpeed':
|
||||||
@@ -111,11 +110,7 @@ export function TankSearchCard({ tank, onSelect }: TankSearchCardProps) {
|
|||||||
case 'survivability.camouflageMoving':
|
case 'survivability.camouflageMoving':
|
||||||
return (tank.camouflage_moving * 100).toFixed(0);
|
return (tank.camouflage_moving * 100).toFixed(0);
|
||||||
case 'survivability.camouflageShooting':
|
case 'survivability.camouflageShooting':
|
||||||
return (
|
return (tank.camouflage_still * gun.camouflage_loss * 100).toFixed(0);
|
||||||
tank.camouflage_still *
|
|
||||||
gun.gun_type!.value.base.camouflage_loss *
|
|
||||||
100
|
|
||||||
).toFixed(0);
|
|
||||||
case 'survivability.volume': {
|
case 'survivability.volume': {
|
||||||
const dimensions = normalizeBoundingBox(
|
const dimensions = normalizeBoundingBox(
|
||||||
unionBoundingBox(
|
unionBoundingBox(
|
||||||
|
|||||||
@@ -104,11 +104,9 @@ export const TankSearch = memo<TankSearchProps>(
|
|||||||
sorted = filtered.sort(
|
sorted = filtered.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
a.camouflage_still *
|
a.camouflage_still *
|
||||||
a.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base
|
a.turrets.at(-1)!.guns.at(-1)!.camouflage_loss -
|
||||||
.camouflage_loss -
|
|
||||||
b.camouflage_still *
|
b.camouflage_still *
|
||||||
b.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base
|
b.turrets.at(-1)!.guns.at(-1)!.camouflage_loss,
|
||||||
.camouflage_loss,
|
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -171,11 +169,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)!.gun_type!.value.base.shells[0],
|
a.turrets.at(-1)!.guns.at(-1)!.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)!.gun_type!.value.base.shells[0],
|
b.turrets.at(-1)!.guns.at(-1)!.shells[0],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
@@ -183,18 +181,16 @@ 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)!.gun_type!.value.base.shells[1]
|
(a.turrets.at(-1)!.guns.at(-1)!.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)!.gun_type!.value.base
|
a.turrets.at(-1)!.guns.at(-1)!.shells[1],
|
||||||
.shells[1],
|
|
||||||
)
|
)
|
||||||
: 0) -
|
: 0) -
|
||||||
(b.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base.shells[1]
|
(b.turrets.at(-1)!.guns.at(-1)!.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)!.gun_type!.value.base
|
b.turrets.at(-1)!.guns.at(-1)!.shells[1],
|
||||||
.shells[1],
|
|
||||||
)
|
)
|
||||||
: 0),
|
: 0),
|
||||||
);
|
);
|
||||||
@@ -211,108 +207,92 @@ 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)!.gun_type!.value.base.shells[0]
|
a.turrets.at(-1)!.guns.at(-1)!.shells[0].caliber -
|
||||||
.caliber -
|
b.turrets.at(-1)!.guns.at(-1)!.shells[0].caliber,
|
||||||
b.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base.shells[0]
|
|
||||||
.caliber,
|
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'fire.standardPenetration':
|
case 'fire.standardPenetration':
|
||||||
sorted = filtered.sort(
|
sorted = filtered.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
a.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base.shells[0]
|
a.turrets.at(-1)!.guns.at(-1)!.shells[0].penetration.near -
|
||||||
.penetration.near -
|
b.turrets.at(-1)!.guns.at(-1)!.shells[0].penetration.near,
|
||||||
b.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base.shells[0]
|
|
||||||
.penetration.near,
|
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'fire.premiumPenetration':
|
case 'fire.premiumPenetration':
|
||||||
sorted = filtered.sort(
|
sorted = filtered.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
(a.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base.shells[1]
|
(a.turrets.at(-1)!.guns.at(-1)!.shells[1]?.penetration.near ??
|
||||||
?.penetration.near ?? 0) -
|
0) -
|
||||||
(b.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base.shells[1]
|
(b.turrets.at(-1)!.guns.at(-1)!.shells[1]?.penetration.near ??
|
||||||
?.penetration.near ?? 0),
|
0),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'fire.tertiaryPenetration':
|
case 'fire.tertiaryPenetration':
|
||||||
sorted = filtered.sort(
|
sorted = filtered.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
(a.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base.shells[2]
|
(a.turrets.at(-1)!.guns.at(-1)!.shells[2]?.penetration.near ??
|
||||||
?.penetration.near ?? 0) -
|
0) -
|
||||||
(b.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base.shells[2]
|
(b.turrets.at(-1)!.guns.at(-1)!.shells[2]?.penetration.near ??
|
||||||
?.penetration.near ?? 0),
|
0),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'fire.damage':
|
case 'fire.damage':
|
||||||
sorted = filtered.sort(
|
sorted = filtered.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
a.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base.shells[0]
|
a.turrets.at(-1)!.guns.at(-1)!.shells[0].armor_damage -
|
||||||
.armor_damage -
|
b.turrets.at(-1)!.guns.at(-1)!.shells[0].armor_damage,
|
||||||
b.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base.shells[0]
|
|
||||||
.armor_damage,
|
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'fire.moduleDamage':
|
case 'fire.moduleDamage':
|
||||||
sorted = filtered.sort(
|
sorted = filtered.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
a.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base.shells[0]
|
a.turrets.at(-1)!.guns.at(-1)!.shells[0].module_damage -
|
||||||
.module_damage -
|
b.turrets.at(-1)!.guns.at(-1)!.shells[0].module_damage,
|
||||||
b.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base.shells[0]
|
|
||||||
.module_damage,
|
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'fire.shellVelocity':
|
case 'fire.shellVelocity':
|
||||||
sorted = filtered.sort(
|
sorted = filtered.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
a.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base.shells[0]
|
a.turrets.at(-1)!.guns.at(-1)!.shells[0].velocity -
|
||||||
.velocity -
|
b.turrets.at(-1)!.guns.at(-1)!.shells[0].velocity,
|
||||||
b.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base.shells[0]
|
|
||||||
.velocity,
|
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'fire.shellRange':
|
case 'fire.shellRange':
|
||||||
sorted = filtered.sort(
|
sorted = filtered.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
a.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base.shells[0]
|
a.turrets.at(-1)!.guns.at(-1)!.shells[0].range -
|
||||||
.range -
|
b.turrets.at(-1)!.guns.at(-1)!.shells[0].range,
|
||||||
b.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base.shells[0]
|
|
||||||
.range,
|
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'fire.shellCapacity':
|
case 'fire.shellCapacity':
|
||||||
sorted = filtered.sort(
|
sorted = filtered.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
a.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base
|
a.turrets.at(-1)!.guns.at(-1)!.shell_capacity -
|
||||||
.shell_capacity -
|
b.turrets.at(-1)!.guns.at(-1)!.shell_capacity,
|
||||||
b.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base
|
|
||||||
.shell_capacity,
|
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'fire.aimTime':
|
case 'fire.aimTime':
|
||||||
sorted = filtered.sort(
|
sorted = filtered.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
a.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base.aim_time -
|
a.turrets.at(-1)!.guns.at(-1)!.aim_time -
|
||||||
b.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base.aim_time,
|
b.turrets.at(-1)!.guns.at(-1)!.aim_time,
|
||||||
);
|
);
|
||||||
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)!.gun_type!.value.base
|
a.turrets.at(-1)!.guns.at(-1)!.dispersion_base -
|
||||||
.dispersion_base -
|
b.turrets.at(-1)!.guns.at(-1)!.dispersion_base,
|
||||||
b.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base
|
|
||||||
.dispersion_base,
|
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -328,13 +308,11 @@ 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)!.gun_type!.value.base.id]
|
.guns[a.turrets.at(-1)!.guns.at(-1)!.id].pitch.max +
|
||||||
.pitch.max +
|
|
||||||
(modelDefinitions.models[a.id].initial_turret_rotation?.pitch ??
|
(modelDefinitions.models[a.id].initial_turret_rotation?.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)!.gun_type!.value.base.id]
|
.guns[b.turrets.at(-1)!.guns.at(-1)!.id].pitch.max -
|
||||||
.pitch.max -
|
|
||||||
(modelDefinitions.models[b.id].initial_turret_rotation?.pitch ??
|
(modelDefinitions.models[b.id].initial_turret_rotation?.pitch ??
|
||||||
0),
|
0),
|
||||||
);
|
);
|
||||||
@@ -344,13 +322,11 @@ 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)!.gun_type!.value.base.id]
|
.guns[b.turrets.at(-1)!.guns.at(-1)!.id].pitch.min +
|
||||||
.pitch.min +
|
|
||||||
(modelDefinitions.models[b.id].initial_turret_rotation?.pitch ??
|
(modelDefinitions.models[b.id].initial_turret_rotation?.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)!.gun_type!.value.base.id]
|
.guns[a.turrets.at(-1)!.guns.at(-1)!.id].pitch.min -
|
||||||
.pitch.min -
|
|
||||||
(modelDefinitions.models[a.id].initial_turret_rotation?.pitch ??
|
(modelDefinitions.models[a.id].initial_turret_rotation?.pitch ??
|
||||||
0),
|
0),
|
||||||
);
|
);
|
||||||
@@ -382,14 +358,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)!.gun_type!.value.base
|
a.turrets.at(-1)!.guns.at(-1)!.weight) -
|
||||||
.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)!.gun_type!.value.base.weight),
|
b.turrets.at(-1)!.guns.at(-1)!.weight),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -400,12 +375,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)!.gun_type!.value.base.weight -
|
a.turrets.at(-1)!.guns.at(-1)!.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)!.gun_type!.value.base.weight),
|
b.turrets.at(-1)!.guns.at(-1)!.weight),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
+12
-93
@@ -5,18 +5,9 @@ import {
|
|||||||
resolvePenetrationCoefficient,
|
resolvePenetrationCoefficient,
|
||||||
} from '@blitzkit/core';
|
} from '@blitzkit/core';
|
||||||
import { literals } from '@blitzkit/i18n';
|
import { literals } from '@blitzkit/i18n';
|
||||||
import {
|
import { Flex, Heading, IconButton, Tooltip } from '@radix-ui/themes';
|
||||||
Flex,
|
|
||||||
Heading,
|
|
||||||
IconButton,
|
|
||||||
Slider,
|
|
||||||
Text,
|
|
||||||
TextField,
|
|
||||||
Tooltip,
|
|
||||||
} from '@radix-ui/themes';
|
|
||||||
import { debounce } from 'lodash-es';
|
import { debounce } from 'lodash-es';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { lerp } from 'three/src/math/MathUtils.js';
|
|
||||||
import { useEquipment } from '../../../../../../hooks/useEquipment';
|
import { useEquipment } from '../../../../../../hooks/useEquipment';
|
||||||
import { useLocale } from '../../../../../../hooks/useLocale';
|
import { useLocale } from '../../../../../../hooks/useLocale';
|
||||||
import { useTankModelDefinition } from '../../../../../../hooks/useTankModelDefinition';
|
import { useTankModelDefinition } from '../../../../../../hooks/useTankModelDefinition';
|
||||||
@@ -26,6 +17,7 @@ import { AssaultRangesVisualizer } from './AssaultRangesVisualizer';
|
|||||||
import { GunFlexibilityVisualizer } from './GunFlexibilityVisualizer';
|
import { GunFlexibilityVisualizer } from './GunFlexibilityVisualizer';
|
||||||
import { Info } from './Info';
|
import { Info } from './Info';
|
||||||
import { InfoWithDelta } from './InfoWithDelta';
|
import { InfoWithDelta } from './InfoWithDelta';
|
||||||
|
import { ReloadVisualizer } from './ReloadVisualizer';
|
||||||
import { RicochetVisualizer } from './RicochetVisualizer';
|
import { RicochetVisualizer } from './RicochetVisualizer';
|
||||||
import { StatsTableWrapper } from './StatsTableWrapper';
|
import { StatsTableWrapper } from './StatsTableWrapper';
|
||||||
import type { StatsAcceptorProps } from './TraverseVisualizer';
|
import type { StatsAcceptorProps } from './TraverseVisualizer';
|
||||||
@@ -40,7 +32,9 @@ export function Firepower({
|
|||||||
const setPenetrationDistanceDebounced = debounce((value: number) => {
|
const setPenetrationDistanceDebounced = debounce((value: number) => {
|
||||||
setPenetrationDistance(value);
|
setPenetrationDistance(value);
|
||||||
}, 500);
|
}, 500);
|
||||||
const { gun, shell, turret } = Duel.use((state) => state.protagonist);
|
const turret = Duel.use((state) => state.protagonist.turret);
|
||||||
|
const gun = Duel.use((state) => state.protagonist.gun);
|
||||||
|
const shell = Duel.use((state) => state.protagonist.shell);
|
||||||
const penetrationDistanceInput = useRef<HTMLInputElement>(null);
|
const penetrationDistanceInput = useRef<HTMLInputElement>(null);
|
||||||
const hasSupercharger = useEquipment(107);
|
const hasSupercharger = useEquipment(107);
|
||||||
const hasCalibratedShells = useEquipment(103);
|
const hasCalibratedShells = useEquipment(103);
|
||||||
@@ -54,8 +48,7 @@ export function Firepower({
|
|||||||
]);
|
]);
|
||||||
const tankModelDefinition = useTankModelDefinition();
|
const tankModelDefinition = useTankModelDefinition();
|
||||||
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
||||||
const gunModelDefinition =
|
const gunModelDefinition = turretModelDefinition.guns[gun.id];
|
||||||
turretModelDefinition.guns[gun.gun_type!.value.base.id];
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (penetrationDistanceInput.current) {
|
if (penetrationDistanceInput.current) {
|
||||||
@@ -71,7 +64,7 @@ export function Firepower({
|
|||||||
</Heading>
|
</Heading>
|
||||||
|
|
||||||
<Flex>
|
<Flex>
|
||||||
{gun.gun_type!.value.base.shells.map((thisShell, shellIndex) => {
|
{gun.shells.map((thisShell, shellIndex) => {
|
||||||
const selected = thisShell.id === shell.id;
|
const selected = thisShell.id === shell.id;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -84,13 +77,9 @@ export function Firepower({
|
|||||||
borderTopLeftRadius: shellIndex === 0 ? undefined : 0,
|
borderTopLeftRadius: shellIndex === 0 ? undefined : 0,
|
||||||
borderBottomLeftRadius: shellIndex === 0 ? undefined : 0,
|
borderBottomLeftRadius: shellIndex === 0 ? undefined : 0,
|
||||||
borderTopRightRadius:
|
borderTopRightRadius:
|
||||||
shellIndex === gun.gun_type!.value.base.shells.length - 1
|
shellIndex === gun.shells.length - 1 ? undefined : 0,
|
||||||
? undefined
|
|
||||||
: 0,
|
|
||||||
borderBottomRightRadius:
|
borderBottomRightRadius:
|
||||||
shellIndex === gun.gun_type!.value.base.shells.length - 1
|
shellIndex === gun.shells.length - 1 ? undefined : 0,
|
||||||
? undefined
|
|
||||||
: 0,
|
|
||||||
marginLeft: shellIndex === 0 ? 0 : -1,
|
marginLeft: shellIndex === 0 ? 0 : -1,
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -125,11 +114,8 @@ export function Firepower({
|
|||||||
<InfoWithDelta stats={stats} decimals={0} value="moduleDamage" />
|
<InfoWithDelta stats={stats} decimals={0} value="moduleDamage" />
|
||||||
<InfoWithDelta stats={stats} decimals={0} value="penetration" />
|
<InfoWithDelta stats={stats} decimals={0} value="penetration" />
|
||||||
|
|
||||||
{gun.gun_type!.value.base.assault_ranges?.types.includes(shell.type) && (
|
{gun.assault_ranges?.types.includes(shell.type) && (
|
||||||
<AssaultRangesVisualizer
|
<AssaultRangesVisualizer stats={stats} ranges={gun.assault_ranges} />
|
||||||
stats={stats}
|
|
||||||
ranges={gun.gun_type!.value.base.assault_ranges}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{gun.gun_type!.$case === 'auto_reloader' && (
|
{gun.gun_type!.$case === 'auto_reloader' && (
|
||||||
@@ -190,75 +176,8 @@ export function Firepower({
|
|||||||
value="intraClip"
|
value="intraClip"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{/* <ReloadVisualizer stats={stats} /> */}
|
<ReloadVisualizer stats={stats} />
|
||||||
|
|
||||||
{typeof shell.penetration !== 'number' && (
|
|
||||||
<>
|
|
||||||
<InfoWithDelta
|
|
||||||
stats={stats}
|
|
||||||
indent
|
|
||||||
decimals={0}
|
|
||||||
name={literals(
|
|
||||||
strings.website.tools.tankopedia.characteristics.values
|
|
||||||
.at_distance,
|
|
||||||
[`${penetrationDistance}`],
|
|
||||||
)}
|
|
||||||
noRanking
|
|
||||||
value={() =>
|
|
||||||
lerp(
|
|
||||||
shell.penetration.near,
|
|
||||||
shell.penetration.far,
|
|
||||||
(penetrationDistance * penetrationLossOverDistanceCoefficient) /
|
|
||||||
500,
|
|
||||||
) * penetrationCoefficient
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Flex align="center" gap="2" style={{ paddingLeft: 24 }}>
|
|
||||||
<Text color="gray">
|
|
||||||
{
|
|
||||||
strings.website.tools.tankopedia.characteristics.values
|
|
||||||
.at_distance_slider_label
|
|
||||||
}
|
|
||||||
</Text>
|
|
||||||
<Slider
|
|
||||||
variant="classic"
|
|
||||||
key={penetrationDistance}
|
|
||||||
min={0}
|
|
||||||
max={500}
|
|
||||||
style={{ flex: 1 }}
|
|
||||||
defaultValue={[penetrationDistance]}
|
|
||||||
onValueChange={([value]) => {
|
|
||||||
setPenetrationDistanceDebounced(value);
|
|
||||||
|
|
||||||
if (!penetrationDistanceInput.current) return;
|
|
||||||
|
|
||||||
penetrationDistanceInput.current.value = value.toString();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<TextField.Root
|
|
||||||
variant="classic"
|
|
||||||
style={{ width: 64 }}
|
|
||||||
ref={penetrationDistanceInput}
|
|
||||||
defaultValue={penetrationDistance}
|
|
||||||
onBlur={() => {
|
|
||||||
setPenetrationDistance(
|
|
||||||
Math.min(
|
|
||||||
parseInt(penetrationDistanceInput.current!.value),
|
|
||||||
500,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
onKeyDown={(event) => {
|
|
||||||
if (event.key === 'Enter') {
|
|
||||||
event.currentTarget.blur();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TextField.Slot side="right">m</TextField.Slot>
|
|
||||||
</TextField.Root>
|
|
||||||
</Flex>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{isExplosive(shell.type) && (
|
{isExplosive(shell.type) && (
|
||||||
<InfoWithDelta
|
<InfoWithDelta
|
||||||
stats={stats}
|
stats={stats}
|
||||||
|
|||||||
+1
-1
@@ -51,7 +51,7 @@ export function ModelChunk({ only }: Props) {
|
|||||||
|
|
||||||
const tankModel = modelDefinitions.models[tank.id];
|
const tankModel = modelDefinitions.models[tank.id];
|
||||||
const turretModel = tankModel.turrets[turret.id];
|
const turretModel = tankModel.turrets[turret.id];
|
||||||
const gunModel = turretModel.guns[gun.gun_type!.value.base.id];
|
const gunModel = turretModel.guns[gun.id];
|
||||||
|
|
||||||
const originZ = tankModel.turret_origin.z + turretModel.gun_origin.z;
|
const originZ = tankModel.turret_origin.z + turretModel.gun_origin.z;
|
||||||
const originY = tankModel.turret_origin.y + turretModel.gun_origin.y;
|
const originY = tankModel.turret_origin.y + turretModel.gun_origin.y;
|
||||||
|
|||||||
+1
-1
@@ -47,7 +47,7 @@ export function GunFlexibilityVisualizer({
|
|||||||
|
|
||||||
const tankModel = modelDefinition.models[tank.id];
|
const tankModel = modelDefinition.models[tank.id];
|
||||||
const turretModel = tankModel.turrets[turret.id];
|
const turretModel = tankModel.turrets[turret.id];
|
||||||
const gunModel = turretModel.guns[gun.gun_type!.value.base.id];
|
const gunModel = turretModel.guns[gun.id];
|
||||||
|
|
||||||
const [yaw, setYaw] = useState(0);
|
const [yaw, setYaw] = useState(0);
|
||||||
const [pitch, setPitch] = useState(0);
|
const [pitch, setPitch] = useState(0);
|
||||||
|
|||||||
+3
-7
@@ -61,9 +61,7 @@ export const InfoWithDelta = memo<InfoWithDeltaProps>(
|
|||||||
const delta = useDelta(uhWhatDoICallThisVariable);
|
const delta = useDelta(uhWhatDoICallThisVariable);
|
||||||
const protagonistTank = Duel.use((state) => state.protagonist.tank);
|
const protagonistTank = Duel.use((state) => state.protagonist.tank);
|
||||||
const shellIndex = Duel.use((state) =>
|
const shellIndex = Duel.use((state) =>
|
||||||
state.protagonist.gun.gun_type!.value.base.shells.indexOf(
|
state.protagonist.gun.shells.indexOf(state.protagonist.shell),
|
||||||
state.protagonist.shell,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
const equipmentMatrix = Duel.use(
|
const equipmentMatrix = Duel.use(
|
||||||
(state) => state.protagonist.equipmentMatrix,
|
(state) => state.protagonist.equipmentMatrix,
|
||||||
@@ -89,9 +87,7 @@ export const InfoWithDelta = memo<InfoWithDeltaProps>(
|
|||||||
provisionDefinitions,
|
provisionDefinitions,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
if (member.gun.shells[shellIndex] === undefined) {
|
||||||
member.gun.gun_type!.value.base.shells[shellIndex] === undefined
|
|
||||||
) {
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +103,7 @@ export const InfoWithDelta = memo<InfoWithDeltaProps>(
|
|||||||
engine: member.engine,
|
engine: member.engine,
|
||||||
gun: member.gun,
|
gun: member.gun,
|
||||||
equipmentMatrix: equipmentMatrix,
|
equipmentMatrix: equipmentMatrix,
|
||||||
shell: member.gun.gun_type!.value.base.shells[shellIndex],
|
shell: member.gun.shells[shellIndex],
|
||||||
stockEngine: tank.engines[0],
|
stockEngine: tank.engines[0],
|
||||||
stockGun: tank.turrets[0].guns[0],
|
stockGun: tank.turrets[0].guns[0],
|
||||||
stockTrack: tank.tracks[0],
|
stockTrack: tank.tracks[0],
|
||||||
|
|||||||
+1
-3
@@ -40,7 +40,7 @@ export function Shell({ index, stats }: ShellProps & StatsAcceptorProps) {
|
|||||||
<Box
|
<Box
|
||||||
left="50%"
|
left="50%"
|
||||||
position="absolute"
|
position="absolute"
|
||||||
ml="calc(25% + 2.5rem)"
|
ml="40%"
|
||||||
width="1.25rem"
|
width="1.25rem"
|
||||||
height="1.25rem"
|
height="1.25rem"
|
||||||
style={{
|
style={{
|
||||||
@@ -70,11 +70,9 @@ export function Shell({ index, stats }: ShellProps & StatsAcceptorProps) {
|
|||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
maskImage:
|
maskImage:
|
||||||
'url(/assets/images/tankopedia/visualizers/reload/shell.png)',
|
'url(/assets/images/tankopedia/visualizers/reload/shell.png)',
|
||||||
// maskComposite: 'exclude',
|
|
||||||
maskSize: 'contain',
|
maskSize: 'contain',
|
||||||
maskRepeat: 'no-repeat',
|
maskRepeat: 'no-repeat',
|
||||||
maskPosition: 'center',
|
maskPosition: 'center',
|
||||||
// background: 'red',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
|
|||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
#reload-visualizer-flash {
|
||||||
|
background: transparent;
|
||||||
|
transition-duration: 200ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
#reload-visualizer-flash.go {
|
||||||
|
background: var(--white-a3);
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reload {
|
||||||
|
width: 65%;
|
||||||
|
height: 65%;
|
||||||
|
transition-duration: 200ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reload.go {
|
||||||
|
width: 70%;
|
||||||
|
height: 70%;
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
+121
-51
@@ -1,13 +1,15 @@
|
|||||||
import { type Datum, type Serie } from '@nivo/line';
|
import { Box, Code } from '@radix-ui/themes';
|
||||||
import { Box } from '@radix-ui/themes';
|
|
||||||
import { times } from 'lodash-es';
|
import { times } from 'lodash-es';
|
||||||
import { Quicklime, type QuicklimeCallback } from 'quicklime';
|
import { Quicklime, type QuicklimeCallback } from 'quicklime';
|
||||||
import { useEffect, useRef } from 'react';
|
import { useCallback, useEffect, useRef } from 'react';
|
||||||
import { Var } from '../../../../../../../core/radix/var';
|
import { Var } from '../../../../../../../core/radix/var';
|
||||||
import type { StatsAcceptorProps } from '../TraverseVisualizer';
|
import type { StatsAcceptorProps } from '../TraverseVisualizer';
|
||||||
import { VisualizerCard } from '../VisualizerCard';
|
import { VisualizerCard } from '../VisualizerCard';
|
||||||
import { Shell } from './components/Shell';
|
import { Shell } from './components/Shell';
|
||||||
import { Target } from './components/Target';
|
import { Target } from './components/Target';
|
||||||
|
import './index.css';
|
||||||
|
|
||||||
|
const PRECISION = 2;
|
||||||
|
|
||||||
export interface ReloadUpdateData {
|
export interface ReloadUpdateData {
|
||||||
reload: number;
|
reload: number;
|
||||||
@@ -16,29 +18,29 @@ export interface ReloadUpdateData {
|
|||||||
|
|
||||||
export const reloadUpdate = new Quicklime<ReloadUpdateData>();
|
export const reloadUpdate = new Quicklime<ReloadUpdateData>();
|
||||||
|
|
||||||
const T = 60;
|
|
||||||
|
|
||||||
export function ReloadVisualizer({ stats }: StatsAcceptorProps) {
|
export function ReloadVisualizer({ stats }: StatsAcceptorProps) {
|
||||||
const reloadCircle = useRef<HTMLDivElement>(null);
|
const reloadCircle = useRef<HTMLDivElement>(null);
|
||||||
const reloadCore = useRef<HTMLDivElement>(null);
|
const reloadCore = useRef<HTMLDivElement>(null);
|
||||||
const reloadGlow = useRef<HTMLDivElement>(null);
|
const reloadGlow = useRef<HTMLDivElement>(null);
|
||||||
|
const totalTime = useRef<HTMLSpanElement>(null);
|
||||||
|
const progressTime = useRef<HTMLSpanElement>(null);
|
||||||
|
const flash = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
/**
|
|
||||||
* 3-shell gun example:
|
|
||||||
*
|
|
||||||
* 0-1: clip reload
|
|
||||||
* ~~~: shell shot
|
|
||||||
* 1-2: intra-clip
|
|
||||||
* ~~~: shell shot
|
|
||||||
* 2-3: intra-clip
|
|
||||||
* ~~~: shell shot & reset to 0
|
|
||||||
*
|
|
||||||
* intra-clip time = intraClip * (n - 1)
|
|
||||||
*/
|
|
||||||
const state = useRef(0);
|
const state = useRef(0);
|
||||||
const dpm = useRef(0);
|
|
||||||
const shotPool = useRef(new Set<number>());
|
const shoot = useCallback(() => {
|
||||||
const data = useRef<Serie[]>([{ id: 'value', data: [] }]);
|
flash.current?.classList.add('go');
|
||||||
|
reloadCircle.current?.classList.add('go');
|
||||||
|
reloadGlow.current?.classList.add('go');
|
||||||
|
|
||||||
|
void flash.current?.offsetWidth;
|
||||||
|
void reloadCircle.current?.offsetWidth;
|
||||||
|
void reloadGlow.current?.offsetWidth;
|
||||||
|
|
||||||
|
flash.current?.classList.remove('go');
|
||||||
|
reloadCircle.current?.classList.remove('go');
|
||||||
|
reloadGlow.current?.classList.remove('go');
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let cancel = false;
|
let cancel = false;
|
||||||
@@ -46,37 +48,76 @@ export function ReloadVisualizer({ stats }: StatsAcceptorProps) {
|
|||||||
let lastState = state.current;
|
let lastState = state.current;
|
||||||
const shellArray = new Array(stats.shells);
|
const shellArray = new Array(stats.shells);
|
||||||
|
|
||||||
function shoot() {
|
|
||||||
shotPool.current.add(Date.now() / 1000);
|
|
||||||
dpm.current += stats.damage;
|
|
||||||
}
|
|
||||||
|
|
||||||
function frame() {
|
function frame() {
|
||||||
|
if (!totalTime.current || !progressTime.current) return;
|
||||||
|
|
||||||
|
const reloadThreshold = stats.shellReloads
|
||||||
|
? stats.shellReloads.length
|
||||||
|
: 1;
|
||||||
const now = Date.now() / 1000;
|
const now = Date.now() / 1000;
|
||||||
const dt = now - lastT;
|
const dt = now - lastT;
|
||||||
let reload;
|
let reload;
|
||||||
let shells;
|
let shells;
|
||||||
|
|
||||||
shotPool.current.forEach((t) => {
|
if (state.current < reloadThreshold) {
|
||||||
if (now - t > 60) shotPool.current.delete(t);
|
const shellReload =
|
||||||
});
|
reloadThreshold === 1
|
||||||
|
? stats.shellReload!
|
||||||
|
: stats.shellReloads![Math.floor(state.current)];
|
||||||
|
|
||||||
dpm.current = shotPool.current.size * stats.damage;
|
state.current = Math.min(
|
||||||
|
reloadThreshold,
|
||||||
if (state.current < 1) {
|
state.current + dt / shellReload,
|
||||||
state.current = Math.min(1, state.current + dt / stats.shellReload!);
|
);
|
||||||
reload = state.current;
|
reload = state.current % 1;
|
||||||
shells = shellArray.fill(reload);
|
shells = shellArray.fill(reload);
|
||||||
|
|
||||||
if (state.current >= 1) shoot();
|
if (reloadThreshold > 1) {
|
||||||
} else if (state.current < stats.shells) {
|
shells = shells.map((_, index) =>
|
||||||
|
Math.max(0, state.current - shellArray.length + index + 1),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
totalTime.current.innerHTML = shellReload.toFixed(PRECISION);
|
||||||
|
progressTime.current.innerHTML = ((1 - reload) * shellReload).toFixed(
|
||||||
|
PRECISION,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (state.current >= reloadThreshold) shoot();
|
||||||
|
} else if (state.current < stats.shells + reloadThreshold - 1) {
|
||||||
|
let interval;
|
||||||
|
|
||||||
|
if (
|
||||||
|
stats.burstShells === 1 ||
|
||||||
|
Math.floor(state.current) % stats.burstShells === 0
|
||||||
|
) {
|
||||||
|
interval = stats.intraClip!;
|
||||||
|
} else {
|
||||||
|
interval = stats.burstInterShell!;
|
||||||
|
}
|
||||||
|
|
||||||
state.current = Math.min(
|
state.current = Math.min(
|
||||||
stats.shells,
|
stats.shells + reloadThreshold,
|
||||||
state.current + dt / stats.intraClip!,
|
state.current + dt / interval,
|
||||||
);
|
);
|
||||||
shells = times(stats.shells, (index) => index > state.current - 1);
|
|
||||||
reload = state.current % 1;
|
reload = state.current % 1;
|
||||||
|
|
||||||
|
if (reloadThreshold > 1) {
|
||||||
|
shells = shellArray.map((_, index) =>
|
||||||
|
Math.max(
|
||||||
|
0,
|
||||||
|
Math.floor(index + 1 + reloadThreshold - state.current),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
shells = times(stats.shells, (index) => index > state.current - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
totalTime.current.innerHTML = interval.toFixed(PRECISION);
|
||||||
|
progressTime.current.innerHTML = ((1 - reload) * interval).toFixed(
|
||||||
|
PRECISION,
|
||||||
|
);
|
||||||
|
|
||||||
if (lastState % 1 > state.current % 1) shoot();
|
if (lastState % 1 > state.current % 1) shoot();
|
||||||
} else {
|
} else {
|
||||||
state.current = 0;
|
state.current = 0;
|
||||||
@@ -90,15 +131,6 @@ export function ReloadVisualizer({ stats }: StatsAcceptorProps) {
|
|||||||
|
|
||||||
lastT = now;
|
lastT = now;
|
||||||
lastState = state.current;
|
lastState = state.current;
|
||||||
|
|
||||||
(data.current[0].data as Datum[]).push({
|
|
||||||
x: now,
|
|
||||||
y: dpm.current / (stats.dpm + stats.damage),
|
|
||||||
});
|
|
||||||
(data.current[0].data as Datum[]).splice(
|
|
||||||
0,
|
|
||||||
data.current[0].data.findIndex((d) => now - (d.x as number) < T),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
frame();
|
frame();
|
||||||
@@ -151,8 +183,6 @@ export function ReloadVisualizer({ stats }: StatsAcceptorProps) {
|
|||||||
position="absolute"
|
position="absolute"
|
||||||
top="50%"
|
top="50%"
|
||||||
left="50%"
|
left="50%"
|
||||||
width="12rem"
|
|
||||||
height="12rem"
|
|
||||||
className="reload"
|
className="reload"
|
||||||
ref={reloadGlow}
|
ref={reloadGlow}
|
||||||
style={{
|
style={{
|
||||||
@@ -167,8 +197,6 @@ export function ReloadVisualizer({ stats }: StatsAcceptorProps) {
|
|||||||
position="absolute"
|
position="absolute"
|
||||||
top="50%"
|
top="50%"
|
||||||
left="50%"
|
left="50%"
|
||||||
width="12rem"
|
|
||||||
height="12rem"
|
|
||||||
className="reload"
|
className="reload"
|
||||||
ref={reloadCircle}
|
ref={reloadCircle}
|
||||||
style={{
|
style={{
|
||||||
@@ -216,10 +244,52 @@ export function ReloadVisualizer({ stats }: StatsAcceptorProps) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Code
|
||||||
|
ref={progressTime}
|
||||||
|
size="1"
|
||||||
|
variant="ghost"
|
||||||
|
weight="bold"
|
||||||
|
color="gray"
|
||||||
|
highContrast
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: '50%',
|
||||||
|
right: 'calc(50% + var(--space-8))',
|
||||||
|
transform: 'translateX(50%)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
0.00
|
||||||
|
</Code>
|
||||||
|
<Code
|
||||||
|
ref={totalTime}
|
||||||
|
size="1"
|
||||||
|
variant="ghost"
|
||||||
|
weight="bold"
|
||||||
|
color="gray"
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: '50%',
|
||||||
|
left: 'calc(50% + var(--space-8))',
|
||||||
|
transform: 'translateX(-50%)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
0.00
|
||||||
|
</Code>
|
||||||
|
|
||||||
{stats.shells > 1 &&
|
{stats.shells > 1 &&
|
||||||
times(stats.shells, (index) => (
|
times(stats.shells, (index) => (
|
||||||
<Shell stats={stats} index={index} key={index} />
|
<Shell stats={stats} index={index} key={index} />
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
<Box
|
||||||
|
id="reload-visualizer-flash"
|
||||||
|
ref={flash}
|
||||||
|
position="absolute"
|
||||||
|
top="0"
|
||||||
|
left="0"
|
||||||
|
width="100%"
|
||||||
|
height="100%"
|
||||||
|
/>
|
||||||
</VisualizerCard>
|
</VisualizerCard>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
+2
-4
@@ -11,7 +11,7 @@ export function ShellSwitcher() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex>
|
<Flex>
|
||||||
{gun.gun_type!.value.base.shells.map((thisShell, shellIndex) => {
|
{gun.shells.map((thisShell, shellIndex) => {
|
||||||
const selected = thisShell.id === shell.id;
|
const selected = thisShell.id === shell.id;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -26,9 +26,7 @@ export function ShellSwitcher() {
|
|||||||
borderBottomLeftRadius: shellIndex === 0 ? '50%' : 0,
|
borderBottomLeftRadius: shellIndex === 0 ? '50%' : 0,
|
||||||
borderTopRightRadius: 0,
|
borderTopRightRadius: 0,
|
||||||
borderBottomRightRadius:
|
borderBottomRightRadius:
|
||||||
shellIndex === gun.gun_type!.value.base.shells.length - 1
|
shellIndex === gun.shells.length - 1 ? '50%' : 0,
|
||||||
? '50%'
|
|
||||||
: 0,
|
|
||||||
marginLeft: shellIndex === 0 ? 0 : -1,
|
marginLeft: shellIndex === 0 ? 0 : -1,
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
+1
-2
@@ -41,8 +41,7 @@ export function Characteristics({ skeleton }: MaybeSkeletonComponentProps) {
|
|||||||
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 =
|
const gunModelDefinition = turretModelDefinition.guns[gun.id];
|
||||||
turretModelDefinition.guns[gun.gun_type!.value.base.id];
|
|
||||||
const stats = tankCharacteristics(
|
const stats = tankCharacteristics(
|
||||||
{
|
{
|
||||||
tank,
|
tank,
|
||||||
|
|||||||
+15
-26
@@ -33,7 +33,7 @@ type ModuleDefinition =
|
|||||||
|
|
||||||
function extractModule(raw: ModuleDefinition | TankDefinition) {
|
function extractModule(raw: ModuleDefinition | TankDefinition) {
|
||||||
if ('gun_type' in raw) {
|
if ('gun_type' in raw) {
|
||||||
return raw.gun_type!.value.base;
|
return raw;
|
||||||
} else
|
} else
|
||||||
return raw as Exclude<ModuleDefinition | TankDefinition, GunDefinition>;
|
return raw as Exclude<ModuleDefinition | TankDefinition, GunDefinition>;
|
||||||
}
|
}
|
||||||
@@ -179,37 +179,31 @@ export function Modules() {
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
!draft.protagonist.turret.guns.some(
|
!draft.protagonist.turret.guns.some(
|
||||||
(gun) =>
|
(gun) => gun.id === draft.protagonist.gun.id,
|
||||||
gun.gun_type!.value.base.id ===
|
|
||||||
draft.protagonist.gun.gun_type!.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.shell = draft.protagonist.gun.shells[0];
|
||||||
draft.protagonist.gun.gun_type!.value.base.shells[0];
|
|
||||||
}
|
}
|
||||||
} else if (unlock.type === ModuleType.GUN) {
|
} else if (unlock.type === ModuleType.GUN) {
|
||||||
const gunInTurret = draft.protagonist.turret.guns.find(
|
const gunInTurret = draft.protagonist.turret.guns.find(
|
||||||
(gun) => gun.gun_type!.value.base.id === unlock.id,
|
(gun) => gun.id === unlock.id,
|
||||||
);
|
);
|
||||||
if (gunInTurret) {
|
if (gunInTurret) {
|
||||||
draft.protagonist.gun = gunInTurret;
|
draft.protagonist.gun = gunInTurret;
|
||||||
draft.protagonist.shell = gunInTurret.gun_type!.value.base.shells[0];
|
draft.protagonist.shell = gunInTurret.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(
|
turret.guns.some((gun) => gun.id === unlock.id),
|
||||||
(gun) => gun.gun_type!.value.base.id === unlock.id,
|
|
||||||
),
|
|
||||||
)!;
|
)!;
|
||||||
const gunInSuitableTurret = suitableTurret.guns.find(
|
const gunInSuitableTurret = suitableTurret.guns.find(
|
||||||
(gun) => gun.gun_type!.value.base.id === unlock.id,
|
(gun) => gun.id === unlock.id,
|
||||||
)!;
|
)!;
|
||||||
|
|
||||||
draft.protagonist.turret = suitableTurret;
|
draft.protagonist.turret = suitableTurret;
|
||||||
draft.protagonist.gun = gunInSuitableTurret;
|
draft.protagonist.gun = gunInSuitableTurret;
|
||||||
draft.protagonist.shell =
|
draft.protagonist.shell = gunInSuitableTurret.shells[0];
|
||||||
gunInSuitableTurret.gun_type!.value.base.shells[0];
|
|
||||||
}
|
}
|
||||||
} else if (unlock.type === ModuleType.ENGINE) {
|
} else if (unlock.type === ModuleType.ENGINE) {
|
||||||
draft.protagonist.engine = draft.protagonist.tank.engines.find(
|
draft.protagonist.engine = draft.protagonist.tank.engines.find(
|
||||||
@@ -241,11 +235,9 @@ export function Modules() {
|
|||||||
} else if (unlock.type === ModuleType.GUN) {
|
} else if (unlock.type === ModuleType.GUN) {
|
||||||
module = tank.turrets
|
module = tank.turrets
|
||||||
.find((turret) =>
|
.find((turret) =>
|
||||||
turret.guns.some(
|
turret.guns.some((gun) => gun.id === unlock.id),
|
||||||
(gun) => gun.gun_type!.value.base.id === unlock.id,
|
|
||||||
),
|
|
||||||
)!
|
)!
|
||||||
.guns.find((gun) => gun.gun_type!.value.base.id === unlock.id)!;
|
.guns.find((gun) => gun.id === unlock.id)!;
|
||||||
} else if (unlock.type === ModuleType.VEHICLE) {
|
} else if (unlock.type === ModuleType.VEHICLE) {
|
||||||
module = tankDefinitions.tanks[unlock.id];
|
module = tankDefinitions.tanks[unlock.id];
|
||||||
}
|
}
|
||||||
@@ -308,7 +300,7 @@ export function Modules() {
|
|||||||
(unlock.type === ModuleType.TURRET
|
(unlock.type === ModuleType.TURRET
|
||||||
? turret.id
|
? turret.id
|
||||||
: unlock.type === ModuleType.GUN
|
: unlock.type === ModuleType.GUN
|
||||||
? gun.gun_type!.value.base.id
|
? gun.id
|
||||||
: unlock.type === ModuleType.ENGINE
|
: unlock.type === ModuleType.ENGINE
|
||||||
? engine.id
|
? engine.id
|
||||||
: unlock.type === ModuleType.TRACKS
|
: unlock.type === ModuleType.TRACKS
|
||||||
@@ -319,8 +311,7 @@ export function Modules() {
|
|||||||
unlock.type === ModuleType.TURRET
|
unlock.type === ModuleType.TURRET
|
||||||
? extractModule(module).id == topTurret.id
|
? extractModule(module).id == topTurret.id
|
||||||
: unlock.type === ModuleType.GUN
|
: unlock.type === ModuleType.GUN
|
||||||
? extractModule(module).id ===
|
? extractModule(module).id === topGun.id
|
||||||
topGun.gun_type!.value.base.id
|
|
||||||
: unlock.type === ModuleType.ENGINE
|
: unlock.type === ModuleType.ENGINE
|
||||||
? extractModule(module).id === topEngine.id
|
? extractModule(module).id === topEngine.id
|
||||||
: unlock.type === ModuleType.TRACKS
|
: unlock.type === ModuleType.TRACKS
|
||||||
@@ -371,8 +362,7 @@ 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.shell = draft.protagonist.gun.shells[0];
|
||||||
draft.protagonist.gun.gun_type!.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];
|
||||||
});
|
});
|
||||||
@@ -387,8 +377,7 @@ 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.shell = draft.protagonist.gun.shells[0];
|
||||||
draft.protagonist.gun.gun_type!.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 =
|
||||||
@@ -427,7 +416,7 @@ export function Modules() {
|
|||||||
{tree(ModuleType.GUN, [
|
{tree(ModuleType.GUN, [
|
||||||
{
|
{
|
||||||
cost: { type: 'xp', value: 0 },
|
cost: { type: 'xp', value: 0 },
|
||||||
id: gun0.gun_type!.value.base.id,
|
id: gun0.id,
|
||||||
type: ModuleType.GUN,
|
type: ModuleType.GUN,
|
||||||
},
|
},
|
||||||
])}
|
])}
|
||||||
|
|||||||
+1
-1
@@ -90,7 +90,7 @@ export function CustomShellButton() {
|
|||||||
modified.normalization = undefined;
|
modified.normalization = undefined;
|
||||||
modified.ricochet = undefined;
|
modified.ricochet = undefined;
|
||||||
modified.explosion_radius =
|
modified.explosion_radius =
|
||||||
gun.gun_type!.value.base.shells.find(
|
gun.shells.find(
|
||||||
(shell) => shell.type === ShellType.HE,
|
(shell) => shell.type === ShellType.HE,
|
||||||
)?.explosion_radius ?? 1;
|
)?.explosion_radius ?? 1;
|
||||||
break;
|
break;
|
||||||
|
|||||||
+1
-2
@@ -21,8 +21,7 @@ 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 =
|
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
|
||||||
turretModelDefinition.guns[protagonist.gun.gun_type!.value.base.id];
|
|
||||||
const initialGunPitch =
|
const initialGunPitch =
|
||||||
tankModelDefinition.initial_turret_rotation?.pitch ?? 0;
|
tankModelDefinition.initial_turret_rotation?.pitch ?? 0;
|
||||||
const hasImprovedVerticalStabilizer = useEquipment(122);
|
const hasImprovedVerticalStabilizer = useEquipment(122);
|
||||||
|
|||||||
+8
-15
@@ -141,7 +141,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
|||||||
mr="2"
|
mr="2"
|
||||||
mb="1"
|
mb="1"
|
||||||
>
|
>
|
||||||
{TIER_ROMAN_NUMERALS[antagonistGun.gun_type!.value.base.tier]}
|
{TIER_ROMAN_NUMERALS[antagonistGun.tier]}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<img
|
<img
|
||||||
@@ -161,26 +161,20 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
|||||||
{[...antagonistUniqueGuns].reverse().map(({ gun, turret }) => (
|
{[...antagonistUniqueGuns].reverse().map(({ gun, turret }) => (
|
||||||
<ModuleButton
|
<ModuleButton
|
||||||
module="gun"
|
module="gun"
|
||||||
key={gun.gun_type!.value.base.id}
|
key={gun.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 =
|
draft.antagonist.shell = gun.shells[0];
|
||||||
gun.gun_type!.value.base.shells[0];
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
selected={
|
selected={gun.id === antagonistGun.id}
|
||||||
gun.gun_type!.value.base.id ===
|
discriminator={TIER_ROMAN_NUMERALS[gun.tier]}
|
||||||
antagonistGun.gun_type!.value.base.id
|
|
||||||
}
|
|
||||||
discriminator={
|
|
||||||
TIER_ROMAN_NUMERALS[gun.gun_type!.value.base.tier]
|
|
||||||
}
|
|
||||||
secondaryDiscriminator={
|
secondaryDiscriminator={
|
||||||
<Text style={{ fontSize: '0.75em' }}>
|
<Text style={{ fontSize: '0.75em' }}>
|
||||||
{literals(strings.common.units.mm, [
|
{literals(strings.common.units.mm, [
|
||||||
gun.gun_type!.value.base.shells[0].caliber.toFixed(0),
|
gun.shells[0].caliber.toFixed(0),
|
||||||
])}
|
])}
|
||||||
</Text>
|
</Text>
|
||||||
}
|
}
|
||||||
@@ -198,7 +192,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
|||||||
}}
|
}}
|
||||||
overflow="hidden"
|
overflow="hidden"
|
||||||
>
|
>
|
||||||
{antagonistGun.gun_type!.value.base.shells.map((thisShell) => (
|
{antagonistGun.shells.map((thisShell) => (
|
||||||
<IconButton
|
<IconButton
|
||||||
color={
|
color={
|
||||||
thisShell.id === antagonistShell.id && !hasCustomShell
|
thisShell.id === antagonistShell.id && !hasCustomShell
|
||||||
@@ -367,8 +361,7 @@ export function Options({ thicknessRange, canvas, skeleton }: 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.shell = draft.antagonist.gun.shells[0];
|
||||||
draft.antagonist.gun.gun_type!.value.base.shells[0];
|
|
||||||
});
|
});
|
||||||
setAntagonistSelectorOpen(false);
|
setAntagonistSelectorOpen(false);
|
||||||
}}
|
}}
|
||||||
|
|||||||
+1
-3
@@ -56,9 +56,7 @@ export function Controls({
|
|||||||
const antagonistTurretModelDefinition =
|
const antagonistTurretModelDefinition =
|
||||||
antagonistModelDefinition.turrets[antagonistTurret.id];
|
antagonistModelDefinition.turrets[antagonistTurret.id];
|
||||||
const protagonistGunModelDefinition =
|
const protagonistGunModelDefinition =
|
||||||
protagonistTurretModelDefinition.guns[
|
protagonistTurretModelDefinition.guns[protagonistGun.id];
|
||||||
protagonistGun.gun_type!.value.base.id
|
|
||||||
];
|
|
||||||
const protagonistHullOrigin = new Vector3(
|
const protagonistHullOrigin = new Vector3(
|
||||||
protagonistTrackModelDefinition.origin.x,
|
protagonistTrackModelDefinition.origin.x,
|
||||||
protagonistTrackModelDefinition.origin.y,
|
protagonistTrackModelDefinition.origin.y,
|
||||||
|
|||||||
+1
-1
@@ -14,7 +14,7 @@ export function InitialAligner() {
|
|||||||
|
|
||||||
const tankModel = modelDefinitions.models[tank.id];
|
const tankModel = modelDefinitions.models[tank.id];
|
||||||
const turretModel = tankModel.turrets[turret.id];
|
const turretModel = tankModel.turrets[turret.id];
|
||||||
const gunModel = turretModel.guns[gun.gun_type!.value.base.id];
|
const gunModel = turretModel.guns[gun.id];
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const [pitch, yaw] = applyPitchYawLimits(
|
const [pitch, yaw] = applyPitchYawLimits(
|
||||||
|
|||||||
+1
-1
@@ -49,7 +49,7 @@ export function SceneProps() {
|
|||||||
const trackModelDefinition =
|
const trackModelDefinition =
|
||||||
modelDefinitions.models[tank.id].tracks[track.id];
|
modelDefinitions.models[tank.id].tracks[track.id];
|
||||||
const gunModelDefinition =
|
const gunModelDefinition =
|
||||||
tankModelDefinition.turrets[turret.id].guns[gun.gun_type!.value.base.id];
|
tankModelDefinition.turrets[turret.id].guns[gun.id];
|
||||||
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
||||||
const hullOrigin = new Vector3(
|
const hullOrigin = new Vector3(
|
||||||
trackModelDefinition.origin.x,
|
trackModelDefinition.origin.x,
|
||||||
|
|||||||
+1
-2
@@ -28,8 +28,7 @@ export function TankModel() {
|
|||||||
const tankModelDefinition = useTankModelDefinition();
|
const tankModelDefinition = useTankModelDefinition();
|
||||||
const turretModelDefinition =
|
const turretModelDefinition =
|
||||||
tankModelDefinition.turrets[protagonist.turret.id];
|
tankModelDefinition.turrets[protagonist.turret.id];
|
||||||
const gunModelDefinition =
|
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
|
||||||
turretModelDefinition.guns[protagonist.gun.gun_type!.value.base.id];
|
|
||||||
const { gltf } = useModel(protagonist.tank.id);
|
const { gltf } = useModel(protagonist.tank.id);
|
||||||
const nodes = Object.values(gltf.nodes);
|
const nodes = Object.values(gltf.nodes);
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -31,8 +31,7 @@ export function TransitionSkeleton() {
|
|||||||
const tankModelDefinition = useTankModelDefinition();
|
const tankModelDefinition = useTankModelDefinition();
|
||||||
const turretModelDefinition =
|
const turretModelDefinition =
|
||||||
tankModelDefinition.turrets[protagonist.turret.id];
|
tankModelDefinition.turrets[protagonist.turret.id];
|
||||||
const gunModelDefinition =
|
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
|
||||||
turretModelDefinition.guns[protagonist.gun.gun_type!.value.base.id];
|
|
||||||
const { gltf } = useModel(protagonist.tank.id);
|
const { gltf } = useModel(protagonist.tank.id);
|
||||||
const nodes = Object.values(gltf.nodes);
|
const nodes = Object.values(gltf.nodes);
|
||||||
const revealed = tankopediaEphemeralStore((state) => state.revealed);
|
const revealed = tankopediaEphemeralStore((state) => state.revealed);
|
||||||
|
|||||||
+1
-2
@@ -59,8 +59,7 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
|||||||
const tankModelDefinition = useTankModelDefinition();
|
const tankModelDefinition = useTankModelDefinition();
|
||||||
const turretModelDefinition =
|
const turretModelDefinition =
|
||||||
tankModelDefinition.turrets[protagonistTurret.id];
|
tankModelDefinition.turrets[protagonistTurret.id];
|
||||||
const gunModelDefinition =
|
const gunModelDefinition = turretModelDefinition.guns[protagonistGun.id];
|
||||||
turretModelDefinition.guns[protagonistGun.gun_type!.value.base.id];
|
|
||||||
const rawDisplay = TankopediaEphemeral.use((state) => state.display);
|
const rawDisplay = TankopediaEphemeral.use((state) => state.display);
|
||||||
const [display, setDisplay] = useState(rawDisplay);
|
const [display, setDisplay] = useState(rawDisplay);
|
||||||
const hideTankModelUnderArmor = TankopediaPersistent.use(
|
const hideTankModelUnderArmor = TankopediaPersistent.use(
|
||||||
|
|||||||
@@ -29,8 +29,7 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
|||||||
(filtered.reduce((accumulator, thisTank) => {
|
(filtered.reduce((accumulator, thisTank) => {
|
||||||
return (
|
return (
|
||||||
accumulator +
|
accumulator +
|
||||||
thisTank.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base.shells[0]
|
thisTank.turrets.at(-1)!.guns.at(-1)!.shells[0].penetration.near
|
||||||
.penetration.near
|
|
||||||
);
|
);
|
||||||
}, 0) /
|
}, 0) /
|
||||||
filtered.length) *
|
filtered.length) *
|
||||||
@@ -51,8 +50,7 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function handleKeyDown(event: KeyboardEvent) {
|
function handleKeyDown(event: KeyboardEvent) {
|
||||||
const { shells } =
|
const { shells } = duelStore.getState().antagonist.gun;
|
||||||
duelStore.getState().antagonist.gun.gun_type!.value.base;
|
|
||||||
|
|
||||||
times(3, (index) => {
|
times(3, (index) => {
|
||||||
if (event.key === `${index + 1}` && shells.length > index) {
|
if (event.key === `${index + 1}` && shells.length > index) {
|
||||||
|
|||||||
@@ -31,9 +31,8 @@ export function filterTank(filters: TankFilters, tank: TankDefinition) {
|
|||||||
SHELLS.every(
|
SHELLS.every(
|
||||||
(index) =>
|
(index) =>
|
||||||
filters.shells[index] === null ||
|
filters.shells[index] === null ||
|
||||||
gun.gun_type!.value.base.shells[index] === undefined ||
|
gun.shells[index] === undefined ||
|
||||||
gun.gun_type!.value.base.shells[index].type ===
|
gun.shells[index].type === filters.shells[index],
|
||||||
filters.shells[index],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,18 +2,16 @@ import type { GunDefinition } from '@blitzkit/core';
|
|||||||
|
|
||||||
export function resolveReload(gun: GunDefinition) {
|
export function resolveReload(gun: GunDefinition) {
|
||||||
if (gun.gun_type!.$case === 'regular') {
|
if (gun.gun_type!.$case === 'regular') {
|
||||||
return gun.gun_type!.value.extension.reload;
|
return gun.gun_type!.value.reload;
|
||||||
} else if (gun.gun_type!.$case === 'auto_loader') {
|
} else if (gun.gun_type!.$case === 'auto_loader') {
|
||||||
return (
|
return (
|
||||||
gun.gun_type!.value.extension.clip_reload +
|
gun.gun_type!.value.clip_reload +
|
||||||
(gun.gun_type!.value.extension.shell_count - 1) *
|
(gun.gun_type!.value.shell_count - 1) * gun.gun_type!.value.intra_clip
|
||||||
gun.gun_type!.value.extension.intra_clip
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
gun.gun_type!.value.extension.shell_reloads.reduce((a, b) => a + b, 0) +
|
gun.gun_type!.value.shell_reloads.reduce((a, b) => a + b, 0) +
|
||||||
(gun.gun_type!.value.extension.shell_count - 1) *
|
(gun.gun_type!.value.shell_count - 1) * gun.gun_type!.value.intra_clip
|
||||||
gun.gun_type!.value.extension.intra_clip
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,8 +80,7 @@ export function tankCharacteristics(
|
|||||||
) {
|
) {
|
||||||
const preset = equipmentDefinitions.presets[tank.equipment_preset];
|
const preset = equipmentDefinitions.presets[tank.equipment_preset];
|
||||||
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
||||||
const gunModelDefinition =
|
const gunModelDefinition = turretModelDefinition.guns[gun.id];
|
||||||
turretModelDefinition.guns[gun.gun_type!.value.base.id];
|
|
||||||
|
|
||||||
function equipment(id: number) {
|
function equipment(id: number) {
|
||||||
return preset.slots.some((slot, index) => {
|
return preset.slots.some((slot, index) => {
|
||||||
@@ -152,6 +151,8 @@ export function tankCharacteristics(
|
|||||||
commanderMastery *
|
commanderMastery *
|
||||||
(tank.crew.some(({ type }) => type === CrewType.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 burstShells = gun.burst?.count ?? 1;
|
||||||
|
const burstInterShell = gun.burst?.interval;
|
||||||
const armorDamageCoefficient =
|
const armorDamageCoefficient =
|
||||||
coefficient([hasTungsten, 0.15]) *
|
coefficient([hasTungsten, 0.15]) *
|
||||||
coefficient(
|
coefficient(
|
||||||
@@ -160,9 +161,8 @@ export function tankCharacteristics(
|
|||||||
[applySpallLiner && shell.type === ShellType.HE, -0.2],
|
[applySpallLiner && shell.type === ShellType.HE, -0.2],
|
||||||
);
|
);
|
||||||
const assaultDamageCoefficient =
|
const assaultDamageCoefficient =
|
||||||
gun.gun_type!.value.base.assault_ranges &&
|
gun.assault_ranges && gun.assault_ranges.types.includes(shell.type)
|
||||||
gun.gun_type!.value.base.assault_ranges.types.includes(shell.type)
|
? (gun.assault_ranges.ranges.find(
|
||||||
? (gun.gun_type!.value.base.assault_ranges.ranges.find(
|
|
||||||
({ distance }) => distance >= assaultDistance,
|
({ distance }) => distance >= assaultDistance,
|
||||||
)?.factor ?? 0)
|
)?.factor ?? 0)
|
||||||
: 1;
|
: 1;
|
||||||
@@ -322,18 +322,14 @@ export function tankCharacteristics(
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
const weightKg =
|
const weightKg =
|
||||||
tank.weight +
|
tank.weight + engine.weight + track.weight + turret.weight + gun.weight;
|
||||||
engine.weight +
|
|
||||||
track.weight +
|
|
||||||
turret.weight +
|
|
||||||
gun.gun_type!.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.gun_type!.value.base.weight;
|
stockGun.weight;
|
||||||
const resolvedEnginePower = engine.power * enginePowerCoefficient;
|
const resolvedEnginePower = engine.power * enginePowerCoefficient;
|
||||||
const damageCoefficientWithoutAssault = armorDamageCoefficient;
|
const damageCoefficientWithoutAssault = armorDamageCoefficient;
|
||||||
const damageCoefficient =
|
const damageCoefficient =
|
||||||
@@ -348,10 +344,10 @@ export function tankCharacteristics(
|
|||||||
const intraClip =
|
const intraClip =
|
||||||
gun.gun_type?.$case === 'regular'
|
gun.gun_type?.$case === 'regular'
|
||||||
? undefined
|
? undefined
|
||||||
: gun.gun_type!.value.extension.intra_clip * intraClipCoefficient;
|
: gun.gun_type!.value.intra_clip * intraClipCoefficient;
|
||||||
const mostOptimalShellIndex =
|
const mostOptimalShellIndex =
|
||||||
gun.gun_type!.$case === 'auto_reloader'
|
gun.gun_type!.$case === 'auto_reloader'
|
||||||
? gun.gun_type!.value.extension.shell_reloads.reduce<null | {
|
? gun.gun_type!.value.shell_reloads.reduce<null | {
|
||||||
index: number;
|
index: number;
|
||||||
reload: number;
|
reload: number;
|
||||||
}>((current, reloadRaw, index) => {
|
}>((current, reloadRaw, index) => {
|
||||||
@@ -369,43 +365,34 @@ export function tankCharacteristics(
|
|||||||
const damage = shell.armor_damage * damageCoefficient;
|
const damage = shell.armor_damage * damageCoefficient;
|
||||||
const dpmEffective =
|
const dpmEffective =
|
||||||
gun.gun_type!.$case === 'auto_reloader'
|
gun.gun_type!.$case === 'auto_reloader'
|
||||||
? gun.gun_type!.value.extension.shell_reloads[0] >
|
? gun.gun_type!.value.shell_reloads[0] >
|
||||||
gun.gun_type!.value.extension.shell_reloads[1] +
|
gun.gun_type!.value.shell_reloads[1] + gun.gun_type!.value.intra_clip
|
||||||
gun.gun_type!.value.extension.intra_clip
|
|
||||||
? ((armorDamageCoefficient * shell.armor_damage) /
|
? ((armorDamageCoefficient * shell.armor_damage) /
|
||||||
(gun.gun_type!.value.extension.shell_reloads.at(-1)! *
|
(gun.gun_type!.value.shell_reloads.at(-1)! * reloadCoefficient +
|
||||||
reloadCoefficient +
|
gun.gun_type!.value.intra_clip * intraClipCoefficient)) *
|
||||||
gun.gun_type!.value.extension.intra_clip *
|
|
||||||
intraClipCoefficient)) *
|
|
||||||
(60 -
|
(60 -
|
||||||
(gun.gun_type!.value.extension.shell_reloads.slice(0, -1).length -
|
(gun.gun_type!.value.shell_reloads.slice(0, -1).length - 1) *
|
||||||
1) *
|
gun.gun_type!.value.intra_clip *
|
||||||
gun.gun_type!.value.extension.intra_clip *
|
|
||||||
intraClipCoefficient) +
|
intraClipCoefficient) +
|
||||||
armorDamageCoefficient *
|
armorDamageCoefficient *
|
||||||
shell.armor_damage *
|
shell.armor_damage *
|
||||||
gun.gun_type!.value.extension.shell_reloads.slice(0, -1).length
|
gun.gun_type!.value.shell_reloads.slice(0, -1).length
|
||||||
: ((armorDamageCoefficient * shell.armor_damage) /
|
: ((armorDamageCoefficient * shell.armor_damage) /
|
||||||
(gun.gun_type!.value.extension.shell_reloads[0] *
|
(gun.gun_type!.value.shell_reloads[0] * reloadCoefficient +
|
||||||
reloadCoefficient +
|
gun.gun_type!.value.intra_clip * intraClipCoefficient)) *
|
||||||
gun.gun_type!.value.extension.intra_clip *
|
|
||||||
intraClipCoefficient)) *
|
|
||||||
(60 -
|
(60 -
|
||||||
(gun.gun_type!.value.extension.shell_reloads.slice(1).length -
|
(gun.gun_type!.value.shell_reloads.slice(1).length - 1) *
|
||||||
1) *
|
gun.gun_type!.value.intra_clip *
|
||||||
gun.gun_type!.value.extension.intra_clip *
|
|
||||||
intraClipCoefficient) +
|
intraClipCoefficient) +
|
||||||
armorDamageCoefficient *
|
armorDamageCoefficient *
|
||||||
shell.armor_damage *
|
shell.armor_damage *
|
||||||
gun.gun_type!.value.extension.shell_reloads.slice(1).length
|
gun.gun_type!.value.shell_reloads.slice(1).length
|
||||||
: undefined;
|
: undefined;
|
||||||
const shells =
|
const shells =
|
||||||
gun.gun_type!.$case === 'regular'
|
gun.gun_type!.$case === 'regular' ? 1 : gun.gun_type!.value.shell_count;
|
||||||
? 1
|
|
||||||
: gun.gun_type!.value.extension.shell_count;
|
|
||||||
const shellReloads =
|
const shellReloads =
|
||||||
gun.gun_type!.$case === 'auto_reloader'
|
gun.gun_type!.$case === 'auto_reloader'
|
||||||
? gun.gun_type!.value.extension.shell_reloads.map(
|
? gun.gun_type!.value.shell_reloads.map(
|
||||||
(reload) => reload * reloadCoefficient,
|
(reload) => reload * reloadCoefficient,
|
||||||
)
|
)
|
||||||
: undefined;
|
: undefined;
|
||||||
@@ -413,30 +400,28 @@ export function tankCharacteristics(
|
|||||||
gun.gun_type!.$case === 'auto_reloader'
|
gun.gun_type!.$case === 'auto_reloader'
|
||||||
? undefined
|
? undefined
|
||||||
: (gun.gun_type!.$case === 'regular'
|
: (gun.gun_type!.$case === 'regular'
|
||||||
? gun.gun_type!.value.extension.reload
|
? gun.gun_type!.value.reload
|
||||||
: gun.gun_type!.value.extension.clip_reload) * reloadCoefficient;
|
: gun.gun_type!.value.clip_reload) * reloadCoefficient;
|
||||||
const caliber = shell.caliber;
|
const caliber = shell.caliber;
|
||||||
const penetration = shell.penetration.near * penetrationCoefficient;
|
const penetration = shell.penetration.near * penetrationCoefficient;
|
||||||
const clipDamage =
|
const clipDamage =
|
||||||
gun.gun_type!.$case === 'regular'
|
gun.gun_type!.$case === 'regular'
|
||||||
? undefined
|
? undefined
|
||||||
: gun.gun_type!.value.extension.shell_count * damage;
|
: gun.gun_type!.value.shell_count * damage;
|
||||||
const moduleDamage = shell.module_damage * moduleDamageCoefficient;
|
const moduleDamage = shell.module_damage * moduleDamageCoefficient;
|
||||||
const explosionRadius = shell.explosion_radius;
|
const explosionRadius = shell.explosion_radius;
|
||||||
const shellVelocity = shell.velocity * shellVelocityCoefficient;
|
const shellVelocity = shell.velocity * shellVelocityCoefficient;
|
||||||
const aimTime = gun.gun_type!.value.base.aim_time * aimTimeCoefficient;
|
const aimTime = gun.aim_time * aimTimeCoefficient;
|
||||||
const dispersion =
|
const dispersion = gun.dispersion_base * dispersionStillCoefficient;
|
||||||
gun.gun_type!.value.base.dispersion_base * dispersionStillCoefficient;
|
|
||||||
const dispersionMoving = track.dispersion_move * dispersionMovingCoefficient;
|
const dispersionMoving = track.dispersion_move * dispersionMovingCoefficient;
|
||||||
const dispersionHullTraversing =
|
const dispersionHullTraversing =
|
||||||
track.dispersion_traverse * dispersionHullTraverseCoefficient;
|
track.dispersion_traverse * dispersionHullTraverseCoefficient;
|
||||||
const dispersionTurretTraversing =
|
const dispersionTurretTraversing =
|
||||||
gun.gun_type!.value.base.dispersion_traverse *
|
gun.dispersion_traverse * dispersionTurretTraverseCoefficient;
|
||||||
dispersionTurretTraverseCoefficient;
|
|
||||||
const dispersionShooting =
|
const dispersionShooting =
|
||||||
gun.gun_type!.value.base.dispersion_shot * dispersionAfterShotCoefficient;
|
gun.dispersion_shot * dispersionAfterShotCoefficient;
|
||||||
const dispersionGunDamaged =
|
const dispersionGunDamaged =
|
||||||
gun.gun_type!.value.base.dispersion_damaged * dispersionDamagedCoefficient;
|
gun.dispersion_damaged * dispersionDamagedCoefficient;
|
||||||
const gunDepression =
|
const gunDepression =
|
||||||
gunModelDefinition.pitch.max +
|
gunModelDefinition.pitch.max +
|
||||||
gunDefaultPitch +
|
gunDefaultPitch +
|
||||||
@@ -478,8 +463,7 @@ export function tankCharacteristics(
|
|||||||
resolvedEnginePower / weightTons / softTerrainCoefficientRaw;
|
resolvedEnginePower / weightTons / softTerrainCoefficientRaw;
|
||||||
const weight = weightTons;
|
const weight = weightTons;
|
||||||
const turretTraverseSpeed = turret.traverse_speed * turretTraverseCoefficient;
|
const turretTraverseSpeed = turret.traverse_speed * turretTraverseCoefficient;
|
||||||
const gunTraverseSpeed =
|
const gunTraverseSpeed = gun.rotation_speed * turretTraverseCoefficient;
|
||||||
gun.gun_type!.value.base.rotation_speed * turretTraverseCoefficient;
|
|
||||||
/**
|
/**
|
||||||
* resistance_hard is used in all cases as this is the coefficient between
|
* resistance_hard is used in all cases as this is the coefficient between
|
||||||
* the absolute best coefficient and the current
|
* the absolute best coefficient and the current
|
||||||
@@ -511,14 +495,12 @@ export function tankCharacteristics(
|
|||||||
? tank.camouflage_still * camouflageCoefficientStill
|
? tank.camouflage_still * camouflageCoefficientStill
|
||||||
: tank.camouflage_moving * camouflageCoefficientMoving;
|
: tank.camouflage_moving * camouflageCoefficientMoving;
|
||||||
const camouflageShootingStill =
|
const camouflageShootingStill =
|
||||||
tank.camouflage_still *
|
tank.camouflage_still * gun.camouflage_loss * camouflageCoefficientStill;
|
||||||
gun.gun_type!.value.base.camouflage_loss *
|
|
||||||
camouflageCoefficientStill;
|
|
||||||
const camouflageShootingMoving =
|
const camouflageShootingMoving =
|
||||||
(tank.class === TankClass.LIGHT
|
(tank.class === TankClass.LIGHT
|
||||||
? tank.camouflage_still * camouflageCoefficientStill
|
? tank.camouflage_still * camouflageCoefficientStill
|
||||||
: tank.camouflage_moving * camouflageCoefficientMoving) *
|
: tank.camouflage_moving * camouflageCoefficientMoving) *
|
||||||
gun.gun_type!.value.base.camouflage_loss;
|
gun.camouflage_loss;
|
||||||
const camouflageCaughtOnFire =
|
const camouflageCaughtOnFire =
|
||||||
tank.camouflage_onFire * tank.camouflage_still * camouflageCoefficientStill;
|
tank.camouflage_onFire * tank.camouflage_still * camouflageCoefficientStill;
|
||||||
const width = size.z;
|
const width = size.z;
|
||||||
@@ -529,7 +511,7 @@ export function tankCharacteristics(
|
|||||||
const shellRicochet = isExplosive(shell.type) ? undefined : shell.ricochet;
|
const shellRicochet = isExplosive(shell.type) ? undefined : shell.ricochet;
|
||||||
const crewCount = tank.crew.length;
|
const crewCount = tank.crew.length;
|
||||||
const shellRange = shell.range;
|
const shellRange = shell.range;
|
||||||
const shellCapacity = gun.gun_type!.value.base.shell_capacity;
|
const shellCapacity = gun.shell_capacity;
|
||||||
const gunType = gun.gun_type!.$case;
|
const gunType = gun.gun_type!.$case;
|
||||||
const penetrationAt250m = shell.penetration.far;
|
const penetrationAt250m = shell.penetration.far;
|
||||||
|
|
||||||
@@ -601,6 +583,8 @@ export function tankCharacteristics(
|
|||||||
shellCapacity,
|
shellCapacity,
|
||||||
gunType,
|
gunType,
|
||||||
penetrationAt250m,
|
penetrationAt250m,
|
||||||
|
burstShells,
|
||||||
|
burstInterShell,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,11 +23,10 @@ export function tankToDuelMember(
|
|||||||
provisions: createDefaultProvisions(tank, gun, provisionDefinitions),
|
provisions: createDefaultProvisions(tank, gun, provisionDefinitions),
|
||||||
engine: tank.engines.at(-1)!,
|
engine: tank.engines.at(-1)!,
|
||||||
gun,
|
gun,
|
||||||
shell: tank.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base.shells[0],
|
shell: tank.turrets.at(-1)!.guns.at(-1)!.shells[0],
|
||||||
tank,
|
tank,
|
||||||
track: tank.tracks.at(-1)!,
|
track: tank.tracks.at(-1)!,
|
||||||
turret,
|
turret,
|
||||||
assaultDistance:
|
assaultDistance: (gun.assault_ranges?.ranges[0].distance ?? 0) / 2,
|
||||||
(gun.gun_type!.value.base.assault_ranges?.ranges[0].distance ?? 0) / 2,
|
|
||||||
} satisfies DuelMember;
|
} satisfies DuelMember;
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,7 @@ import Navbar from '../components/Navbar.astro';
|
|||||||
import Party3 from '../components/Party3.astro';
|
import Party3 from '../components/Party3.astro';
|
||||||
import './base.css';
|
import './base.css';
|
||||||
import { DEFAULT_LOCALE } from '@blitzkit/i18n';
|
import { DEFAULT_LOCALE } from '@blitzkit/i18n';
|
||||||
|
import Servicing from '../components/Servicing.astro';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
noFooter?: boolean;
|
noFooter?: boolean;
|
||||||
@@ -15,6 +16,7 @@ export interface Props {
|
|||||||
integrated?: boolean;
|
integrated?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const servicing = import.meta.env.SERVICING;
|
||||||
const { noFooter, noNavbar, integrated } = Astro.props;
|
const { noFooter, noNavbar, integrated } = Astro.props;
|
||||||
const locale = Astro.params.locale ?? DEFAULT_LOCALE;
|
const locale = Astro.params.locale ?? DEFAULT_LOCALE;
|
||||||
---
|
---
|
||||||
@@ -65,7 +67,7 @@ const locale = Astro.params.locale ?? DEFAULT_LOCALE;
|
|||||||
|
|
||||||
<Checks {locale} client:only="react" />
|
<Checks {locale} client:only="react" />
|
||||||
|
|
||||||
<slot />
|
{servicing ? <Servicing /> : <slot />}
|
||||||
|
|
||||||
{!noFooter && <Footer />}
|
{!noFooter && <Footer />}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ const url = import.meta.env.SITE + Astro.url.pathname;
|
|||||||
const icon = asset(`icons/tanks/big/${id}.webp`);
|
const icon = asset(`icons/tanks/big/${id}.webp`);
|
||||||
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 shell = gun.gun_type!.value.base.shells[0];
|
const shell = gun.shells[0];
|
||||||
const characteristics = tankCharacteristics(
|
const characteristics = tankCharacteristics(
|
||||||
{
|
{
|
||||||
tank,
|
tank,
|
||||||
|
|||||||
Reference in new issue
Block a user