diff --git a/game/reconstructed/mech4.cpp b/game/reconstructed/mech4.cpp index 0f149a0..3e4b965 100644 --- a/game/reconstructed/mech4.cpp +++ b/game/reconstructed/mech4.cpp @@ -1075,7 +1075,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 weapon_subsys, int splash_burst, int muzzle_seg) { // MUZZLE (muzzle wave, 2026-07-12): the passed muzzle is now the weapon's // AUTHENTIC mount segment (GetMuzzlePoint reads the real segmentIndex -- @@ -1137,20 +1137,50 @@ void if (launch_velocity != 0 && shooter != 0) { // FRAME CONVENTION (telemetry-verified 2026-07-12): the authored - // MuzzleVelocity is (0,0,+100) -- the launcher frame's +Z is - // FORWARD, but the mech's body frame faces -Z. Transforming +Z - // through the body basis launched every round BACKWARD (then the - // seeker looped it through the ground onto the target -- - // user-reported). Map launcher-forward (+Z) onto body-forward - // (-Z basis): negate the z term. + // MuzzleVelocity is (0,0,+100) with a NEGATED z when composed -- + // the binary's own fire builder does `local_40 -= MV.z` + // (FUN_004bcc60 @part_013.c:8761) [T1]. + // TORSO-TWIST FIX (task #67, user-reported "missiles fire out of + // his back"): rotate through the WEAPON MOUNT SEGMENT's world + // frame, not the body basis. The MadCat's racks ride the TORSO: + // the muzzle POINT tracked the twist (segment-resolved) while the + // launch DIRECTION followed the LEGS -- twist far enough and + // rounds left backward. The binary spawns the missile with the + // full muzzle-segment frame (GetMuzzlePoint fills an AffineMatrix + // into the spawn descriptor, @0x4bcc60:8762-8764) and the dumb + // seeker aims 100u ahead of the MISSILE's own frame -- segment + // orientation IS the authentic launch direction. Mech *sm = (Mech *)shooter; UnitVector ax, ay, az; - sm->localToWorld.GetFromAxis(X_Axis, &ax); - sm->localToWorld.GetFromAxis(Y_Axis, &ay); - sm->localToWorld.GetFromAxis(Z_Axis, &az); + int haveFrame = 0; + if (muzzle_seg >= 0) + { + EntitySegment *seg = sm->GetSegment(muzzle_seg); + if (seg != 0) + { + AffineMatrix mw; + mw.Multiply(seg->GetSegmentToEntity(), sm->localToWorld); + mw.GetFromAxis(X_Axis, &ax); + mw.GetFromAxis(Y_Axis, &ay); + mw.GetFromAxis(Z_Axis, &az); + haveFrame = 1; + } + } + if (!haveFrame) + { + sm->localToWorld.GetFromAxis(X_Axis, &ax); + sm->localToWorld.GetFromAxis(Y_Axis, &ay); + sm->localToWorld.GetFromAxis(Z_Axis, &az); + } p.vel.x = ax.x*launch_velocity->x + ay.x*launch_velocity->y - az.x*launch_velocity->z; p.vel.y = ax.y*launch_velocity->x + ay.y*launch_velocity->y - az.y*launch_velocity->z; p.vel.z = ax.z*launch_velocity->x + ay.z*launch_velocity->y - az.z*launch_velocity->z; + // (task #67) inherit the shooter's motion: the binary adds the + // mech's localVelocity into the spawn velocity (FUN_004b9cbc = + // owner+0x1c4 read, composed @0x4bcc60:8758-8761) [T1]. + p.vel.x += sm->worldLinearVelocity.x; + p.vel.y += sm->worldLinearVelocity.y; + p.vel.z += sm->worldLinearVelocity.z; Scalar lv = (Scalar)sqrtf(p.vel.x*p.vel.x + p.vel.y*p.vel.y + p.vel.z*p.vel.z); if (lv > 1.0f) { p.speed = lv; haveLaunch = 1; } } diff --git a/game/reconstructed/mislanch.cpp b/game/reconstructed/mislanch.cpp index e755281..157c02a 100644 --- a/game/reconstructed/mislanch.cpp +++ b/game/reconstructed/mislanch.cpp @@ -73,7 +73,7 @@ extern void BTPushProjectile(const Point3D &muzzle, void *shooter, void *target, const Point3D &targetPos, Scalar speed, Scalar damage, const Vector3D *launch_velocity = 0, int guided = 1, - int weapon_subsys = -1, int splash_burst = 0); + int weapon_subsys = -1, int splash_burst = 0, int muzzle_seg = -1); //########################################################################### // Port-side salvo replication state (missile-visibility wave). @@ -317,7 +317,8 @@ void MissileLauncher::FireWeapon() (i == 0) ? damageData.damageAmount : 0.0f, &launchVelocity, 1, subsystemID /*messmgr explosion bundling at impact (task #7)*/, - (i == 0) ? nmiss : 0 /*salvo-lead: cluster splash baseBurst*/); + (i == 0) ? nmiss : 0 /*salvo-lead: cluster splash baseBurst*/, + GetSegmentIndex() /*task #67: launch through the MOUNT frame (torso twist)*/); // Salvo replication (missile-visibility wave): bump the fire counter + stamp // the aim point; the extended update record carries both to peer nodes. @@ -389,7 +390,8 @@ void for (int i = 0; i < n; ++i) BTPushProjectile(mz, owner, 0 /*no entity: aim point only*/, rec->salvoTarget, spd, 0.0f /*VISUAL*/, &launchVelocity, 1, - subsystemID /*per-round detonation resolve on this node*/); + subsystemID /*per-round detonation resolve on this node*/, + 0, GetSegmentIndex() /*task #67 mount frame*/); if (getenv("BT_PROJ_LOG")) DEBUG_STREAM << "[projectile] REPLICANT salvo x" << n << " at(" << rec->salvoTarget.x << "," << rec->salvoTarget.y diff --git a/game/reconstructed/projweap.cpp b/game/reconstructed/projweap.cpp index 2a3dc96..0717f13 100644 --- a/game/reconstructed/projweap.cpp +++ b/game/reconstructed/projweap.cpp @@ -95,7 +95,7 @@ extern void BTPushProjectile(const Point3D &muzzle, void *shooter, void *target, const Point3D &targetPos, Scalar speed, Scalar damage, const Vector3D *launch_velocity = 0, int guided = 1, - int weapon_subsys = -1, int splash_burst = 0); // AC: no cluster splash (default 0) + int weapon_subsys = -1, int splash_burst = 0, int muzzle_seg = -1); // AC: no cluster splash (default 0) //############################################################################# @@ -846,7 +846,8 @@ void || rec->fireTarget.z != 0.0f); BTPushProjectile(mz, owner, 0 /*aim point only, no entity*/, rec->fireTarget, spd, 0.0f /*VISUAL*/, - haveAim ? 0 : &launchVelocity, 0 /*ballistic*/, subsystemID); + haveAim ? 0 : &launchVelocity, 0 /*ballistic*/, subsystemID, + 0, GetSegmentIndex() /*task #67 mount frame*/); const char *mzf = getenv("BT_MUZZLE"); // default ON; =0 disables if (mzf == 0 || mzf[0] != '0')