mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 18:57:03 +02:00
489 lines
16 KiB
TypeScript
489 lines
16 KiB
TypeScript
import { EquipmentMatrix } from '../../stores/duel';
|
|
import { isExplosive } from '../blitz/isExplosive';
|
|
import { resolveNearPenetration } from '../blitz/resolveNearPenetration';
|
|
import { coefficient } from './coefficient';
|
|
import { degressiveStat } from './degressiveStat';
|
|
import { EquipmentDefinitions } from './equipmentDefinitions';
|
|
import { ModelDefinitions } from './modelDefinitions';
|
|
import { normalizeBoundingBox } from './normalizeBoundingBox';
|
|
import { progressiveStat } from './progressiveStat';
|
|
import { ProvisionDefinitions } from './provisionDefinitions';
|
|
import { resolveDpm } from './resolveDpm';
|
|
import { sum } from './sum';
|
|
import {
|
|
EngineDefinition,
|
|
GunDefinition,
|
|
ShellDefinition,
|
|
TankDefinition,
|
|
TrackDefinition,
|
|
TurretDefinition,
|
|
} from './tankDefinitions';
|
|
import { unionBoundingBox } from './unionBoundingBox';
|
|
|
|
export function tankCharacteristics(
|
|
{
|
|
tank,
|
|
turret,
|
|
gun,
|
|
shell,
|
|
consumables,
|
|
equipmentMatrix,
|
|
crewMastery,
|
|
provisions,
|
|
engine,
|
|
crewSkills,
|
|
camouflage,
|
|
track,
|
|
stockEngine,
|
|
stockTrack,
|
|
stockTurret,
|
|
stockGun,
|
|
applyReactive,
|
|
applyDynamicArmor,
|
|
applySpallLiner,
|
|
}: {
|
|
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;
|
|
applyReactive: boolean;
|
|
applyDynamicArmor: boolean;
|
|
applySpallLiner: boolean;
|
|
},
|
|
{
|
|
modelDefinitions,
|
|
equipmentDefinitions,
|
|
provisionDefinitions,
|
|
}: {
|
|
modelDefinitions: ModelDefinitions;
|
|
equipmentDefinitions: EquipmentDefinitions;
|
|
provisionDefinitions: ProvisionDefinitions;
|
|
},
|
|
) {
|
|
const presetRows = equipmentDefinitions.presets[tank.equipment];
|
|
const tankModelDefinition = modelDefinitions[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) =>
|
|
provisionDefinitions[provision].crew
|
|
? total + provisionDefinitions[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]) *
|
|
coefficient(
|
|
[applyReactive, -0.2],
|
|
[applyDynamicArmor, -0.1],
|
|
[applySpallLiner && shell.type === 'he', -0.2],
|
|
);
|
|
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.count > 2
|
|
? gun.reload.at(-1)! < gun.reload.at(-2)!
|
|
: gun.reload.at(-1)! + gun.intraClip < 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.count;
|
|
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 width = size[2];
|
|
const height = size[0];
|
|
const length = size[1];
|
|
const volume = width * height * length;
|
|
|
|
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,
|
|
width,
|
|
height,
|
|
length,
|
|
volume,
|
|
};
|
|
}
|