2026-10-04 17:08:03 -05:00
|
|
|
import { blitzkitToBlitzShellKind } from "@blitzkit/core";
|
2026-10-04 17:22:23 -05:00
|
|
|
import { ShellType } from "@blitzkit/protos";
|
2026-08-31 20:13:31 -05:00
|
|
|
import {
|
|
|
|
|
CharacteristicType,
|
|
|
|
|
type Characteristic,
|
|
|
|
|
} from "../types/characteristics";
|
|
|
|
|
|
|
|
|
|
export const characteristics = {
|
|
|
|
|
gun_type: {
|
|
|
|
|
type: CharacteristicType.Enum,
|
|
|
|
|
compute({ gun }) {
|
|
|
|
|
return gun.gun_type!.$case;
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
clip_size: {
|
|
|
|
|
type: CharacteristicType.Number,
|
|
|
|
|
|
|
|
|
|
should_render({ gun }) {
|
|
|
|
|
return gun.gun_type!.$case !== "regular";
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
compute({ gun }) {
|
|
|
|
|
if (gun.gun_type!.$case === "regular") return 1;
|
|
|
|
|
return gun.gun_type!.value.shell_count;
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
damage: {
|
|
|
|
|
type: CharacteristicType.Number,
|
2026-10-04 17:57:12 -05:00
|
|
|
compute({ shell, environment, equalizer, script, gun }) {
|
2026-10-04 16:42:35 -05:00
|
|
|
let coefficient = 1;
|
|
|
|
|
|
|
|
|
|
if (environment.equalize) coefficient *= equalizer.damage;
|
|
|
|
|
|
2026-10-04 17:53:57 -05:00
|
|
|
// tungsten shells
|
2026-10-04 17:22:23 -05:00
|
|
|
script("protagonist", "TungstenTip", (effect) => {
|
2026-10-04 17:08:03 -05:00
|
|
|
coefficient *= (effect.minDamageFactor + effect.maxDamageFactor) / 2;
|
2026-10-04 16:42:35 -05:00
|
|
|
});
|
|
|
|
|
|
2026-10-04 17:53:57 -05:00
|
|
|
// reactive armor
|
|
|
|
|
script("antagonist", "ShieldKit", (effect) => {
|
|
|
|
|
if (shell.type === ShellType.SHELL_TYPE_HE) return;
|
|
|
|
|
coefficient *= 1 - effect.shieldCoef;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// dynamic armour system
|
|
|
|
|
script("antagonist", "ArmorMover", (effect) => {
|
|
|
|
|
coefficient *= 1 - effect.bonusValues!.damageReductionPercent / 100;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// spall liner
|
|
|
|
|
script("antagonist", "AntiHighExplosive", (effect) => {
|
|
|
|
|
if (shell.type !== ShellType.SHELL_TYPE_HE) return;
|
|
|
|
|
coefficient *= effect.bonusValues!.factor;
|
|
|
|
|
});
|
|
|
|
|
|
2026-10-04 17:57:12 -05:00
|
|
|
if (gun.assault_ranges && gun.assault_ranges.types.includes(shell.type)) {
|
|
|
|
|
const range = gun.assault_ranges.ranges.find(
|
|
|
|
|
({ distance }) => distance >= environment.distance,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (range) coefficient *= range.factor;
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-04 16:42:35 -05:00
|
|
|
return coefficient * shell.armor_damage;
|
2026-10-01 20:11:12 -05:00
|
|
|
},
|
|
|
|
|
},
|
2026-08-31 20:13:31 -05:00
|
|
|
|
2026-10-01 20:11:12 -05:00
|
|
|
module_damage: {
|
|
|
|
|
type: CharacteristicType.Number,
|
2026-10-04 17:08:03 -05:00
|
|
|
compute({ shell, script }) {
|
|
|
|
|
let coefficient = 1;
|
|
|
|
|
|
2026-10-04 17:53:57 -05:00
|
|
|
// tungsten shells
|
2026-10-04 17:22:23 -05:00
|
|
|
script("protagonist", "TungstenTip", (effect) => {
|
2026-10-04 17:08:03 -05:00
|
|
|
coefficient *= (effect.minDamageFactor + effect.maxDamageFactor) / 2;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return coefficient * shell.module_damage;
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
penetration: {
|
|
|
|
|
type: CharacteristicType.Number,
|
|
|
|
|
compute({ shell, script }) {
|
|
|
|
|
let coefficient = 1;
|
|
|
|
|
|
2026-10-04 17:22:23 -05:00
|
|
|
script("protagonist", "CalibratedShells", (effect) => {
|
2026-10-04 18:16:08 -05:00
|
|
|
coefficient +=
|
2026-10-04 17:08:03 -05:00
|
|
|
effect.piercingFactors![blitzkitToBlitzShellKind[shell.type]] / 100;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return coefficient * shell.penetration!.near;
|
2026-08-31 20:13:31 -05:00
|
|
|
},
|
|
|
|
|
},
|
2026-10-04 18:16:08 -05:00
|
|
|
|
|
|
|
|
reload: {
|
|
|
|
|
type: CharacteristicType.Number,
|
|
|
|
|
|
|
|
|
|
should_render({ gun }) {
|
|
|
|
|
return gun.gun_type!.$case !== "auto_reloader";
|
|
|
|
|
},
|
|
|
|
|
|
2026-10-05 17:45:56 -05:00
|
|
|
compute({ gun, script, degressive }) {
|
2026-10-04 18:16:08 -05:00
|
|
|
// should never happen! just for typescript
|
|
|
|
|
if (gun.gun_type!.$case === "auto_reloader") return -Infinity;
|
|
|
|
|
|
2026-10-05 17:45:56 -05:00
|
|
|
let c0 = 1;
|
2026-10-04 18:16:08 -05:00
|
|
|
|
|
|
|
|
script("protagonist", "miscAttrs/gunReloadTimeFactor", (effect) => {
|
2026-10-05 17:45:56 -05:00
|
|
|
c0 += effect.bonusValues!.percent / 100;
|
2026-10-04 18:16:08 -05:00
|
|
|
});
|
|
|
|
|
|
2026-10-05 17:45:56 -05:00
|
|
|
const c = c0 * degressive;
|
|
|
|
|
|
2026-10-04 18:16:08 -05:00
|
|
|
if (gun.gun_type!.$case === "regular") {
|
2026-10-05 17:45:56 -05:00
|
|
|
return c * gun.gun_type!.value.reload;
|
2026-10-04 18:16:08 -05:00
|
|
|
}
|
|
|
|
|
|
2026-10-05 17:45:56 -05:00
|
|
|
return c * gun.gun_type!.value.clip_reload;
|
2026-10-04 18:16:08 -05:00
|
|
|
},
|
|
|
|
|
},
|
2026-08-31 20:13:31 -05:00
|
|
|
} satisfies Record<string, Characteristic>;
|