World-pick targeting: 0x388 = whatever is downrange -- mech, terrain, or nothing (task #41)
User video evidence (lasers firing at nothing) + the colleague's
torso-locked report + the binary all reconcile into one model:
The boresight pick hits the WORLD, and the target slot mech+0x388 holds
whatever entity is downrange:
- enemy mech under the boresight -> aimed target (hull point -> STEP-6
zone under the boresight, hotbox + lock ring, damage);
- else the TERRAIN downrange (BTGroundRayHit to 1200) -> the beam and
missiles fire at the scenery ("firing at nothing", as in the pod
videos), range caret reads the ground distance (authentic :5639), no
damage;
- else (sky) -> no target, and the weapon's own double 0x388 gate
refuses the discharge.
Binary evidence for non-mech targets: HudSimulation :5620 explicitly
handles a target WITHOUT damage zones (target->0x120 == 0) -- dead code
if only mechs were ever targeted. The pick is automatic every frame
(0x388: 11 reads / 0 direct stores in CODE).
Implementation: gBTTerrainEntity captured in MakeEntityRenderables
(Terrain::GetClassDerivations); mech4 pick = enemy PickRayHit else
ground ray -> terrain entity + ground point; damage/hotbox/lock only
for the mech pick; the task-40 enemy auto-converge REMOVED (facing away
now fires at the scenery, not magically at the enemy -- the user's
complaint). Verified all three states headless (BT_AIM 0,0 / .6,.5 /
0,-.8): aimed zone hits / terrain beams + 0 damage / no discharge.
Fourth (and evidence-complete) targeting model iteration; KB updated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
60ed54e008
commit
e30a61a62f
@@ -2286,38 +2286,62 @@ void
|
||||
}
|
||||
}
|
||||
|
||||
// --- TARGETING (task #36 -- the AUTHENTIC Reticle acquisition,
|
||||
// engine/MUNGA/RETICLE.h [T0]): the pilot slews the crosshair
|
||||
// (gBTAimX/Y); a pick ray through it tests the enemy's collision
|
||||
// volume; the mech UNDER the crosshair becomes the designated target
|
||||
// (sticky -- it persists when the crosshair drifts off, marked by
|
||||
// the HUD designator; re-hover to refresh, burial clears it). The
|
||||
// designation feeds the engine-Entity target slots the whole weapon
|
||||
// path reads (target position @0x37c, target entity @0x388, zone
|
||||
// @0x38c). While the crosshair is ON the target ("hot"), the aim
|
||||
// point is the PICKED HULL POINT -- the STEP-6 cylinder lookup then
|
||||
// resolves damage to the zone under the crosshair (aimed fire).
|
||||
Entity *hotTarget = 0; // mech under the crosshair NOW
|
||||
// --- TARGETING: the WORLD-PICK model (task #41, the reconciliation of
|
||||
// ALL the evidence). The boresight ray picks whatever is DOWNRANGE:
|
||||
// 1. the enemy mech's collision volume -> aimed target (hull point
|
||||
// -> STEP-6 zone under the boresight; hotbox + lock ring);
|
||||
// 2. else the TERRAIN (BTGroundRayHit) -> the ground point becomes
|
||||
// the target -- the beam fires at the scenery ("firing at
|
||||
// nothing", seen in the pod demo videos), no damage;
|
||||
// 3. else (sky, nothing within range) -> NO target, and the
|
||||
// weapon's own double gate (FUN_004baa88:7689 + FUN_004bace8:
|
||||
// 7727 -- both require mech+0x388 != 0) refuses the discharge.
|
||||
// Binary evidence for non-mech targets: HudSimulation :5620
|
||||
// explicitly handles a target WITHOUT damage zones (target->0x120
|
||||
// == 0) -- dead code if only mechs were ever targeted. The pick is
|
||||
// AUTOMATIC every frame (0x388 has 11 reads / 0 direct stores in
|
||||
// CODE -- written indirectly, never a manual player lock).
|
||||
Entity *hotTarget = 0; // the enemy mech under the boresight
|
||||
Point3D hotPoint; // picked world point on its hull
|
||||
static int gAimNoRay = 0, gAimNoPick = 0, gAimHits = 0; // 1Hz diagnostics
|
||||
Entity *pickTarget = 0; // what the boresight ray hit (mech/terrain)
|
||||
Point3D pickPoint; // where it hit
|
||||
static int gAimNoRay = 0, gAimGround = 0, gAimHits = 0; // 1Hz diagnostics
|
||||
{
|
||||
extern int BTGetAimRay(float rx, float ry, float outStart[3], float outDir[3]);
|
||||
extern Entity *gBTTerrainEntity; // captured by MakeEntityRenderables
|
||||
float rs[3], rd[3];
|
||||
if (!BTGetAimRay(gBTAimX, gBTAimY, rs, rd))
|
||||
{
|
||||
++gAimNoRay;
|
||||
}
|
||||
else if (gEnemyMech != 0)
|
||||
else
|
||||
{
|
||||
Point3D rayStart(rs[0], rs[1], rs[2]);
|
||||
Vector3D rayDir(rd[0], rd[1], rd[2]);
|
||||
if (((Mech *)gEnemyMech)->PickRayHit(rayStart, rayDir, 4000.0f, &hotPoint))
|
||||
if (gEnemyMech != 0 && !((Mech *)gEnemyMech)->IsMechDestroyed()
|
||||
&& ((Mech *)gEnemyMech)->PickRayHit(rayStart, rayDir, 4000.0f, &hotPoint))
|
||||
{
|
||||
hotTarget = gEnemyMech;
|
||||
hotTarget = gEnemyMech;
|
||||
pickTarget = gEnemyMech;
|
||||
pickPoint = hotPoint;
|
||||
++gAimHits;
|
||||
}
|
||||
else
|
||||
++gAimNoPick;
|
||||
{
|
||||
// the ground downrange of the guns (max = the pod's HUD
|
||||
// range scale; past it the shot is a sky shot -> no target)
|
||||
extern bool BTGroundRayHit(float,float,float, float,float,float,
|
||||
float, float*,float*,float*);
|
||||
float hx, hy, hz;
|
||||
if (gBTTerrainEntity != 0 &&
|
||||
BTGroundRayHit(rs[0], rs[1], rs[2], rd[0], rd[1], rd[2],
|
||||
1200.0f, &hx, &hy, &hz))
|
||||
{
|
||||
pickTarget = gBTTerrainEntity;
|
||||
pickPoint.x = hx; pickPoint.y = hy; pickPoint.z = hz;
|
||||
++gAimGround;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The Reticle struct (the mech's TargetReticle attribute): position,
|
||||
@@ -2325,53 +2349,44 @@ void
|
||||
// damage delivery (the authentic percent-table roll, STEP 6).
|
||||
targetReticle.reticlePosition.x = gBTAimX;
|
||||
targetReticle.reticlePosition.y = gBTAimY;
|
||||
targetReticle.targetEntity = hotTarget;
|
||||
targetReticle.targetEntity = pickTarget;
|
||||
targetReticle.targetDamageZone = -1;
|
||||
if (hotTarget != 0)
|
||||
targetReticle.rayIntersection = hotPoint;
|
||||
if (pickTarget != 0)
|
||||
targetReticle.rayIntersection = pickPoint;
|
||||
|
||||
// TARGET (mech+0x388) is AUTO-ACQUIRED, not manually locked (task #40,
|
||||
// binary-confirmed): the weapon's fire path (Emitter FUN_004baa88 :7689
|
||||
// + FireWeapon FUN_004bace8 :7727) is DOUBLY gated on mech+0x388 != 0 --
|
||||
// no target, NO beam, ever -- but 0x388 is WRITTEN INDIRECTLY (a
|
||||
// disasm scan of the whole CODE section finds 11 reads and ZERO direct
|
||||
// stores), i.e. the game AUTO-SELECTS the target for you; you never
|
||||
// manually "lock" to fire. The spinning-ring LOCK (HudSimulation
|
||||
// 5619-5634) is a SEPARATE, stricter state layered on top. So: 0x388
|
||||
// = the enemy whenever it is alive (the pod's auto-target); firing
|
||||
// needs only that, NOT a pinpoint boresight hit. A pinpoint hull hit
|
||||
// (hotTarget) UPGRADES the shot to aimed-zone damage + the lock ring.
|
||||
Entity *autoTarget =
|
||||
(gEnemyMech != 0 && !((Mech *)gEnemyMech)->IsMechDestroyed())
|
||||
? gEnemyMech : 0;
|
||||
if (autoTarget != 0)
|
||||
// the engine-Entity target slots the whole weapon path reads
|
||||
if (pickTarget != 0)
|
||||
{
|
||||
MECH_TARGET_ENTITY(this) = autoTarget;
|
||||
MECH_TARGET_ENTITY(this) = pickTarget;
|
||||
MECH_TARGET_SUBIDX(this) = -1;
|
||||
if (hotTarget != 0)
|
||||
MECH_TARGET_POS(this) = hotPoint; // aimed: the picked hull point
|
||||
else
|
||||
{
|
||||
// no pinpoint pick -> converge on the target's centre mass
|
||||
// (the beam still fires; the zone resolves near-centre).
|
||||
Point3D cm = ((Mech *)autoTarget)->localOrigin.linearPosition;
|
||||
cm.y += kMuzzleHeight;
|
||||
MECH_TARGET_POS(this) = cm;
|
||||
}
|
||||
MECH_TARGET_POS(this) = pickPoint; // beam endpoint = the pick
|
||||
}
|
||||
else
|
||||
{
|
||||
MECH_TARGET_ENTITY(this) = 0; // no living enemy -> no target
|
||||
MECH_TARGET_ENTITY(this) = 0; // sky: no target, no discharge
|
||||
MECH_TARGET_SUBIDX(this) = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// HUD feeds: the range caret + the hotbox (world point + state) + the
|
||||
// recovered-Execute instruments (compass, twist tape, group mask).
|
||||
// The range caret tracks the PICK (authentic: :5639 computes it from
|
||||
// mech+0x37c whatever the target is -- a terrain pick reads the ground
|
||||
// distance); the hotbox + lock exist ONLY for a mech target (:5620 --
|
||||
// a target with no damage zones gets neither).
|
||||
{
|
||||
extern void BTSetHudTargetRange(Scalar range);
|
||||
Entity *des = MECH_TARGET_ENTITY(this);
|
||||
if (des != 0)
|
||||
if (des != 0 && des != hotTarget)
|
||||
{
|
||||
// terrain pick: range to the ground point; no hotbox, no ring.
|
||||
Point3D tp = MECH_TARGET_POS(this);
|
||||
float hddx = tp.x - localOrigin.linearPosition.x;
|
||||
float hddz = tp.z - localOrigin.linearPosition.z;
|
||||
BTSetHudTargetRange((Scalar)sqrtf(hddx*hddx + hddz*hddz));
|
||||
gBTHudLockState = 0;
|
||||
}
|
||||
else if (des != 0)
|
||||
{
|
||||
Mech *dm = (Mech *)des;
|
||||
Point3D dp = dm->localOrigin.linearPosition;
|
||||
@@ -2426,7 +2441,8 @@ void
|
||||
{
|
||||
Point3D enemyPos = ((Mech *)gEnemyMech)->localOrigin.linearPosition;
|
||||
|
||||
const int designated = (MECH_TARGET_ENTITY(this) != 0);
|
||||
// damage routes ONLY when the boresight pick is the enemy mech
|
||||
const int designated = (hotTarget != 0);
|
||||
|
||||
float ddx = enemyPos.x - localOrigin.linearPosition.x;
|
||||
float ddy = enemyPos.y - localOrigin.linearPosition.y;
|
||||
@@ -2438,14 +2454,15 @@ void
|
||||
{
|
||||
gTargetLogAccum = 0.0f;
|
||||
DEBUG_STREAM << "[target] aim=(" << gBTAimX << "," << gBTAimY << ")"
|
||||
<< (hotTarget != 0 ? " HOT-aimed (zone under crosshair)"
|
||||
: (designated ? " auto-target (fire freely, body hits)" : " no living target"))
|
||||
<< (hotTarget != 0 ? " ENEMY under boresight (aimed)"
|
||||
: (pickTarget != 0 ? " terrain downrange (beam at scenery)"
|
||||
: " sky (no target, no discharge)"))
|
||||
<< " range=" << range
|
||||
<< (range <= kWeaponRange ? " IN RANGE" : "")
|
||||
<< " [aimedHits=" << gAimHits << " noRay=" << gAimNoRay
|
||||
<< " offHull=" << gAimNoPick << "]"
|
||||
<< " [enemyPicks=" << gAimHits << " groundPicks=" << gAimGround
|
||||
<< " noRay=" << gAimNoRay << "]"
|
||||
<< "\n" << std::flush;
|
||||
gAimHits = 0; gAimNoRay = 0; gAimNoPick = 0;
|
||||
gAimHits = 0; gAimNoRay = 0; gAimGround = 0;
|
||||
}
|
||||
|
||||
// --- FIRING (bring-up): on the trigger, with a target in range and the
|
||||
@@ -2502,29 +2519,14 @@ void
|
||||
gFireCooldown = kFireCooldown;
|
||||
++gShotCount;
|
||||
|
||||
// Impact point (task #36): while the crosshair is ON the target the
|
||||
// shot lands at the PICKED HULL POINT -- the STEP-6 cylinder lookup
|
||||
// resolves damage to the zone under the crosshair (aimed fire: leg
|
||||
// shots hit legs, torso shots hit the torso). Off-crosshair (the
|
||||
// sticky designation converging), the shot enters on the surface
|
||||
// toward the shooter at chest height (the unaimed synth point --
|
||||
// exactly the axis point would be angularly degenerate for the
|
||||
// cylinder sector lookup).
|
||||
Point3D impact;
|
||||
if (hotTarget != 0)
|
||||
{
|
||||
impact = hotPoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
impact = enemyPos;
|
||||
if (range > 1e-3f)
|
||||
{
|
||||
impact.x -= (ddx / range) * 3.0f; // ~torso radius toward shooter
|
||||
impact.z -= (ddz / range) * 3.0f;
|
||||
}
|
||||
impact.y += kMuzzleHeight; // chest height (beam aim height)
|
||||
}
|
||||
// Impact point (tasks #36/#41): damage happens ONLY when the enemy
|
||||
// is under the boresight (designated == hotTarget != 0, checked in
|
||||
// the gate above), so the shot always lands at the PICKED HULL
|
||||
// POINT -- the STEP-6 cylinder lookup resolves damage to the zone
|
||||
// under the boresight (aimed fire: leg shots hit legs, torso shots
|
||||
// the torso). Terrain picks never reach here (beam + scenery
|
||||
// impact only, no damage dispatch).
|
||||
Point3D impact = hotPoint;
|
||||
|
||||
Origin exp_origin = ((Mech *)gEnemyMech)->localOrigin;
|
||||
exp_origin.linearPosition = impact; // at the hit point
|
||||
|
||||
Reference in New Issue
Block a user