From 9b5004c39db6b37ce3af9616aefd2fc93157f611 Mon Sep 17 00:00:00 2001 From: arcattack Date: Wed, 8 Jul 2026 16:15:03 -0500 Subject: [PATCH] Missiles: authentic dsrm smoke trail + torso aim (placeholder tracer removed) The "white lines toward the feet" report: the LAUNCHES are authentic (the mech's real MissileLauncher/ProjectileWeapon roster subsystems firing on their own recharge cadence -- that's the every-Nth-shot rhythm), but the visual was the bring-up 3-segment beam tracer, and the aim point was the victim's ORIGIN (ground level between the feet). - Trail: per-frame puffs of the AUTHENTIC dsrm effect (psfx 0, "the lrm smoke trail") along the flight path, frame-oriented so the smoke streams behind the round (its .PFX velocities are +Z = backward); the round itself is a short hot streak. New BTPfxTrailPuff spawns slot particles directly at a moving point (no emitter instance). - Aim: the resolved target point lifts to torso height (the beams already converged there) -- impacts now cyl-resolve to body zones instead of diving at the feet/legs. Follow-up noted: the round MESHES (BULLET/LRMS.BGF ship) for a true model-per-projectile look. Co-Authored-By: Claude Fable 5 --- engine/MUNGA_L4/L4VIDEO.cpp | 30 ++++++++++++++++++++++++++++++ game/reconstructed/mech4.cpp | 23 ++++++++++++++++------- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/engine/MUNGA_L4/L4VIDEO.cpp b/engine/MUNGA_L4/L4VIDEO.cpp index 5f14677..f549a48 100644 --- a/engine/MUNGA_L4/L4VIDEO.cpp +++ b/engine/MUNGA_L4/L4VIDEO.cpp @@ -546,6 +546,36 @@ void BTStartPfx(int effect_number, float x, float y, float z) BTStartPfxFrame(effect_number, x, y, z, 0, 0, 0); } +// Spawn a few particles of a slot's effect DIRECTLY at a moving point (no +// emitter instance) -- the per-frame projectile SMOKE TRAIL (psfx 0 = dsrm, +// "the lrm smoke trail": its velocities stream +Z = BEHIND the round, so the +// frame is built with local +Z = the flight's backward direction). +static void BTPfxSpawn(const BTPfxEmitter &e); // defined below +void BTPfxTrailPuff(int effect_number, float x, float y, float z, + float backx, float backy, float backz, int count) +{ + if (effect_number < 0 || effect_number >= BT_PFX_SLOTS) + return; + const BTPfxDef &d = gBTPfxDefs[effect_number]; + if (!d.valid) + return; + BTPfxEmitter e; // transient -- used only as the spawn context + e.def = &d; + e.pos = D3DXVECTOR3(x, y, z); + D3DXVECTOR3 az(backx, backy, backz); + if (D3DXVec3LengthSq(&az) < 1e-6f) + az = D3DXVECTOR3(0, 0, 1); + D3DXVec3Normalize(&az, &az); + D3DXVECTOR3 up(0, 1, 0); + if (fabsf(az.y) > 0.99f) up = D3DXVECTOR3(1, 0, 0); + D3DXVECTOR3 ax; D3DXVec3Cross(&ax, &up, &az); D3DXVec3Normalize(&ax, &ax); + D3DXVECTOR3 ay; D3DXVec3Cross(&ay, &az, &ax); + e.ax = ax; e.ay = ay; e.az = az; + e.emitAccum = 0.0f; e.issued = 0; e.active = 0; + for (int i = 0; i < count; ++i) + BTPfxSpawn(e); +} + static void BTPfxSpawn(const BTPfxEmitter &e) { const BTPfxDef &d = *e.def; diff --git a/game/reconstructed/mech4.cpp b/game/reconstructed/mech4.cpp index 0ac860e..77b77f3 100644 --- a/game/reconstructed/mech4.cpp +++ b/game/reconstructed/mech4.cpp @@ -768,6 +768,12 @@ void target = gEnemyMech; tpos = ((Mech *)gEnemyMech)->localOrigin.linearPosition; } + // AIM at the torso, not the feet: every target-position source here is the + // victim's ORIGIN (ground level between the feet) -- the beams already + // converge at chest height; missiles must too, or they visibly dive at the + // target's feet and the cylinder resolves leg zones for every hit. + if (target != 0) + tpos.y += kMuzzleHeight; Vector3D d; d.x = tpos.x - mz.x; d.y = tpos.y - mz.y; d.z = tpos.z - mz.z; Scalar len = (Scalar)sqrtf(d.x*d.x + d.y*d.y + d.z*d.z); @@ -804,14 +810,17 @@ static void Scalar step = p.speed * dt; p.pos.x += p.dir.x*step; p.pos.y += p.dir.y*step; p.pos.z += p.dir.z*step; p.traveled += step; - // Missile look: a short smoky exhaust trail (behind) + a bright warm body + a - // hot flame tip, so it reads as a projectile rather than a thin laser line. + // AUTHENTIC missile look: the round is a short hot streak, and the trail + // is the real dsrm smoke-trail effect (psfx 0, "the lrm smoke trail") + // puffed along the flight path each frame with local +Z = backward (the + // .PFX's velocities stream the smoke behind the round). This replaces + // the old 3-segment white tracer lines (a bring-up placeholder). Vector3D bd = p.dir; - Point3D tail; tail.x = prev.x - bd.x*8.0f; tail.y = prev.y - bd.y*8.0f; tail.z = prev.z - bd.z*8.0f; - Point3D tip; tip.x = p.pos.x + bd.x*2.0f; tip.y = p.pos.y + bd.y*2.0f; tip.z = p.pos.z + bd.z*2.0f; - BTPushBeam(tail.x, tail.y, tail.z, prev.x, prev.y, prev.z, 0x00804020u, 0.20f, 3.2f); // dim smoke trail - BTPushBeam(prev.x, prev.y, prev.z, p.pos.x, p.pos.y, p.pos.z, 0x00FF6010u, 0.15f, 2.6f); // orange body - BTPushBeam(p.pos.x, p.pos.y, p.pos.z, tip.x, tip.y, tip.z, 0x00FFF0C0u, 0.15f, 1.4f); // hot flame tip + BTPushBeam(prev.x, prev.y, prev.z, p.pos.x, p.pos.y, p.pos.z, 0x00FFB040u, 0.10f, 0.9f); // the round (hot streak) + { + extern void BTPfxTrailPuff(int, float, float, float, float, float, float, int); + BTPfxTrailPuff(0, prev.x, prev.y, prev.z, -bd.x, -bd.y, -bd.z, 2); + } Scalar dx = p.targetPos.x - p.pos.x, dy = p.targetPos.y - p.pos.y, dz = p.targetPos.z - p.pos.z; if (dx*dx + dy*dy + dz*dz < (10.0f*10.0f) || p.traveled >= p.range)