Pick: authentic per-instance INTERSECT mask replaces the interim gates
Instance body word 9 is the INTERSECT mask (verified live: terrain, sky, buildings and enemy replicant mechs carry 0xffffffff; the player's own mech parts, laser beams, missiles and effects carry 0). The raycast now ANDs it against the query mask from the 0x26 arm -- exactly the real board's exclusion. The min-range and hidden-until-armed gates retire: point-blank enemy hulls are targetable again, and own-ordnance exclusion is authored by the game itself. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -72,6 +72,7 @@ static int in_len = 0, in_pos = 0;
|
||||
* NULL => every weapon misfires ("boo-beep"). A real i860 answered this each
|
||||
* frame; our HLE never did. We now carry a real scene hit in the frame ack. */
|
||||
static bool sect_armed = false;
|
||||
static unsigned sect_mask = 0xffffffffu; /* query mask from the 0x26 arm */
|
||||
/* Real reticle raycast (Moller-Trumbore against the live scene, center ray). */
|
||||
static bool raycast_pick(unsigned *inst_out, unsigned *dcs_out, unsigned *gg_out,
|
||||
unsigned *geom_out, float xyz_out[3]);
|
||||
@@ -789,6 +790,7 @@ static struct VScene {
|
||||
/* game (full DPL) hierarchy: instance placement + articulation */
|
||||
std::map<unsigned, unsigned> inst_object; /* instance -> object */
|
||||
std::map<unsigned, unsigned> inst_w3; /* instance -> display mode word3 */
|
||||
std::map<unsigned, unsigned> inst_mask; /* instance -> INTERSECT mask */
|
||||
std::map<unsigned, M16> dcs_mat; /* dcs -> local matrix */
|
||||
std::map<unsigned, unsigned> dcs_parent; /* dcs_link child->parent */
|
||||
/* wire lights: dpl3-revive decode -- lmodel (type 6) / light (type 0xe)
|
||||
@@ -2113,6 +2115,11 @@ static void scene_burst(const unsigned char *p, size_t n) {
|
||||
* 2=billboard, 0/1=HIDDEN until armed (the parked player mech
|
||||
* is w3=1 pre-translocation). Stored for the assembly skip. */
|
||||
if (nb >= 20) S.inst_w3[name] = rd_u32(d + 16);
|
||||
/* INTERSECT mask @d+40 (verified live: buildings/floor/sky =
|
||||
* 0xffffffff, the player's own mech parts + beams + missiles
|
||||
* = 0). The AUTHENTIC pick exclusion: the game's sect query
|
||||
* ANDs its mask against this. */
|
||||
if (nb >= 44) S.inst_mask[name] = rd_u32(d + 40);
|
||||
for (size_t o = 8; o + 3 < nb; o += 4) {
|
||||
unsigned val = rd_u32(d + o);
|
||||
if (val && val != 0xFFFFFFFFu) {
|
||||
@@ -2196,8 +2203,11 @@ static void scene_burst(const unsigned char *p, size_t n) {
|
||||
break;
|
||||
case 38: /* set_sect_pixel: game armed the continuous reticle pick.
|
||||
* From here the board is expected to return the hit in each
|
||||
* frame reply -- that's what unblocks weapons fire. */
|
||||
* frame reply -- that's what unblocks weapons fire.
|
||||
* Payload: [xp][yp][radius][mask]; keep the query mask for
|
||||
* the per-instance INTERSECT test. */
|
||||
sect_armed = true;
|
||||
if (nb >= 16) sect_mask = rd_u32(d + 12);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -2287,27 +2297,19 @@ static bool raycast_pick(unsigned *inst_out, unsigned *dcs_out, unsigned *gg_out
|
||||
M16 world; dcs_world(di->first, cache, world);
|
||||
for (size_t ii = 0; ii < di->second.size(); ii++) {
|
||||
unsigned inst = di->second[ii];
|
||||
/* Skip hidden-until-armed instances (w3 0/1: laser beams,
|
||||
* missile models, effects). Picking them is poison: the beam
|
||||
* stretches to the pick point, the next pick hits the BEAM at
|
||||
* that point and locks the target there (feedback loop); a
|
||||
* volley's own missiles get picked and homed on; and a picked
|
||||
* missile whose DCS deletes on impact hands the game a stale
|
||||
* handle (the arena2 freeze). The reticle resolves the WORLD:
|
||||
* terrain, buildings, vehicles. */
|
||||
std::map<unsigned, unsigned>::const_iterator wi =
|
||||
S.inst_w3.find(inst);
|
||||
if (wi != S.inst_w3.end() && wi->second != 2 && wi->second != 3)
|
||||
/* AUTHENTIC pick exclusion: per-instance INTERSECT mask
|
||||
* (instance word 9) ANDed with the query mask from the 0x26
|
||||
* arm -- exactly what the real board did. The game authors it
|
||||
* for us: own mech parts, laser beams, missiles and effects
|
||||
* carry mask 0 (never picked -- no beam feedback loops, no
|
||||
* homing on own ordnance, no stale missile handles); terrain,
|
||||
* sky, buildings and ENEMY mechs carry INTERSECT_ALL, so
|
||||
* point-blank hulls are targetable again (this replaced the
|
||||
* interim min-range and hidden-until-armed gates). */
|
||||
std::map<unsigned, unsigned>::const_iterator mi =
|
||||
S.inst_mask.find(inst);
|
||||
if (mi != S.inst_mask.end() && (mi->second & sect_mask) == 0)
|
||||
continue;
|
||||
/* Own-mech exclusion is by MINIMUM RANGE, not ancestry: walking
|
||||
* S.dcs_parent (dcs_link edges) falsely flagged the FLOOR and
|
||||
* SKY as the player's subtree (links also chain sibling scene
|
||||
* DCSs in this dialect) -- ground/sky aims went no-hit and every
|
||||
* such trigger boo-beeped. The player's arms and muzzle flash
|
||||
* all live within ~12 of the eye; everything legitimately
|
||||
* targetable is farther. Sky/ground are REAL pickable entities
|
||||
* (sky.mod, afloor): firing into them fires -- and misses --
|
||||
* exactly like the period pods. */
|
||||
std::map<unsigned, unsigned>::const_iterator oi =
|
||||
S.inst_object.find(inst);
|
||||
if (oi == S.inst_object.end()) continue;
|
||||
@@ -2351,7 +2353,7 @@ static bool raycast_pick(unsigned *inst_out, unsigned *dcs_out, unsigned *gg_out
|
||||
m16_xform(world, &vv[o1], w1);
|
||||
m16_xform(world, &vv[o2], w2);
|
||||
float t = ray_tri(O, D, w0, w1, w2);
|
||||
if (t > 15.0f && t < best_t) { /* min pick range */
|
||||
if (t > 0.0f && t < best_t) {
|
||||
best_t = t; best_inst = inst;
|
||||
best_dcs = di->first; best_gg = gg; best_geo = geo;
|
||||
}
|
||||
@@ -2386,7 +2388,8 @@ static bool raycast_pick(unsigned *inst_out, unsigned *dcs_out, unsigned *gg_out
|
||||
static void scene_reset(void) {
|
||||
S.type.clear(); S.verts.clear(); S.polys.clear(); S.mat.clear();
|
||||
S.ggmat.clear(); S.children.clear();
|
||||
S.inst_object.clear(); S.inst_w3.clear(); S.dcs_mat.clear();
|
||||
S.inst_object.clear(); S.inst_w3.clear(); S.inst_mask.clear();
|
||||
S.dcs_mat.clear();
|
||||
S.dcs_parent.clear(); S.lights.clear();
|
||||
S.view = VFrame();
|
||||
S.geom_active = false; S.conn_active = false;
|
||||
|
||||
Reference in New Issue
Block a user