mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 20:19:49 +02:00
feat: generalized tank stats
This commit is contained in:
1 parent
913411275b
commit
bd412cdaa2
11 files changed
+633
-331
No files matched your search
@@ -14,25 +14,17 @@ import { lerp } from 'three/src/math/MathUtils';
|
|||||||
import { ShellButton } from '../../../../../../components/ModuleButtons/ShellButton';
|
import { ShellButton } from '../../../../../../components/ModuleButtons/ShellButton';
|
||||||
import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawLimits';
|
import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawLimits';
|
||||||
import { isExplosive } from '../../../../../../core/blitz/isExplosive';
|
import { isExplosive } from '../../../../../../core/blitz/isExplosive';
|
||||||
import { resolveNearPenetration } from '../../../../../../core/blitz/resolveNearPenetration';
|
|
||||||
import { coefficient } from '../../../../../../core/blitzkrieg/coefficient';
|
import { coefficient } from '../../../../../../core/blitzkrieg/coefficient';
|
||||||
import { degressiveStat } from '../../../../../../core/blitzkrieg/degressiveStat';
|
|
||||||
import { modelDefinitions } from '../../../../../../core/blitzkrieg/modelDefinitions';
|
import { modelDefinitions } from '../../../../../../core/blitzkrieg/modelDefinitions';
|
||||||
import { normalizeBoundingBox } from '../../../../../../core/blitzkrieg/normalizeBoundingBox';
|
import { provisionDefinitions } from '../../../../../../core/blitzkrieg/provisionDefinitions';
|
||||||
import { progressiveStat } from '../../../../../../core/blitzkrieg/progressiveStat';
|
|
||||||
import { resolveDpm } from '../../../../../../core/blitzkrieg/resolveDpm';
|
|
||||||
import { sum } from '../../../../../../core/blitzkrieg/sum';
|
|
||||||
import {
|
import {
|
||||||
CREW_MEMBER_NAMES,
|
CREW_MEMBER_NAMES,
|
||||||
GUN_TYPE_NAMES,
|
GUN_TYPE_NAMES,
|
||||||
SHELL_NAMES,
|
SHELL_NAMES,
|
||||||
} from '../../../../../../core/blitzkrieg/tankDefinitions/constants';
|
} from '../../../../../../core/blitzkrieg/tankDefinitions/constants';
|
||||||
import { unionBoundingBox } from '../../../../../../core/blitzkrieg/unionBoundingBox';
|
|
||||||
import { useConsumable } from '../../../../../../hooks/useConsumable';
|
|
||||||
import { useEquipment } from '../../../../../../hooks/useEquipment';
|
import { useEquipment } from '../../../../../../hooks/useEquipment';
|
||||||
import { useFullScreen } from '../../../../../../hooks/useFullScreen';
|
import { useFullScreen } from '../../../../../../hooks/useFullScreen';
|
||||||
import { useProvision } from '../../../../../../hooks/useProvision';
|
import { useTankCharacteristics } from '../../../../../../hooks/useTankCharacteristics';
|
||||||
import { useProvisions } from '../../../../../../hooks/useProvisions';
|
|
||||||
import { mutateDuel, useDuel } from '../../../../../../stores/duel';
|
import { mutateDuel, useDuel } from '../../../../../../stores/duel';
|
||||||
import { useTankopediaTemporary } from '../../../../../../stores/tankopedia';
|
import { useTankopediaTemporary } from '../../../../../../stores/tankopedia';
|
||||||
import { Info } from './components/Info';
|
import { Info } from './components/Info';
|
||||||
@@ -40,22 +32,31 @@ import { InfoWithDelta } from './components/InfoWithDelta';
|
|||||||
|
|
||||||
export function Characteristics() {
|
export function Characteristics() {
|
||||||
const awaitedModelDefinitions = use(modelDefinitions);
|
const awaitedModelDefinitions = use(modelDefinitions);
|
||||||
|
const awaitedProvisionDefinitions = use(provisionDefinitions);
|
||||||
const crewSkills = useTankopediaTemporary((state) => state.skills);
|
const crewSkills = useTankopediaTemporary((state) => state.skills);
|
||||||
const { tank, turret, gun, engine, track, shell } = useDuel(
|
const penetrationDistanceInput = useRef<HTMLInputElement>(null);
|
||||||
(state) => state.protagonist!,
|
|
||||||
);
|
|
||||||
const isFullScreen = useFullScreen();
|
const isFullScreen = useFullScreen();
|
||||||
const hasImprovedVentilation = useEquipment(102);
|
const hasImprovedVentilation = useEquipment(102);
|
||||||
const provisions = useProvisions();
|
|
||||||
const crewMastery = useDuel((state) => state.protagonist!.crewMastery);
|
const crewMastery = useDuel((state) => state.protagonist!.crewMastery);
|
||||||
|
const [penetrationDistance, setPenetrationDistance] = useState(250);
|
||||||
|
|
||||||
|
const provisions = useDuel((state) => state.protagonist!.provisions);
|
||||||
const provisionCrewBonus =
|
const provisionCrewBonus =
|
||||||
provisions.reduce(
|
provisions.reduce(
|
||||||
(total, provision) =>
|
(total, provision) =>
|
||||||
provision.crew ? total + provision.crew / 100 : total,
|
awaitedProvisionDefinitions[provision].crew
|
||||||
|
? total + awaitedProvisionDefinitions[provision].crew! / 100
|
||||||
|
: total,
|
||||||
0,
|
0,
|
||||||
) + (hasImprovedVentilation ? 0.05 : 0);
|
) + (hasImprovedVentilation ? 0.05 : 0);
|
||||||
const commanderMastery = crewMastery + provisionCrewBonus;
|
const commanderMastery = crewMastery + provisionCrewBonus;
|
||||||
const crewMemberMastery = commanderMastery * 1.1;
|
const crewMemberMastery = commanderMastery * 1.1;
|
||||||
|
const consumables = useDuel((state) => state.protagonist!.consumables);
|
||||||
|
const camouflage = useDuel((state) => state.protagonist!.camouflage);
|
||||||
|
const equipmentMatrix = useDuel((state) => state.protagonist!.equipment);
|
||||||
|
const { tank, turret, gun, engine, track, shell } = useDuel(
|
||||||
|
(state) => state.protagonist!,
|
||||||
|
);
|
||||||
const stockEngine = tank.engines[0];
|
const stockEngine = tank.engines[0];
|
||||||
const stockTrack = tank.tracks[0];
|
const stockTrack = tank.tracks[0];
|
||||||
const stockTurret = tank.turrets[0];
|
const stockTurret = tank.turrets[0];
|
||||||
@@ -63,187 +64,36 @@ export function Characteristics() {
|
|||||||
const tankModelDefinition = awaitedModelDefinitions[tank.id];
|
const tankModelDefinition = awaitedModelDefinitions[tank.id];
|
||||||
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
||||||
const gunModelDefinition = turretModelDefinition.guns[gun.id];
|
const gunModelDefinition = turretModelDefinition.guns[gun.id];
|
||||||
const size = normalizeBoundingBox(
|
const stats = useTankCharacteristics({
|
||||||
unionBoundingBox(
|
tank,
|
||||||
tankModelDefinition.boundingBox,
|
camouflage,
|
||||||
turretModelDefinition.boundingBox,
|
consumables,
|
||||||
),
|
crewMastery,
|
||||||
);
|
crewSkills,
|
||||||
const gunDefaultPitch = tankModelDefinition.turretRotation?.pitch ?? 0;
|
engine,
|
||||||
const weight =
|
equipmentMatrix,
|
||||||
tank.weight + engine.weight + track.weight + turret.weight + gun.weight;
|
gun,
|
||||||
const weightTons = weight / 1000;
|
provisions,
|
||||||
const stockWeight =
|
shell,
|
||||||
tank.weight +
|
turret,
|
||||||
stockEngine.weight +
|
track,
|
||||||
stockTrack.weight +
|
stockEngine,
|
||||||
stockTurret.weight +
|
stockGun,
|
||||||
stockGun.weight;
|
stockTrack,
|
||||||
const [penetrationDistance, setPenetrationDistance] = useState(250);
|
stockTurret,
|
||||||
const camouflage = useDuel((state) => state.protagonist!.camouflage);
|
});
|
||||||
const hasRammer = useEquipment(100);
|
|
||||||
const hasCalibratedShells = useEquipment(103);
|
|
||||||
const hasEnhancedGunLayingDrive = useEquipment(104);
|
|
||||||
const hasSupercharger = useEquipment(107);
|
|
||||||
const hasVerticalStabilizer = useEquipment(105);
|
|
||||||
const hasRefinedGun = useEquipment(106);
|
|
||||||
const hasImprovedAssembly = useEquipment(111);
|
|
||||||
const hasImprovedOptics = useEquipment(114);
|
|
||||||
const hasImprovedControl = useEquipment(116);
|
|
||||||
const hasEngineAccelerator = useEquipment(117);
|
|
||||||
const hasCamouflageNet = useEquipment(115);
|
|
||||||
const hasImprovedVerticalStabilizer = useEquipment(122);
|
|
||||||
const hasImprovedSuspension = useEquipment(123);
|
|
||||||
const penetrationDistanceInput = useRef<HTMLInputElement>(null);
|
|
||||||
const hasEnginePowerBoost = useConsumable(14);
|
|
||||||
const hasImprovedEnginePowerBoost = useConsumable(27);
|
|
||||||
const hasAdrenaline = useConsumable(18);
|
|
||||||
const hasTungsten = useConsumable(45);
|
|
||||||
const hasReticleCalibration = useConsumable(28);
|
|
||||||
const hasShellReloadBoost = useConsumable(29);
|
|
||||||
const hasStandardFuel = useProvision(18);
|
|
||||||
const hasImprovedFuel = useProvision(19);
|
|
||||||
const hasProtectiveKit = useProvision(22);
|
|
||||||
const hasSandbagArmor = useProvision(28);
|
|
||||||
const hasEnhancedSandbagArmor = useProvision(29);
|
|
||||||
const hasImprovedGunPowder = useProvision(46);
|
|
||||||
const hasGearOil = useProvision(44);
|
|
||||||
const hasImprovedGearOil = useProvision(45);
|
|
||||||
|
|
||||||
const healthCoefficient = coefficient(
|
const hasSupercharger = useEquipment(107);
|
||||||
[hasSandbagArmor, 0.03],
|
const hasCalibratedShells = useEquipment(103);
|
||||||
[hasEnhancedSandbagArmor, 0.06],
|
const hasImprovedVerticalStabilizer = useEquipment(122);
|
||||||
[hasImprovedAssembly, 0.06],
|
|
||||||
);
|
|
||||||
const reloadCoefficient =
|
|
||||||
(coefficient([hasRammer, -0.07]) *
|
|
||||||
coefficient([true, degressiveStat(crewMemberMastery)])) /
|
|
||||||
coefficient([hasAdrenaline && gun.type === 'regular', 0.2]);
|
|
||||||
const penetrationCoefficient = coefficient([
|
const penetrationCoefficient = coefficient([
|
||||||
hasCalibratedShells,
|
hasCalibratedShells,
|
||||||
isExplosive(shell.type) ? 0.1 : 0.05,
|
isExplosive(shell.type) ? 0.1 : 0.05,
|
||||||
]);
|
]);
|
||||||
const damageCoefficient = coefficient(
|
|
||||||
// TODO: add max rolls
|
|
||||||
[hasTungsten, 0.15],
|
|
||||||
);
|
|
||||||
const intraClipCoefficient = coefficient([hasShellReloadBoost, -0.3]);
|
|
||||||
const shellVelocityCoefficient = coefficient(
|
|
||||||
[hasSupercharger, 0.3],
|
|
||||||
[hasImprovedGunPowder, 0.3],
|
|
||||||
);
|
|
||||||
const penetrationLossOverDistanceCoefficient = coefficient([
|
const penetrationLossOverDistanceCoefficient = coefficient([
|
||||||
hasSupercharger,
|
hasSupercharger,
|
||||||
-0.5,
|
-0.5,
|
||||||
]);
|
]);
|
||||||
const aimTimeCoefficient =
|
|
||||||
coefficient(
|
|
||||||
[hasEnhancedGunLayingDrive, -0.1],
|
|
||||||
[hasReticleCalibration, -0.4],
|
|
||||||
) * coefficient([true, degressiveStat(crewMemberMastery)]);
|
|
||||||
const dispersionStillCoefficient =
|
|
||||||
coefficient([hasRefinedGun, -0.1], [hasReticleCalibration, -0.4]) *
|
|
||||||
coefficient([true, degressiveStat(crewMemberMastery)]);
|
|
||||||
const dispersionDamagedCoefficient = coefficient([
|
|
||||||
hasVerticalStabilizer,
|
|
||||||
-0.15,
|
|
||||||
]);
|
|
||||||
const dispersionHullTraverseCoefficient = coefficient(
|
|
||||||
[hasVerticalStabilizer, -0.15],
|
|
||||||
[true, -crewSkills.smooth_turn / 100],
|
|
||||||
);
|
|
||||||
const dispersionAfterShotCoefficient = coefficient(
|
|
||||||
[hasVerticalStabilizer, -0.15],
|
|
||||||
[true, -crewSkills.soft_recoil / 100],
|
|
||||||
);
|
|
||||||
const dispersionMovingCoefficient = coefficient(
|
|
||||||
[hasVerticalStabilizer, -0.15],
|
|
||||||
[true, -crewSkills.smooth_driving / 100],
|
|
||||||
);
|
|
||||||
const dispersionTurretTraverseCoefficient = coefficient(
|
|
||||||
[hasVerticalStabilizer, -0.15],
|
|
||||||
[true, -crewSkills.smooth_turret * (2 / 100)],
|
|
||||||
);
|
|
||||||
const enginePowerCoefficient = coefficient(
|
|
||||||
[
|
|
||||||
hasEngineAccelerator &&
|
|
||||||
(tank.class === 'lightTank' || tank.class === 'mediumTank'),
|
|
||||||
0.05,
|
|
||||||
],
|
|
||||||
[
|
|
||||||
hasEngineAccelerator &&
|
|
||||||
(tank.class === 'heavyTank' || tank.class === 'AT-SPG'),
|
|
||||||
0.07,
|
|
||||||
],
|
|
||||||
[hasEnginePowerBoost, 0.2],
|
|
||||||
[hasImprovedEnginePowerBoost, 0.4],
|
|
||||||
[hasStandardFuel, 0.03],
|
|
||||||
[hasImprovedFuel, 0.1],
|
|
||||||
[hasGearOil, 0.03],
|
|
||||||
[hasImprovedGearOil, 0.06],
|
|
||||||
);
|
|
||||||
const turretTraverseCoefficient =
|
|
||||||
coefficient([hasStandardFuel, 0.03], [hasImprovedFuel, 0.1]) *
|
|
||||||
coefficient([true, progressiveStat(crewMemberMastery)]);
|
|
||||||
const hullTraverseCoefficient =
|
|
||||||
coefficient(
|
|
||||||
[hasImprovedControl, 0.1],
|
|
||||||
[hasImprovedEnginePowerBoost, 0.05],
|
|
||||||
[true, crewSkills.virtuoso / 100],
|
|
||||||
) * coefficient([true, progressiveStat(crewMemberMastery)]);
|
|
||||||
const resistanceCoefficient = coefficient([hasImprovedSuspension, -0.25]);
|
|
||||||
const viewRangeCoefficient =
|
|
||||||
coefficient([
|
|
||||||
hasImprovedOptics,
|
|
||||||
tank.class === 'AT-SPG' ? 0.05 : tank.class === 'heavyTank' ? 0.07 : 0.1,
|
|
||||||
]) * coefficient([true, progressiveStat(commanderMastery)]);
|
|
||||||
const fireChanceCoefficient = coefficient([hasProtectiveKit, -0.2]);
|
|
||||||
|
|
||||||
const speedForwardsSum = sum(
|
|
||||||
[hasImprovedEnginePowerBoost, 5],
|
|
||||||
[hasGearOil, 2],
|
|
||||||
[hasImprovedGearOil, 4],
|
|
||||||
);
|
|
||||||
const speedBackwardsSum = sum(
|
|
||||||
[hasImprovedEnginePowerBoost, 10],
|
|
||||||
[hasGearOil, 2],
|
|
||||||
[hasImprovedGearOil, 4],
|
|
||||||
);
|
|
||||||
const camouflageSumMoving = sum(
|
|
||||||
[
|
|
||||||
hasCamouflageNet,
|
|
||||||
tank.class === 'heavyTank' ? 0.03 : tank.class === 'AT-SPG' ? 0.07 : 0.05,
|
|
||||||
],
|
|
||||||
[
|
|
||||||
camouflage,
|
|
||||||
tank.class === 'AT-SPG' ? 0.04 : tank.class === 'heavyTank' ? 0.03 : 0.02,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
const camouflageSumStill = sum(
|
|
||||||
[
|
|
||||||
hasCamouflageNet,
|
|
||||||
2 *
|
|
||||||
(tank.class === 'heavyTank'
|
|
||||||
? 0.03
|
|
||||||
: tank.class === 'AT-SPG'
|
|
||||||
? 0.07
|
|
||||||
: 0.05),
|
|
||||||
],
|
|
||||||
[
|
|
||||||
camouflage,
|
|
||||||
tank.class === 'AT-SPG' ? 0.04 : tank.class === 'heavyTank' ? 0.03 : 0.02,
|
|
||||||
],
|
|
||||||
[true, crewSkills.camouflage * (3 / 100)],
|
|
||||||
);
|
|
||||||
|
|
||||||
const resolvedEnginePower = engine.power * enginePowerCoefficient;
|
|
||||||
const dpm = resolveDpm(
|
|
||||||
gun,
|
|
||||||
shell,
|
|
||||||
damageCoefficient,
|
|
||||||
reloadCoefficient,
|
|
||||||
intraClipCoefficient,
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (penetrationDistanceInput.current) {
|
if (penetrationDistanceInput.current) {
|
||||||
@@ -294,19 +144,12 @@ export function Characteristics() {
|
|||||||
|
|
||||||
<Info name="Gun type">{GUN_TYPE_NAMES[gun.type]}</Info>
|
<Info name="Gun type">{GUN_TYPE_NAMES[gun.type]}</Info>
|
||||||
<InfoWithDelta name="DPM" decimals={0} unit="hp / min">
|
<InfoWithDelta name="DPM" decimals={0} unit="hp / min">
|
||||||
{dpm}
|
{stats.dpm}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
{gun.type === 'autoReloader' && (
|
{gun.type === 'autoReloader' && (
|
||||||
<>
|
<>
|
||||||
<InfoWithDelta decimals={0} indent name="Maximum" unit="hp / min">
|
<InfoWithDelta decimals={0} indent name="Maximum" unit="hp / min">
|
||||||
{gun.reload.at(-1)! < gun.reload.at(-2)!
|
{stats.dpmMaximum!}
|
||||||
? ((damageCoefficient * shell.damage.armor) /
|
|
||||||
(gun.reload.at(-1)! * reloadCoefficient +
|
|
||||||
gun.intraClip * intraClipCoefficient)) *
|
|
||||||
60
|
|
||||||
: ((damageCoefficient * shell.damage.armor) /
|
|
||||||
(gun.reload[0] * reloadCoefficient)) *
|
|
||||||
60}
|
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<InfoWithDelta
|
<InfoWithDelta
|
||||||
decimals={0}
|
decimals={0}
|
||||||
@@ -314,27 +157,7 @@ export function Characteristics() {
|
|||||||
name="Effective at 60s"
|
name="Effective at 60s"
|
||||||
unit="hp / min"
|
unit="hp / min"
|
||||||
>
|
>
|
||||||
{gun.reload.at(-1)! < gun.reload.at(-2)!
|
{stats.dpmEffective!}
|
||||||
? ((damageCoefficient * shell.damage.armor) /
|
|
||||||
(gun.reload.at(-1)! * reloadCoefficient +
|
|
||||||
gun.intraClip * intraClipCoefficient)) *
|
|
||||||
(60 -
|
|
||||||
(gun.reload.slice(0, -1).length - 1) *
|
|
||||||
gun.intraClip *
|
|
||||||
intraClipCoefficient) +
|
|
||||||
damageCoefficient *
|
|
||||||
shell.damage.armor *
|
|
||||||
gun.reload.slice(0, -1).length
|
|
||||||
: ((damageCoefficient * shell.damage.armor) /
|
|
||||||
(gun.reload[0] * reloadCoefficient +
|
|
||||||
gun.intraClip * intraClipCoefficient)) *
|
|
||||||
(60 -
|
|
||||||
(gun.reload.slice(1).length - 1) *
|
|
||||||
gun.intraClip *
|
|
||||||
intraClipCoefficient) +
|
|
||||||
damageCoefficient *
|
|
||||||
shell.damage.armor *
|
|
||||||
gun.reload.slice(1).length}
|
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<Info
|
<Info
|
||||||
indent
|
indent
|
||||||
@@ -349,14 +172,17 @@ export function Characteristics() {
|
|||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{gun.type !== 'regular' && (
|
||||||
|
<InfoWithDelta name="Shells">{stats.shells}</InfoWithDelta>
|
||||||
|
)}
|
||||||
|
|
||||||
{gun.type === 'autoReloader' ? (
|
{gun.type === 'autoReloader' ? (
|
||||||
<>
|
<>
|
||||||
<InfoWithDelta name="Shells">{gun.reload.length}</InfoWithDelta>
|
|
||||||
<Info indent name="Most optimal">
|
<Info indent name="Most optimal">
|
||||||
{gun.reload.at(-1)! < gun.reload.at(-2)! ? gun.reload.length : 1}
|
{stats.mostOptimalShellIndex}
|
||||||
</Info>
|
</Info>
|
||||||
<Info name="Shell reloads" unit="s" />
|
<Info name="Shell reloads" unit="s" />
|
||||||
{gun.reload.map((reload, index) => (
|
{stats.shellReloads!.map((reload, index) => (
|
||||||
<InfoWithDelta
|
<InfoWithDelta
|
||||||
key={index}
|
key={index}
|
||||||
indent
|
indent
|
||||||
@@ -364,7 +190,7 @@ export function Characteristics() {
|
|||||||
decimals={2}
|
decimals={2}
|
||||||
deltaType="lowerIsBetter"
|
deltaType="lowerIsBetter"
|
||||||
>
|
>
|
||||||
{reload * reloadCoefficient}
|
{reload}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
@@ -375,7 +201,7 @@ export function Characteristics() {
|
|||||||
unit="s"
|
unit="s"
|
||||||
deltaType="lowerIsBetter"
|
deltaType="lowerIsBetter"
|
||||||
>
|
>
|
||||||
{gun.reload * reloadCoefficient}
|
{stats.shellReload!}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
)}
|
)}
|
||||||
{gun.type === 'autoLoader' ||
|
{gun.type === 'autoLoader' ||
|
||||||
@@ -387,14 +213,14 @@ export function Characteristics() {
|
|||||||
unit="s"
|
unit="s"
|
||||||
deltaType="lowerIsBetter"
|
deltaType="lowerIsBetter"
|
||||||
>
|
>
|
||||||
{gun.intraClip * intraClipCoefficient}
|
{stats.intraClip!}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
))}
|
))}
|
||||||
<InfoWithDelta name="Caliber" decimals={0} unit="mm">
|
<InfoWithDelta name="Caliber" decimals={0} unit="mm">
|
||||||
{shell.caliber}
|
{stats.caliber}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<InfoWithDelta decimals={0} name="Penetration" unit="mm">
|
<InfoWithDelta decimals={0} name="Penetration" unit="mm">
|
||||||
{resolveNearPenetration(shell.penetration) * penetrationCoefficient}
|
{stats.penetration}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
{typeof shell.penetration !== 'number' && (
|
{typeof shell.penetration !== 'number' && (
|
||||||
<>
|
<>
|
||||||
@@ -446,18 +272,18 @@ export function Characteristics() {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<InfoWithDelta name="Damage" unit="hp" decimals={0}>
|
<InfoWithDelta name="Damage" unit="hp" decimals={0}>
|
||||||
{shell.damage.armor * damageCoefficient}
|
{stats.damage}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<InfoWithDelta name="Module damage" unit="hp" decimals={0}>
|
<InfoWithDelta name="Module damage" unit="hp" decimals={0}>
|
||||||
{shell.damage.module * damageCoefficient}
|
{stats.moduleDamage}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
{isExplosive(shell.type) && (
|
{isExplosive(shell.type) && (
|
||||||
<InfoWithDelta name="Splash radius" unit="m" decimals={0}>
|
<InfoWithDelta name="Splash radius" unit="m" decimals={0}>
|
||||||
{shell.explosionRadius!}
|
{stats.explosionRadius!}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
)}
|
)}
|
||||||
<InfoWithDelta name="Shell velocity" unit="m/s" decimals={0}>
|
<InfoWithDelta name="Shell velocity" unit="m/s" decimals={0}>
|
||||||
{shell.speed * shellVelocityCoefficient}
|
{stats.shellVelocity}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<InfoWithDelta
|
<InfoWithDelta
|
||||||
decimals={2}
|
decimals={2}
|
||||||
@@ -465,7 +291,7 @@ export function Characteristics() {
|
|||||||
unit="s"
|
unit="s"
|
||||||
deltaType="lowerIsBetter"
|
deltaType="lowerIsBetter"
|
||||||
>
|
>
|
||||||
{gun.aimTime * aimTimeCoefficient}
|
{stats.aimTime}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<Info name="Dispersion at 100m" />
|
<Info name="Dispersion at 100m" />
|
||||||
<InfoWithDelta
|
<InfoWithDelta
|
||||||
@@ -475,7 +301,7 @@ export function Characteristics() {
|
|||||||
unit="m"
|
unit="m"
|
||||||
deltaType="lowerIsBetter"
|
deltaType="lowerIsBetter"
|
||||||
>
|
>
|
||||||
{gun.dispersion.base * dispersionStillCoefficient}
|
{stats.dispersion}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<InfoWithDelta
|
<InfoWithDelta
|
||||||
prefix="+ "
|
prefix="+ "
|
||||||
@@ -484,7 +310,7 @@ export function Characteristics() {
|
|||||||
name="Moving"
|
name="Moving"
|
||||||
deltaType="lowerIsBetter"
|
deltaType="lowerIsBetter"
|
||||||
>
|
>
|
||||||
{track.dispersion.move * dispersionMovingCoefficient}
|
{stats.dispersionMoving}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<InfoWithDelta
|
<InfoWithDelta
|
||||||
decimals={3}
|
decimals={3}
|
||||||
@@ -493,7 +319,7 @@ export function Characteristics() {
|
|||||||
name="Hull traversing"
|
name="Hull traversing"
|
||||||
deltaType="lowerIsBetter"
|
deltaType="lowerIsBetter"
|
||||||
>
|
>
|
||||||
{track.dispersion.traverse * dispersionHullTraverseCoefficient}
|
{stats.dispersionHullTraversing}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<InfoWithDelta
|
<InfoWithDelta
|
||||||
decimals={3}
|
decimals={3}
|
||||||
@@ -502,7 +328,7 @@ export function Characteristics() {
|
|||||||
name="Turret traversing"
|
name="Turret traversing"
|
||||||
deltaType="lowerIsBetter"
|
deltaType="lowerIsBetter"
|
||||||
>
|
>
|
||||||
{gun.dispersion.traverse * dispersionTurretTraverseCoefficient}
|
{stats.dispersionTurretTraversing}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<InfoWithDelta
|
<InfoWithDelta
|
||||||
decimals={3}
|
decimals={3}
|
||||||
@@ -511,7 +337,7 @@ export function Characteristics() {
|
|||||||
name="After shooting"
|
name="After shooting"
|
||||||
deltaType="lowerIsBetter"
|
deltaType="lowerIsBetter"
|
||||||
>
|
>
|
||||||
{gun.dispersion.shot * dispersionAfterShotCoefficient}
|
{stats.dispersionShooting}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<InfoWithDelta
|
<InfoWithDelta
|
||||||
decimals={1}
|
decimals={1}
|
||||||
@@ -521,46 +347,42 @@ export function Characteristics() {
|
|||||||
unit="scalar"
|
unit="scalar"
|
||||||
deltaType="lowerIsBetter"
|
deltaType="lowerIsBetter"
|
||||||
>
|
>
|
||||||
{gun.dispersion.damaged * dispersionDamagedCoefficient}
|
{stats.dispersionGunDamaged}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<Info name="Gun flexibility" unit="°" />
|
<Info name="Gun flexibility" unit="°" />
|
||||||
<InfoWithDelta decimals={1} indent name="Depression">
|
<InfoWithDelta decimals={1} indent name="Depression">
|
||||||
{gunModelDefinition.pitch.max +
|
{stats.gunDepression}
|
||||||
gunDefaultPitch +
|
|
||||||
(hasImprovedVerticalStabilizer ? 3 : 0)}
|
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<InfoWithDelta decimals={1} indent name="Elevation">
|
<InfoWithDelta decimals={1} indent name="Elevation">
|
||||||
{-gunModelDefinition.pitch.min -
|
{stats.gunElevation}
|
||||||
gunDefaultPitch +
|
|
||||||
(hasImprovedVerticalStabilizer ? 3 : 0)}
|
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
{gunModelDefinition.pitch.front && (
|
{gunModelDefinition.pitch.front && (
|
||||||
<>
|
<>
|
||||||
<InfoWithDelta decimals={1} indent name="Frontal depression">
|
<InfoWithDelta decimals={1} indent name="Frontal depression">
|
||||||
{gunModelDefinition.pitch.front.max}
|
{stats.gunFrontalDepression!}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<InfoWithDelta decimals={1} indent name="Frontal elevation">
|
<InfoWithDelta decimals={1} indent name="Frontal elevation">
|
||||||
{-gunModelDefinition.pitch.front.min}
|
{stats.gunFrontalElevation!}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{gunModelDefinition.pitch.back && (
|
{gunModelDefinition.pitch.back && (
|
||||||
<>
|
<>
|
||||||
<InfoWithDelta decimals={1} indent name="Rear depression">
|
<InfoWithDelta decimals={1} indent name="Rear depression">
|
||||||
{gunModelDefinition.pitch.back.max}
|
{stats.gunRearDepression!}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<InfoWithDelta decimals={1} indent name="Rear elevation">
|
<InfoWithDelta decimals={1} indent name="Rear elevation">
|
||||||
{-gunModelDefinition.pitch.back.min}
|
{stats.gunRearElevation!}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{turretModelDefinition.yaw && (
|
{turretModelDefinition.yaw && (
|
||||||
<>
|
<>
|
||||||
<InfoWithDelta decimals={1} indent name="Azimuth left">
|
<InfoWithDelta decimals={1} indent name="Azimuth left">
|
||||||
{-turretModelDefinition.yaw.min}
|
{stats.azimuthLeft!}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<InfoWithDelta decimals={1} indent name="Azimuth right">
|
<InfoWithDelta decimals={1} indent name="Azimuth right">
|
||||||
{turretModelDefinition.yaw.max}
|
{stats.azimuthRight!}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@@ -570,29 +392,23 @@ export function Characteristics() {
|
|||||||
<Heading size="5">Maneuverability</Heading>
|
<Heading size="5">Maneuverability</Heading>
|
||||||
<Info name="Speed" unit="km/hr" />
|
<Info name="Speed" unit="km/hr" />
|
||||||
<InfoWithDelta decimals={0} indent name="Forwards">
|
<InfoWithDelta decimals={0} indent name="Forwards">
|
||||||
{tank.speed.forwards + speedForwardsSum}
|
{stats.speedForwards}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<InfoWithDelta decimals={0} indent name="Backwards">
|
<InfoWithDelta decimals={0} indent name="Backwards">
|
||||||
{tank.speed.backwards + speedBackwardsSum}
|
{stats.speedBackwards}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<InfoWithDelta decimals={0} name="Power" unit="hp">
|
<InfoWithDelta decimals={0} name="Power" unit="hp">
|
||||||
{resolvedEnginePower}
|
{stats.enginePower}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<Info name="Power to weight ratio" unit="hp/tn" />
|
<Info name="Power to weight ratio" unit="hp/tn" />
|
||||||
<InfoWithDelta decimals={1} indent name="On hard terrain">
|
<InfoWithDelta decimals={1} indent name="On hard terrain">
|
||||||
{resolvedEnginePower /
|
{stats.powerToWeightRatioHardTerrain}
|
||||||
weightTons /
|
|
||||||
(track.resistance.hard * resistanceCoefficient)}
|
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<InfoWithDelta decimals={1} indent name="On medium terrain">
|
<InfoWithDelta decimals={1} indent name="On medium terrain">
|
||||||
{resolvedEnginePower /
|
{stats.powerToWeightRatioMediumTerrain}
|
||||||
weightTons /
|
|
||||||
(track.resistance.medium * resistanceCoefficient)}
|
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<InfoWithDelta decimals={1} indent name="On soft terrain">
|
<InfoWithDelta decimals={1} indent name="On soft terrain">
|
||||||
{resolvedEnginePower /
|
{stats.powerToWeightRatioSoftTerrain}
|
||||||
weightTons /
|
|
||||||
(track.resistance.soft * resistanceCoefficient)}
|
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<InfoWithDelta
|
<InfoWithDelta
|
||||||
decimals={1}
|
decimals={1}
|
||||||
@@ -600,71 +416,57 @@ export function Characteristics() {
|
|||||||
unit="tn"
|
unit="tn"
|
||||||
deltaType="lowerIsBetter"
|
deltaType="lowerIsBetter"
|
||||||
>
|
>
|
||||||
{weightTons}
|
{stats.weight}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<InfoWithDelta name="Turret traverse speed" unit="°/s" decimals={0}>
|
<InfoWithDelta name="Turret traverse speed" unit="°/s" decimals={0}>
|
||||||
{turret.traverseSpeed * turretTraverseCoefficient}
|
{stats.turretTraverseSpeed}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<Info name="Effective traverse speed" unit="°/s" />
|
<Info name="Effective traverse speed" unit="°/s" />
|
||||||
<InfoWithDelta decimals={1} indent name="On hard terrain">
|
<InfoWithDelta decimals={1} indent name="On hard terrain">
|
||||||
{(resolvedEnginePower / stockEngine.power) *
|
{stats.hullTraverseHardTerrain}
|
||||||
track.traverseSpeed *
|
|
||||||
hullTraverseCoefficient *
|
|
||||||
(track.resistance.hard /
|
|
||||||
(track.resistance.hard * resistanceCoefficient)) *
|
|
||||||
(stockWeight / weight)}
|
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<InfoWithDelta decimals={1} indent name="On medium terrain">
|
<InfoWithDelta decimals={1} indent name="On medium terrain">
|
||||||
{(resolvedEnginePower / stockEngine.power) *
|
{stats.hullTraverseMediumTerrain}
|
||||||
track.traverseSpeed *
|
|
||||||
hullTraverseCoefficient *
|
|
||||||
(track.resistance.hard /
|
|
||||||
(track.resistance.medium * resistanceCoefficient)) *
|
|
||||||
(stockWeight / weight)}
|
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<InfoWithDelta decimals={1} indent name="On soft terrain">
|
<InfoWithDelta decimals={1} indent name="On soft terrain">
|
||||||
{(resolvedEnginePower / stockEngine.power) *
|
{stats.hullTraverseSoftTerrain}
|
||||||
track.traverseSpeed *
|
|
||||||
hullTraverseCoefficient *
|
|
||||||
(track.resistance.hard /
|
|
||||||
(track.resistance.soft * resistanceCoefficient)) *
|
|
||||||
(stockWeight / weight)}
|
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Flex direction="column" gap="2">
|
<Flex direction="column" gap="2">
|
||||||
<Heading size="5">Survivability</Heading>
|
<Heading size="5">Survivability</Heading>
|
||||||
<InfoWithDelta name="Health" unit="hp" decimals={0}>
|
<InfoWithDelta name="Health" unit="hp" decimals={0}>
|
||||||
{(tank.health + turret.health) * healthCoefficient}
|
{stats.health}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<InfoWithDelta name="Fire chance" unit="%" deltaType="lowerIsBetter">
|
<InfoWithDelta
|
||||||
{Math.round(engine.fireChance * fireChanceCoefficient * 100)}
|
name="Fire chance"
|
||||||
|
unit="%"
|
||||||
|
deltaType="lowerIsBetter"
|
||||||
|
decimals={0}
|
||||||
|
>
|
||||||
|
{stats.fireChance * 100}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<InfoWithDelta name="View range" unit="m" decimals={0}>
|
<InfoWithDelta name="View range" unit="m" decimals={0}>
|
||||||
{turret.viewRange * viewRangeCoefficient}
|
{stats.viewRange}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<Info name="Camouflage" unit="%" />
|
<Info name="Camouflage" unit="%" />
|
||||||
<InfoWithDelta indent name="Still" decimals={2}>
|
<InfoWithDelta indent name="Still" decimals={2}>
|
||||||
{(tank.camouflage.still + camouflageSumStill) * 100}
|
{stats.camouflageStill * 100}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<InfoWithDelta indent name="Moving" decimals={2}>
|
<InfoWithDelta indent name="Moving" decimals={2}>
|
||||||
{(tank.camouflage.moving + camouflageSumMoving) * 100}
|
{stats.camouflageMoving * 100}
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<InfoWithDelta indent name="Shooting still" decimals={2}>
|
<InfoWithDelta indent name="Shooting still" decimals={2}>
|
||||||
{(tank.camouflage.still * gun.camouflageLoss + camouflageSumStill) *
|
{stats.camouflageShootingStill * 100}
|
||||||
100}
|
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<InfoWithDelta indent name="Shooting on move" decimals={2}>
|
<InfoWithDelta indent name="Shooting on move" decimals={2}>
|
||||||
{(tank.camouflage.moving * gun.camouflageLoss + camouflageSumMoving) *
|
{stats.camouflageShootingMoving * 100}
|
||||||
100}
|
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<InfoWithDelta indent name="Caught on fire" decimals={2}>
|
<InfoWithDelta indent name="Caught on fire" decimals={2}>
|
||||||
{(tank.camouflage.onFire * tank.camouflage.still +
|
{stats.camouflageCaughtOnFire * 100}
|
||||||
camouflageSumStill) *
|
|
||||||
100}
|
|
||||||
</InfoWithDelta>
|
</InfoWithDelta>
|
||||||
<Info name="Size" unit="m">
|
<Info name="Size" unit="m">
|
||||||
{size[0].toFixed(2)} x {size[2].toFixed(2)} x {size[1].toFixed(2)}
|
{stats.tankSize.map((component) => component.toFixed(2)).join(' x ')}
|
||||||
</Info>
|
</Info>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,12 @@ export function Controls() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
async function handlePoseEvent(event: Pose) {
|
async function handlePoseEvent(event: Pose) {
|
||||||
const hasImprovedVerticalStabilizer = await hasEquipment(122);
|
const duel = useDuel.getState();
|
||||||
|
const hasImprovedVerticalStabilizer = await hasEquipment(
|
||||||
|
122,
|
||||||
|
duel.protagonist!.tank.equipment,
|
||||||
|
duel.protagonist!.equipment,
|
||||||
|
);
|
||||||
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case Pose.HullDown: {
|
case Pose.HullDown: {
|
||||||
|
|||||||
@@ -162,7 +162,12 @@ export const TankModel = memo(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function handlePointerMove(event: PointerEvent) {
|
async function handlePointerMove(event: PointerEvent) {
|
||||||
const hasImprovedVerticalStabilizer = await hasEquipment(122);
|
const duel = useDuel.getState();
|
||||||
|
const hasImprovedVerticalStabilizer = await hasEquipment(
|
||||||
|
122,
|
||||||
|
duel.protagonist!.tank.equipment,
|
||||||
|
duel.protagonist!.equipment,
|
||||||
|
);
|
||||||
[pitch, yaw] = applyPitchYawLimits(
|
[pitch, yaw] = applyPitchYawLimits(
|
||||||
pitch,
|
pitch,
|
||||||
yaw + event.movementX * (Math.PI / canvas.width),
|
yaw + event.movementX * (Math.PI / canvas.width),
|
||||||
@@ -223,7 +228,12 @@ export const TankModel = memo(() => {
|
|||||||
window.addEventListener('pointerup', handlePointerUp);
|
window.addEventListener('pointerup', handlePointerUp);
|
||||||
}
|
}
|
||||||
async function handlePointerMove(event: PointerEvent) {
|
async function handlePointerMove(event: PointerEvent) {
|
||||||
const hasImprovedVerticalStabilizer = await hasEquipment(122);
|
const duel = useDuel.getState();
|
||||||
|
const hasImprovedVerticalStabilizer = await hasEquipment(
|
||||||
|
122,
|
||||||
|
duel.protagonist!.tank.equipment,
|
||||||
|
duel.protagonist!.equipment,
|
||||||
|
);
|
||||||
[pitch, yaw] = applyPitchYawLimits(
|
[pitch, yaw] = applyPitchYawLimits(
|
||||||
pitch - event.movementY * (Math.PI / canvas.height),
|
pitch - event.movementY * (Math.PI / canvas.height),
|
||||||
yaw + event.movementX * (Math.PI / canvas.width),
|
yaw + event.movementX * (Math.PI / canvas.width),
|
||||||
|
|||||||
@@ -84,18 +84,29 @@ export function CoreArmorSceneComponent({
|
|||||||
async function handleProtagonistEquipmentChange(
|
async function handleProtagonistEquipmentChange(
|
||||||
equipment: EquipmentMatrix,
|
equipment: EquipmentMatrix,
|
||||||
) {
|
) {
|
||||||
const hasEnhancedArmor = await hasEquipment(110, false, equipment);
|
const duel = useDuel.getState();
|
||||||
|
const hasEnhancedArmor = await hasEquipment(
|
||||||
|
110,
|
||||||
|
duel.protagonist!.tank.equipment,
|
||||||
|
equipment,
|
||||||
|
);
|
||||||
material.uniforms.thickness.value = hasEnhancedArmor
|
material.uniforms.thickness.value = hasEnhancedArmor
|
||||||
? thickness * 1.04
|
? thickness * 1.04
|
||||||
: thickness;
|
: thickness;
|
||||||
}
|
}
|
||||||
async function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) {
|
async function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) {
|
||||||
const shell = useDuel.getState().antagonist!.shell;
|
const duel = useDuel.getState();
|
||||||
const penetration = resolveNearPenetration(shell.penetration);
|
const penetration = resolveNearPenetration(
|
||||||
const hasCalibratedShells = await hasEquipment(103, true, equipment);
|
duel.antagonist!.shell.penetration,
|
||||||
|
);
|
||||||
|
const hasCalibratedShells = await hasEquipment(
|
||||||
|
103,
|
||||||
|
duel.antagonist!.tank.equipment,
|
||||||
|
equipment,
|
||||||
|
);
|
||||||
|
|
||||||
material.uniforms.penetration.value = hasCalibratedShells
|
material.uniforms.penetration.value = hasCalibratedShells
|
||||||
? penetration * (isExplosive(shell.type) ? 1.1 : 1.05)
|
? penetration * (isExplosive(duel.antagonist!.shell.type) ? 1.1 : 1.05)
|
||||||
: penetration;
|
: penetration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+16
-5
@@ -50,17 +50,28 @@ export function SpacedArmorSubExternal({
|
|||||||
async function handleProtagonistEquipmentChange(
|
async function handleProtagonistEquipmentChange(
|
||||||
equipment: EquipmentMatrix,
|
equipment: EquipmentMatrix,
|
||||||
) {
|
) {
|
||||||
const hasEnhancedArmor = await hasEquipment(110, false, equipment);
|
const duel = useDuel.getState();
|
||||||
|
const hasEnhancedArmor = await hasEquipment(
|
||||||
|
110,
|
||||||
|
duel.protagonist!.tank.equipment,
|
||||||
|
equipment,
|
||||||
|
);
|
||||||
material.uniforms.thickness.value = hasEnhancedArmor
|
material.uniforms.thickness.value = hasEnhancedArmor
|
||||||
? thickness * 1.04
|
? thickness * 1.04
|
||||||
: thickness;
|
: thickness;
|
||||||
}
|
}
|
||||||
async function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) {
|
async function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) {
|
||||||
const shell = useDuel.getState().antagonist!.shell;
|
const duel = useDuel.getState();
|
||||||
const penetration = resolveNearPenetration(shell.penetration);
|
const penetration = resolveNearPenetration(
|
||||||
const hasCalibratedShells = await hasEquipment(103, true, equipment);
|
duel.antagonist!.shell.penetration,
|
||||||
|
);
|
||||||
|
const hasCalibratedShells = await hasEquipment(
|
||||||
|
103,
|
||||||
|
duel.antagonist!.tank.equipment,
|
||||||
|
equipment,
|
||||||
|
);
|
||||||
material.uniforms.penetration.value = hasCalibratedShells
|
material.uniforms.penetration.value = hasCalibratedShells
|
||||||
? penetration * (isExplosive(shell.type) ? 1.1 : 1.05)
|
? penetration * (isExplosive(duel.antagonist!.shell.type) ? 1.1 : 1.05)
|
||||||
: penetration;
|
: penetration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-2
@@ -62,15 +62,25 @@ export function SpacedArmorSubSpaced({
|
|||||||
async function handleProtagonistEquipmentChange(
|
async function handleProtagonistEquipmentChange(
|
||||||
equipment: EquipmentMatrix,
|
equipment: EquipmentMatrix,
|
||||||
) {
|
) {
|
||||||
const hasEnhancedArmor = await hasEquipment(110, false, equipment);
|
const duel = useDuel.getState();
|
||||||
|
const hasEnhancedArmor = await hasEquipment(
|
||||||
|
110,
|
||||||
|
duel.protagonist!.tank.equipment,
|
||||||
|
equipment,
|
||||||
|
);
|
||||||
material.uniforms.thickness.value = hasEnhancedArmor
|
material.uniforms.thickness.value = hasEnhancedArmor
|
||||||
? thickness * 1.04
|
? thickness * 1.04
|
||||||
: thickness;
|
: thickness;
|
||||||
}
|
}
|
||||||
async function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) {
|
async function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) {
|
||||||
|
const duel = useDuel.getState();
|
||||||
const shell = useDuel.getState().antagonist!.shell;
|
const shell = useDuel.getState().antagonist!.shell;
|
||||||
const penetration = resolveNearPenetration(shell.penetration);
|
const penetration = resolveNearPenetration(shell.penetration);
|
||||||
const hasCalibratedShells = await hasEquipment(103, true, equipment);
|
const hasCalibratedShells = await hasEquipment(
|
||||||
|
103,
|
||||||
|
duel.antagonist!.tank.equipment,
|
||||||
|
equipment,
|
||||||
|
);
|
||||||
material.uniforms.penetration.value = hasCalibratedShells
|
material.uniforms.penetration.value = hasCalibratedShells
|
||||||
? penetration * (isExplosive(shell.type) ? 1.1 : 1.05)
|
? penetration * (isExplosive(shell.type) ? 1.1 : 1.05)
|
||||||
: penetration;
|
: penetration;
|
||||||
|
|||||||
@@ -89,8 +89,17 @@ export function SpacedArmorSceneComponent({
|
|||||||
async onClick(event) {
|
async onClick(event) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
const hasCalibratedShells = await hasEquipment(103, true);
|
const duel = useDuel.getState();
|
||||||
const hasEnhancedArmor = await hasEquipment(110);
|
const hasCalibratedShells = await hasEquipment(
|
||||||
|
103,
|
||||||
|
duel.antagonist!.tank.equipment,
|
||||||
|
duel.antagonist!.equipment,
|
||||||
|
);
|
||||||
|
const hasEnhancedArmor = await hasEquipment(
|
||||||
|
110,
|
||||||
|
duel.protagonist!.tank.equipment,
|
||||||
|
duel.protagonist!.equipment,
|
||||||
|
);
|
||||||
const thicknessCoefficient = hasEnhancedArmor ? 1.04 : 1;
|
const thicknessCoefficient = hasEnhancedArmor ? 1.04 : 1;
|
||||||
const shell = useDuel.getState().antagonist!.shell;
|
const shell = useDuel.getState().antagonist!.shell;
|
||||||
const explosive = isExplosive(shell.type);
|
const explosive = isExplosive(shell.type);
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
import { useDuel } from '../../stores/duel';
|
import { EquipmentMatrix } from '../../stores/duel';
|
||||||
import { equipmentDefinitions } from './equipmentDefinitions';
|
import { equipmentDefinitions } from './equipmentDefinitions';
|
||||||
|
|
||||||
export async function hasEquipment(
|
export async function hasEquipment(
|
||||||
id: number,
|
id: number,
|
||||||
antagonist = false,
|
equipmentPreset: string,
|
||||||
equipmentMatrix = useDuel.getState()[
|
equipmentMatrix: EquipmentMatrix,
|
||||||
antagonist ? 'antagonist' : 'protagonist'
|
|
||||||
]!.equipment,
|
|
||||||
) {
|
) {
|
||||||
const awaitedEquipmentDefinitions = await equipmentDefinitions;
|
const awaitedEquipmentDefinitions = await equipmentDefinitions;
|
||||||
const member = useDuel.getState()[antagonist ? 'antagonist' : 'protagonist']!;
|
const presetRows = awaitedEquipmentDefinitions.presets[equipmentPreset];
|
||||||
const presetRows = awaitedEquipmentDefinitions.presets[member.tank.equipment];
|
|
||||||
return presetRows.some((equipmentRow, rowIndex) => {
|
return presetRows.some((equipmentRow, rowIndex) => {
|
||||||
return equipmentRow.some((equipment, columnIndex) => {
|
return equipmentRow.some((equipment, columnIndex) => {
|
||||||
const choice = equipmentMatrix[rowIndex][columnIndex];
|
const choice = equipmentMatrix[rowIndex][columnIndex];
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
import { useDuel } from '../stores/duel';
|
export function useConsumable(id: number, consumables: number[]) {
|
||||||
|
|
||||||
export function useConsumable(id: number) {
|
|
||||||
const consumables = useDuel((state) => state.protagonist!.consumables);
|
|
||||||
return consumables.includes(id);
|
return consumables.includes(id);
|
||||||
}
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import { use } from 'react';
|
|
||||||
import { provisionDefinitions } from '../core/blitzkrieg/provisionDefinitions';
|
|
||||||
import { useDuel } from '../stores/duel';
|
|
||||||
|
|
||||||
export function useProvisions() {
|
|
||||||
const provisions = useDuel((state) => state.protagonist!.provisions);
|
|
||||||
const awaitedProvisionDefinitions = use(provisionDefinitions);
|
|
||||||
return provisions.map((id) => awaitedProvisionDefinitions[id]);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,459 @@
|
|||||||
|
import { use } from 'react';
|
||||||
|
import { isExplosive } from '../core/blitz/isExplosive';
|
||||||
|
import { resolveNearPenetration } from '../core/blitz/resolveNearPenetration';
|
||||||
|
import { coefficient } from '../core/blitzkrieg/coefficient';
|
||||||
|
import { degressiveStat } from '../core/blitzkrieg/degressiveStat';
|
||||||
|
import { equipmentDefinitions } from '../core/blitzkrieg/equipmentDefinitions';
|
||||||
|
import { modelDefinitions } from '../core/blitzkrieg/modelDefinitions';
|
||||||
|
import { normalizeBoundingBox } from '../core/blitzkrieg/normalizeBoundingBox';
|
||||||
|
import { progressiveStat } from '../core/blitzkrieg/progressiveStat';
|
||||||
|
import { provisionDefinitions } from '../core/blitzkrieg/provisionDefinitions';
|
||||||
|
import { resolveDpm } from '../core/blitzkrieg/resolveDpm';
|
||||||
|
import { sum } from '../core/blitzkrieg/sum';
|
||||||
|
import {
|
||||||
|
EngineDefinition,
|
||||||
|
GunDefinition,
|
||||||
|
ShellDefinition,
|
||||||
|
TankDefinition,
|
||||||
|
TrackDefinition,
|
||||||
|
TurretDefinition,
|
||||||
|
} from '../core/blitzkrieg/tankDefinitions';
|
||||||
|
import { unionBoundingBox } from '../core/blitzkrieg/unionBoundingBox';
|
||||||
|
import { EquipmentMatrix } from '../stores/duel';
|
||||||
|
|
||||||
|
export function useTankCharacteristics({
|
||||||
|
tank,
|
||||||
|
turret,
|
||||||
|
gun,
|
||||||
|
shell,
|
||||||
|
consumables,
|
||||||
|
equipmentMatrix,
|
||||||
|
crewMastery,
|
||||||
|
provisions,
|
||||||
|
engine,
|
||||||
|
crewSkills,
|
||||||
|
camouflage,
|
||||||
|
track,
|
||||||
|
stockEngine,
|
||||||
|
stockTrack,
|
||||||
|
stockTurret,
|
||||||
|
stockGun,
|
||||||
|
}: {
|
||||||
|
tank: TankDefinition;
|
||||||
|
turret: TurretDefinition;
|
||||||
|
gun: GunDefinition;
|
||||||
|
shell: ShellDefinition;
|
||||||
|
track: TrackDefinition;
|
||||||
|
engine: EngineDefinition;
|
||||||
|
stockEngine: EngineDefinition;
|
||||||
|
stockTrack: TrackDefinition;
|
||||||
|
stockTurret: TurretDefinition;
|
||||||
|
stockGun: GunDefinition;
|
||||||
|
consumables: number[];
|
||||||
|
equipmentMatrix: EquipmentMatrix;
|
||||||
|
crewMastery: number;
|
||||||
|
provisions: number[];
|
||||||
|
crewSkills: Record<string, number>;
|
||||||
|
camouflage: boolean;
|
||||||
|
}) {
|
||||||
|
const awaitedModelDefinitions = use(modelDefinitions);
|
||||||
|
const awaitedEquipmentDefinitions = use(equipmentDefinitions);
|
||||||
|
const awaitedProvisionDefinitions = use(provisionDefinitions);
|
||||||
|
const presetRows = awaitedEquipmentDefinitions.presets[tank.equipment];
|
||||||
|
const tankModelDefinition = awaitedModelDefinitions[tank.id];
|
||||||
|
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
||||||
|
const gunModelDefinition = turretModelDefinition.guns[gun.id];
|
||||||
|
|
||||||
|
function equipment(id: number) {
|
||||||
|
return presetRows.some((equipmentRow, rowIndex) => {
|
||||||
|
return equipmentRow.some((equipment, columnIndex) => {
|
||||||
|
const choice = equipmentMatrix[rowIndex][columnIndex];
|
||||||
|
if (choice === 0) return false;
|
||||||
|
const equipped = equipment[choice === -1 ? 0 : 1];
|
||||||
|
return equipped === id;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function consumable(id: number) {
|
||||||
|
return consumables.includes(id);
|
||||||
|
}
|
||||||
|
function provision(id: number) {
|
||||||
|
return provisions.includes(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasRammer = equipment(100);
|
||||||
|
const hasImprovedVentilation = equipment(102);
|
||||||
|
const hasCalibratedShells = equipment(103);
|
||||||
|
const hasEnhancedGunLayingDrive = equipment(104);
|
||||||
|
const hasSupercharger = equipment(107);
|
||||||
|
const hasVerticalStabilizer = equipment(105);
|
||||||
|
const hasRefinedGun = equipment(106);
|
||||||
|
const hasImprovedAssembly = equipment(111);
|
||||||
|
const hasImprovedOptics = equipment(114);
|
||||||
|
const hasImprovedControl = equipment(116);
|
||||||
|
const hasEngineAccelerator = equipment(117);
|
||||||
|
const hasCamouflageNet = equipment(115);
|
||||||
|
const hasImprovedVerticalStabilizer = equipment(122);
|
||||||
|
const hasImprovedSuspension = equipment(123);
|
||||||
|
|
||||||
|
const hasTungsten = consumable(45);
|
||||||
|
const hasAdrenaline = consumable(18);
|
||||||
|
const hasShellReloadBoost = consumable(29);
|
||||||
|
const hasEnginePowerBoost = consumable(14);
|
||||||
|
const hasImprovedEnginePowerBoost = consumable(27);
|
||||||
|
const hasReticleCalibration = consumable(28);
|
||||||
|
|
||||||
|
const hasStandardFuel = provision(18);
|
||||||
|
const hasImprovedFuel = provision(19);
|
||||||
|
const hasProtectiveKit = provision(22);
|
||||||
|
const hasSandbagArmor = provision(28);
|
||||||
|
const hasEnhancedSandbagArmor = provision(29);
|
||||||
|
const hasImprovedGunPowder = provision(46);
|
||||||
|
const hasGearOil = provision(44);
|
||||||
|
const hasImprovedGearOil = provision(45);
|
||||||
|
|
||||||
|
const gunDefaultPitch = tankModelDefinition.turretRotation?.pitch ?? 0;
|
||||||
|
const provisionCrewBonus =
|
||||||
|
provisions.reduce(
|
||||||
|
(total, provision) =>
|
||||||
|
awaitedProvisionDefinitions[provision].crew
|
||||||
|
? total + awaitedProvisionDefinitions[provision].crew! / 100
|
||||||
|
: total,
|
||||||
|
0,
|
||||||
|
) + (hasImprovedVentilation ? 0.05 : 0);
|
||||||
|
const commanderMastery = crewMastery + provisionCrewBonus;
|
||||||
|
const crewMemberMastery = commanderMastery * 1.1;
|
||||||
|
|
||||||
|
const intraClipCoefficient = coefficient([hasShellReloadBoost, -0.3]);
|
||||||
|
const damageCoefficient = coefficient([hasTungsten, 0.15]);
|
||||||
|
const reloadCoefficient =
|
||||||
|
(coefficient([hasRammer, -0.07]) *
|
||||||
|
coefficient([true, degressiveStat(crewMemberMastery)])) /
|
||||||
|
coefficient([hasAdrenaline && gun.type === 'regular', 0.2]);
|
||||||
|
const penetrationCoefficient = coefficient([
|
||||||
|
hasCalibratedShells,
|
||||||
|
isExplosive(shell.type) ? 0.1 : 0.05,
|
||||||
|
]);
|
||||||
|
const healthCoefficient = coefficient(
|
||||||
|
[hasSandbagArmor, 0.03],
|
||||||
|
[hasEnhancedSandbagArmor, 0.06],
|
||||||
|
[hasImprovedAssembly, 0.06],
|
||||||
|
);
|
||||||
|
const shellVelocityCoefficient = coefficient(
|
||||||
|
[hasSupercharger, 0.3],
|
||||||
|
[hasImprovedGunPowder, 0.3],
|
||||||
|
);
|
||||||
|
const penetrationLossOverDistanceCoefficient = coefficient([
|
||||||
|
hasSupercharger,
|
||||||
|
-0.5,
|
||||||
|
]);
|
||||||
|
const aimTimeCoefficient =
|
||||||
|
coefficient(
|
||||||
|
[hasEnhancedGunLayingDrive, -0.1],
|
||||||
|
[hasReticleCalibration, -0.4],
|
||||||
|
) * coefficient([true, degressiveStat(crewMemberMastery)]);
|
||||||
|
const dispersionStillCoefficient =
|
||||||
|
coefficient([hasRefinedGun, -0.1], [hasReticleCalibration, -0.4]) *
|
||||||
|
coefficient([true, degressiveStat(crewMemberMastery)]);
|
||||||
|
const dispersionDamagedCoefficient = coefficient([
|
||||||
|
hasVerticalStabilizer,
|
||||||
|
-0.15,
|
||||||
|
]);
|
||||||
|
const dispersionHullTraverseCoefficient = coefficient(
|
||||||
|
[hasVerticalStabilizer, -0.15],
|
||||||
|
[true, -crewSkills.smooth_turn / 100],
|
||||||
|
);
|
||||||
|
const dispersionAfterShotCoefficient = coefficient(
|
||||||
|
[hasVerticalStabilizer, -0.15],
|
||||||
|
[true, -crewSkills.soft_recoil / 100],
|
||||||
|
);
|
||||||
|
const dispersionMovingCoefficient = coefficient(
|
||||||
|
[hasVerticalStabilizer, -0.15],
|
||||||
|
[true, -crewSkills.smooth_driving / 100],
|
||||||
|
);
|
||||||
|
const dispersionTurretTraverseCoefficient = coefficient(
|
||||||
|
[hasVerticalStabilizer, -0.15],
|
||||||
|
[true, -crewSkills.smooth_turret * (2 / 100)],
|
||||||
|
);
|
||||||
|
const enginePowerCoefficient = coefficient(
|
||||||
|
[
|
||||||
|
hasEngineAccelerator &&
|
||||||
|
(tank.class === 'lightTank' || tank.class === 'mediumTank'),
|
||||||
|
0.05,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
hasEngineAccelerator &&
|
||||||
|
(tank.class === 'heavyTank' || tank.class === 'AT-SPG'),
|
||||||
|
0.07,
|
||||||
|
],
|
||||||
|
[hasEnginePowerBoost, 0.2],
|
||||||
|
[hasImprovedEnginePowerBoost, 0.4],
|
||||||
|
[hasStandardFuel, 0.03],
|
||||||
|
[hasImprovedFuel, 0.1],
|
||||||
|
[hasGearOil, 0.03],
|
||||||
|
[hasImprovedGearOil, 0.06],
|
||||||
|
);
|
||||||
|
const turretTraverseCoefficient =
|
||||||
|
coefficient([hasStandardFuel, 0.03], [hasImprovedFuel, 0.1]) *
|
||||||
|
coefficient([true, progressiveStat(crewMemberMastery)]);
|
||||||
|
const hullTraverseCoefficient =
|
||||||
|
coefficient(
|
||||||
|
[hasImprovedControl, 0.1],
|
||||||
|
[hasImprovedEnginePowerBoost, 0.05],
|
||||||
|
[true, crewSkills.virtuoso / 100],
|
||||||
|
) * coefficient([true, progressiveStat(crewMemberMastery)]);
|
||||||
|
const resistanceCoefficient = coefficient([hasImprovedSuspension, -0.25]);
|
||||||
|
const viewRangeCoefficient =
|
||||||
|
coefficient([
|
||||||
|
hasImprovedOptics,
|
||||||
|
tank.class === 'AT-SPG' ? 0.05 : tank.class === 'heavyTank' ? 0.07 : 0.1,
|
||||||
|
]) * coefficient([true, progressiveStat(commanderMastery)]);
|
||||||
|
const fireChanceCoefficient = coefficient([hasProtectiveKit, -0.2]);
|
||||||
|
|
||||||
|
const speedForwardsSum = sum(
|
||||||
|
[hasImprovedEnginePowerBoost, 5],
|
||||||
|
[hasGearOil, 2],
|
||||||
|
[hasImprovedGearOil, 4],
|
||||||
|
);
|
||||||
|
const speedBackwardsSum = sum(
|
||||||
|
[hasImprovedEnginePowerBoost, 10],
|
||||||
|
[hasGearOil, 2],
|
||||||
|
[hasImprovedGearOil, 4],
|
||||||
|
);
|
||||||
|
const camouflageSumMoving = sum(
|
||||||
|
[
|
||||||
|
hasCamouflageNet,
|
||||||
|
tank.class === 'heavyTank' ? 0.03 : tank.class === 'AT-SPG' ? 0.07 : 0.05,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
camouflage,
|
||||||
|
tank.class === 'AT-SPG' ? 0.04 : tank.class === 'heavyTank' ? 0.03 : 0.02,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
const camouflageSumStill = sum(
|
||||||
|
[
|
||||||
|
hasCamouflageNet,
|
||||||
|
2 *
|
||||||
|
(tank.class === 'heavyTank'
|
||||||
|
? 0.03
|
||||||
|
: tank.class === 'AT-SPG'
|
||||||
|
? 0.07
|
||||||
|
: 0.05),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
camouflage,
|
||||||
|
tank.class === 'AT-SPG' ? 0.04 : tank.class === 'heavyTank' ? 0.03 : 0.02,
|
||||||
|
],
|
||||||
|
[true, crewSkills.camouflage * (3 / 100)],
|
||||||
|
);
|
||||||
|
|
||||||
|
const size = normalizeBoundingBox(
|
||||||
|
unionBoundingBox(
|
||||||
|
tankModelDefinition.boundingBox,
|
||||||
|
turretModelDefinition.boundingBox,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
const weightKg =
|
||||||
|
tank.weight + engine.weight + track.weight + turret.weight + gun.weight;
|
||||||
|
const weightTons = weightKg / 1000;
|
||||||
|
const stockWeight =
|
||||||
|
tank.weight +
|
||||||
|
stockEngine.weight +
|
||||||
|
stockTrack.weight +
|
||||||
|
stockTurret.weight +
|
||||||
|
stockGun.weight;
|
||||||
|
const resolvedEnginePower = engine.power * enginePowerCoefficient;
|
||||||
|
const dpm = resolveDpm(
|
||||||
|
gun,
|
||||||
|
shell,
|
||||||
|
damageCoefficient,
|
||||||
|
reloadCoefficient,
|
||||||
|
intraClipCoefficient,
|
||||||
|
);
|
||||||
|
const dpmMaximum =
|
||||||
|
gun.type === 'autoReloader'
|
||||||
|
? gun.reload.at(-1)! < gun.reload.at(-2)!
|
||||||
|
? ((damageCoefficient * shell.damage.armor) /
|
||||||
|
(gun.reload.at(-1)! * reloadCoefficient +
|
||||||
|
gun.intraClip * intraClipCoefficient)) *
|
||||||
|
60
|
||||||
|
: ((damageCoefficient * shell.damage.armor) /
|
||||||
|
(gun.reload[0] * reloadCoefficient)) *
|
||||||
|
60
|
||||||
|
: undefined;
|
||||||
|
const dpmEffective =
|
||||||
|
gun.type === 'autoReloader'
|
||||||
|
? gun.reload.at(-1)! < gun.reload.at(-2)!
|
||||||
|
? ((damageCoefficient * shell.damage.armor) /
|
||||||
|
(gun.reload.at(-1)! * reloadCoefficient +
|
||||||
|
gun.intraClip * intraClipCoefficient)) *
|
||||||
|
(60 -
|
||||||
|
(gun.reload.slice(0, -1).length - 1) *
|
||||||
|
gun.intraClip *
|
||||||
|
intraClipCoefficient) +
|
||||||
|
damageCoefficient *
|
||||||
|
shell.damage.armor *
|
||||||
|
gun.reload.slice(0, -1).length
|
||||||
|
: ((damageCoefficient * shell.damage.armor) /
|
||||||
|
(gun.reload[0] * reloadCoefficient +
|
||||||
|
gun.intraClip * intraClipCoefficient)) *
|
||||||
|
(60 -
|
||||||
|
(gun.reload.slice(1).length - 1) *
|
||||||
|
gun.intraClip *
|
||||||
|
intraClipCoefficient) +
|
||||||
|
damageCoefficient * shell.damage.armor * gun.reload.slice(1).length
|
||||||
|
: undefined;
|
||||||
|
const shells = gun.type === 'regular' ? 1 : gun.shells.length;
|
||||||
|
const mostOptimalShellIndex =
|
||||||
|
gun.type === 'autoReloader'
|
||||||
|
? gun.reload.at(-1)! < gun.reload.at(-2)!
|
||||||
|
? gun.reload.length
|
||||||
|
: 1
|
||||||
|
: undefined;
|
||||||
|
const shellReloads =
|
||||||
|
gun.type === 'autoReloader'
|
||||||
|
? gun.reload.map((reload) => reload * reloadCoefficient)
|
||||||
|
: undefined;
|
||||||
|
const shellReload =
|
||||||
|
gun.type === 'autoReloader' ? undefined : gun.reload * reloadCoefficient;
|
||||||
|
const intraClip =
|
||||||
|
gun.type === 'regular' ? undefined : gun.intraClip * intraClipCoefficient;
|
||||||
|
const caliber = shell.caliber;
|
||||||
|
const penetration =
|
||||||
|
resolveNearPenetration(shell.penetration) * penetrationCoefficient;
|
||||||
|
const damage = shell.damage.armor * damageCoefficient;
|
||||||
|
const moduleDamage = shell.damage.module * damageCoefficient;
|
||||||
|
const explosionRadius = shell.explosionRadius;
|
||||||
|
const shellVelocity = shell.speed * shellVelocityCoefficient;
|
||||||
|
const aimTime = gun.aimTime * aimTimeCoefficient;
|
||||||
|
const dispersion = gun.dispersion.base * dispersionStillCoefficient;
|
||||||
|
const dispersionMoving = track.dispersion.move * dispersionMovingCoefficient;
|
||||||
|
const dispersionHullTraversing =
|
||||||
|
track.dispersion.traverse * dispersionHullTraverseCoefficient;
|
||||||
|
const dispersionTurretTraversing =
|
||||||
|
gun.dispersion.traverse * dispersionTurretTraverseCoefficient;
|
||||||
|
const dispersionShooting =
|
||||||
|
gun.dispersion.shot * dispersionAfterShotCoefficient;
|
||||||
|
const dispersionGunDamaged =
|
||||||
|
gun.dispersion.damaged * dispersionDamagedCoefficient;
|
||||||
|
const gunDepression =
|
||||||
|
gunModelDefinition.pitch.max +
|
||||||
|
gunDefaultPitch +
|
||||||
|
(hasImprovedVerticalStabilizer ? 3 : 0);
|
||||||
|
const gunElevation =
|
||||||
|
-gunModelDefinition.pitch.min -
|
||||||
|
gunDefaultPitch +
|
||||||
|
(hasImprovedVerticalStabilizer ? 3 : 0);
|
||||||
|
const gunFrontalDepression = gunModelDefinition.pitch.front?.max;
|
||||||
|
const gunFrontalElevation = gunModelDefinition.pitch.front
|
||||||
|
? -gunModelDefinition.pitch.front.min
|
||||||
|
: undefined;
|
||||||
|
const gunRearDepression = gunModelDefinition.pitch.back?.max;
|
||||||
|
const gunRearElevation = gunModelDefinition.pitch.back
|
||||||
|
? -gunModelDefinition.pitch.back.min
|
||||||
|
: undefined;
|
||||||
|
const azimuthLeft = turretModelDefinition.yaw
|
||||||
|
? -turretModelDefinition.yaw.min
|
||||||
|
: undefined;
|
||||||
|
const azimuthRight = turretModelDefinition.yaw?.max;
|
||||||
|
const speedForwards = tank.speed.forwards + speedForwardsSum;
|
||||||
|
const speedBackwards = tank.speed.backwards + speedBackwardsSum;
|
||||||
|
const enginePower = resolvedEnginePower;
|
||||||
|
const powerToWeightRatioHardTerrain =
|
||||||
|
resolvedEnginePower /
|
||||||
|
weightTons /
|
||||||
|
(track.resistance.hard * resistanceCoefficient);
|
||||||
|
const powerToWeightRatioMediumTerrain =
|
||||||
|
resolvedEnginePower /
|
||||||
|
weightTons /
|
||||||
|
(track.resistance.medium * resistanceCoefficient);
|
||||||
|
const powerToWeightRatioSoftTerrain =
|
||||||
|
resolvedEnginePower /
|
||||||
|
weightTons /
|
||||||
|
(track.resistance.soft * resistanceCoefficient);
|
||||||
|
const weight = weightTons;
|
||||||
|
const turretTraverseSpeed = turret.traverseSpeed * turretTraverseCoefficient;
|
||||||
|
const hullTraverseHardTerrain =
|
||||||
|
(resolvedEnginePower / stockEngine.power) *
|
||||||
|
track.traverseSpeed *
|
||||||
|
hullTraverseCoefficient *
|
||||||
|
(track.resistance.hard / (track.resistance.hard * resistanceCoefficient)) *
|
||||||
|
(stockWeight / weightKg);
|
||||||
|
const hullTraverseMediumTerrain =
|
||||||
|
(resolvedEnginePower / stockEngine.power) *
|
||||||
|
track.traverseSpeed *
|
||||||
|
hullTraverseCoefficient *
|
||||||
|
(track.resistance.hard /
|
||||||
|
(track.resistance.medium * resistanceCoefficient)) *
|
||||||
|
(stockWeight / weightKg);
|
||||||
|
const hullTraverseSoftTerrain =
|
||||||
|
(resolvedEnginePower / stockEngine.power) *
|
||||||
|
track.traverseSpeed *
|
||||||
|
hullTraverseCoefficient *
|
||||||
|
(track.resistance.hard / (track.resistance.soft * resistanceCoefficient)) *
|
||||||
|
(stockWeight / weightKg);
|
||||||
|
const health = (tank.health + turret.health) * healthCoefficient;
|
||||||
|
const fireChance = engine.fireChance * fireChanceCoefficient;
|
||||||
|
const viewRange = turret.viewRange * viewRangeCoefficient;
|
||||||
|
const camouflageStill = tank.camouflage.still + camouflageSumStill;
|
||||||
|
const camouflageMoving = tank.camouflage.moving + camouflageSumMoving;
|
||||||
|
const camouflageShootingStill =
|
||||||
|
tank.camouflage.still * gun.camouflageLoss + camouflageSumStill;
|
||||||
|
const camouflageShootingMoving =
|
||||||
|
tank.camouflage.moving * gun.camouflageLoss + camouflageSumMoving;
|
||||||
|
const camouflageCaughtOnFire =
|
||||||
|
tank.camouflage.onFire * tank.camouflage.still + camouflageSumStill;
|
||||||
|
const tankSize = [size[0], size[2], size[1]];
|
||||||
|
|
||||||
|
return {
|
||||||
|
dpm,
|
||||||
|
dpmMaximum,
|
||||||
|
dpmEffective,
|
||||||
|
shells,
|
||||||
|
mostOptimalShellIndex,
|
||||||
|
shellReloads,
|
||||||
|
shellReload,
|
||||||
|
intraClip,
|
||||||
|
caliber,
|
||||||
|
penetration,
|
||||||
|
damage,
|
||||||
|
moduleDamage,
|
||||||
|
explosionRadius,
|
||||||
|
shellVelocity,
|
||||||
|
aimTime,
|
||||||
|
dispersion,
|
||||||
|
dispersionMoving,
|
||||||
|
dispersionHullTraversing,
|
||||||
|
dispersionTurretTraversing,
|
||||||
|
dispersionShooting,
|
||||||
|
dispersionGunDamaged,
|
||||||
|
gunDepression,
|
||||||
|
gunElevation,
|
||||||
|
gunFrontalDepression,
|
||||||
|
gunFrontalElevation,
|
||||||
|
gunRearDepression,
|
||||||
|
gunRearElevation,
|
||||||
|
azimuthLeft,
|
||||||
|
azimuthRight,
|
||||||
|
speedForwards,
|
||||||
|
speedBackwards,
|
||||||
|
enginePower,
|
||||||
|
powerToWeightRatioHardTerrain,
|
||||||
|
powerToWeightRatioMediumTerrain,
|
||||||
|
powerToWeightRatioSoftTerrain,
|
||||||
|
weight,
|
||||||
|
turretTraverseSpeed,
|
||||||
|
hullTraverseHardTerrain,
|
||||||
|
hullTraverseMediumTerrain,
|
||||||
|
hullTraverseSoftTerrain,
|
||||||
|
health,
|
||||||
|
fireChance,
|
||||||
|
viewRange,
|
||||||
|
camouflageStill,
|
||||||
|
camouflageMoving,
|
||||||
|
camouflageShootingStill,
|
||||||
|
camouflageShootingMoving,
|
||||||
|
camouflageCaughtOnFire,
|
||||||
|
tankSize,
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in new issue
Block a user