Issue #27: projectiles carry the weapon's real damage type (ballistic)

Playtest matchlog forensics: across 2,591 applied-damage events in two
rounds, Ballistic (type 1) damage NEVER appeared -- Collision/Explosive/
Laser/Energy only -- despite 136 projectile impacts and autocannon/Gauss
mechs.  The weapon parses its authentic damageData.damageType correctly
(mechweap.cpp:316 maps "BallisticDamage" -> 1), but BTPushProjectile
never carried the type and every projectile-impact site in mech4.cpp
hardcoded ExplosiveDamageType.  So autocannon/Gauss rounds were scaled
against armor by damageScale[Explosive] instead of damageScale[Ballistic]
(the model indexes a distinct scale cell per damage type) -- every
ballistic weapon did the wrong damage all night.

Lasers/PPCs were unaffected (their SendDamageMessage path carries the
weapon's own damageData); missiles happen to BE Explosive so only
autocannon & Gauss were mis-scaled.

BTProjectile gains a damageType field, threaded from the firing weapon
(damageData.damageType) at launch through to the impact dispatch;
missiles resolve Explosive from their own streamed type, autocannon/
Gauss now resolve Ballistic.  The missile-splash template stays
Explosive.  Enum is anonymous so the field casts to Enumeration.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-23 01:09:13 -05:00
co-authored by Claude Opus 4.8
parent 76eaddc4a8
commit 02a41f165f
4 changed files with 30 additions and 7 deletions
+8 -3
View File
@@ -805,6 +805,10 @@ struct BTProjectile {
Scalar damage; // <= 0 -> VISUAL-ONLY round (replicant-side salvo mirror)
int guided; // 1 = missile (seeker loft + steering); 0 = ballistic (straight)
Entity *shooter; // the firing mech (messmgr explosion bundling at impact)
int damageType; // the FIRING WEAPON's authentic damageData.damageType
// (issue: was hardcoded Explosive at impact, so
// autocannon/Gauss Ballistic rounds scaled wrong --
// Ballistic type never reached the armor model).
int weaponSubsys; // firing weapon's roster index (-1 = unthreaded) --
// resolves the weapon's ExplosionModelFile (mslhit/
// acanhit) in the messmgr, task #7 bundling
@@ -1143,7 +1147,7 @@ void
void
BTPushProjectile(const Point3D &muzzle, void *shooter, void *target, const Point3D &targetPos,
Scalar speed, Scalar damage, const Vector3D *launch_velocity, int guided,
int weapon_subsys, int splash_burst, int muzzle_seg)
int weapon_subsys, int splash_burst, int muzzle_seg, int damage_type)
{
// MUZZLE (muzzle wave, 2026-07-12): the passed muzzle is now the weapon's
// AUTHENTIC mount segment (GetMuzzlePoint reads the real segmentIndex --
@@ -1267,6 +1271,7 @@ void
p.aimOffsetY = (target != 0 && BTIsRegisteredMech((Entity *)target))
? (tpos.y - ((Mech *)target)->localOrigin.linearPosition.y) : 0.0f;
p.damage = damage;
p.damageType = damage_type; // weapon's authentic type (issue fix)
p.guided = guided; // autocannon shells fly straight (no seeker in the binary's plain Projectile)
p.shooter = (Entity *)shooter;
p.weaponSubsys = weapon_subsys;
@@ -1471,7 +1476,7 @@ static void
// zone from the cylinder hit-location table. (Previously this
// aimed the internal vital zone directly -> invisible insta-kill.)
Damage dmg;
dmg.damageType = Damage::ExplosiveDamageType;
dmg.damageType = (Enumeration)p.damageType; // issue: was hardcoded Explosive
dmg.damageAmount = p.damage;
dmg.burstCount = 1;
dmg.impactPoint = p.pos;
@@ -1557,7 +1562,7 @@ static void
// The icon's own handler maps -1 -> 0 and runs the death
// transition (-> the FULL 'trkdead' explosion, res 32).
Damage dmg;
dmg.damageType = Damage::ExplosiveDamageType;
dmg.damageType = (Enumeration)p.damageType; // issue: was hardcoded Explosive
dmg.damageAmount = p.damage;
dmg.burstCount = 1;
dmg.impactPoint = p.pos;