mirror of
https://github.com/jkdansereau/goldeneye-pc-port.git
synced 2026-10-24 00:02:45 +02:00
D321: map the D318 trigger chain (tanks -> chr 254 gas cascade -> combat bit, ~3.5s authored delay; derail same-tick; PC faithful to N64) + GE_OBJT probe extensions (AI-list ID, GAS prop timestamps)
This commit is contained in:
1 parent
b3b0ff74db
commit
75e5902717
3 files changed
+91
-1
No files matched your search
+22
-1
@@ -12124,7 +12124,7 @@ Per-tick check (hooked at the `g_GlobalTimer += g_ClockTimer` site in lv.c). Sig
|
||||
- Interactive playtest (`scratch/d318_playtest3.log`, 2026-09-19): organic derail occurred; the run drew the escape variant, so c78 broke the pin on his own and killed Trevelyan within seconds. The watchdog detected the signature twice (t=7496, t=9270) but never confirmed — both windows cleared before 600 consecutive ticks → zero false positives in live play.
|
||||
- Diagnostic probes removed (`port/src/d318probe.c`, `port/include/d318probe.h`, and the `#ifdef PORT` hooks in chrai.c/chraction.c/lv.c/model.c); only the permanent watchdog hook remains (lv.c).
|
||||
|
||||
**Status:** RESOLVED (2026-09-19). As-authored latent race documented; port-side watchdog ships by default. Cross-ref: D263 (mechanism map — stands), D202 M-66b (port-side expiration precedent).
|
||||
**Status:** RESOLVED (2026-09-19). As-authored latent race documented; port-side watchdog ships by default. Trigger chain upstream of the derail (tanks → combat bit) fully mapped in D321 — PC behavior confirmed faithful to N64. Cross-ref: D263 (mechanism map — stands), D321 (trigger chain + live validation), D202 M-66b (port-side expiration precedent).
|
||||
|
||||
## D319 — Frame cap slider was mostly a dead zone; replaced with a plain 30/60 toggle (user ask, 2026-09-18).
|
||||
|
||||
@@ -12175,3 +12175,24 @@ Verified: build clean (MSYS2 MINGW64/ninja); `tools_pc/verify.sh bunker1` — PA
|
||||
**Second-pass verification (2026-09-20, review of this entry).** All mechanism claims re-checked against source and confirmed: `chrHasStoppedOrPatroling` (chraction.c:4291) returns TRUE only for ACT_STAND/ACT_ANIM/ACT_PATROL; `actor_fire_or_aim_at_target_update` (chraction.c:4882) succeeds on ACT_ATTACK + (AIM_ONLY\|DONTTURN) and calls `chrlvAttackActionRelated`, which only ever calls `modelSetAnimEndFrame` (recoil/shoot start or end frame) — never `modelSetAnimation`. The three high/med-high lists were re-read in full: ai_19 (UsetuparkZ.c 2630–2826), ai_9 (UsetupcontrolZ.c 2279–2313), ai_12 (UsetupdepoZ.c 2174+) — structures match the table above, with the two precision corrections applied to the ai_19/ai_12 rows. **AI command-ID mapping settled:** `bondaicommands.h`'s explicit IDs are the cmdbuilder enum values from `aicommands.def` order (cross-checked on four known commands; a naive `#define CMDNAME` count is off by 2). In particular `if_guard_bitfield_is_set_on` = ID 0x96 = `AI_IFMyFlags2Has` — it tests the **executing guard's own** flags2 (`AiIFMyFlags2HasRecord {cmd, BITS, GOTOLABEL}`, handler chrai.c:3146), and `guard_bitfield_set_on` = 0x94 = `AI_SetMyFlags2`. The per-chr-targeting family is separate (0x97–0x99, `chr_bitfield_*`). `AI_TRYFireOrAimAtTarget[Kneel]` jumps to GOTOLABEL on success, falls through on failure (chrai.c:1372+). TARGET_ flag decode for the hold commands: 0x24 = CHR\|AIM_ONLY, 0x28 = PAD\|AIM_ONLY (hold poses); updates set 0x04/0x08 (live fire, no AIM_ONLY).
|
||||
|
||||
**Status:** OPEN — static sweep complete and second-pass verified; no live confirmation yet for any list beyond D318 itself. Cross-ref: D318 (mechanism + watchdog), D263 (ai_22 mechanism map).
|
||||
|
||||
## D321 — D318 trigger chain fully mapped: tanks → combat bit is an authored ~3.5 s gas-cascade delay (chr 254 script); derail lands same-tick; PC behavior confirmed faithful to N64 (probe captures, 2026-09-20).
|
||||
|
||||
**Question.** User's N64 memory: Ourumov shoots Trevelyan the moment the tanks are blown up. PC playtests looked like he waited for the countdown to finish, then paused ~10 s, then shot. D318 covered the pin + watchdog; this entry closes the upstream question — *when and why does the combat bit `0x04` land, and is the PC trigger chain faithful?*
|
||||
|
||||
**Probes (all env-gated, port-only, capped, read-only; post-release removal candidates per M-192 item 7):**
|
||||
- `GE_OBJT` in `chrSetStageFlags`/`chrUnsetStageFlags` (`chraction.c`): logs every low-16-bit write to `objectiveregisters1` with tick, writing chr, its actiontype/aioffset, and its AI-list ID (`chraiGetAIListID`).
|
||||
- `GE_OBJT` in the `PROPDEF_GAS_RELEASING` destruction handler (`propobj.c`): timestamps each tank destruction.
|
||||
- Existing `GE_D318T` timeline (`port/src/d318watchdog.c`) for correlation.
|
||||
|
||||
**Findings (three playtest captures, Facility `-level_34`):**
|
||||
1. The level setup script (`UsetuparkZ.c`) never writes the low bits of `objectiveregisters1` — only character-action scripts can, via `chrSetStageFlags`. Writers observed: chr 67 (Trevelyan) sets `0x100`, `0x02`, and `0x20` (monologue); **chr 254** (`ACT_BONDMULTI`, aiid=0x1001, off=15 — a scripted gas-cascade entity) sets **`0x40` (flee) + `0x04` (combat) together, same tick**.
|
||||
2. With tank-destruction timestamps: all 10 tanks destroyed t=7544–7634; the bits land t=7846 — **Δ = 212 ticks (~3.5 s) after the last tank**. This is a fixed ROM script delay in chr 254's cascade, identical data on the N64 — not a port-side lag.
|
||||
3. The derail at ai_22 off=75 fires **on the same tick the bit lands** (run A: t=8807→8808; run B: t=7846→7847). While c78 waits in the monologue loop at off=75 the check is re-evaluated continuously (heartbeat-verified), so there is no additional polling latency.
|
||||
4. Run B took the **natural escape**: Trevelyan died the same tick as the derail, zero `D318W` lines — i.e. "blow up the tanks → shot ~3.5 s later" is exactly what this build does, matching the user's N64 memory. Runs A and the original 09-18 report rolled the **pin branch** (random aim variant, D318 step 4), where the ~10 s felt by the user was the watchdog's 600-tick confirmation window breaking an anim pin the N64 would soft-lock on permanently.
|
||||
|
||||
**Conclusion.** No port bug; no `src/game` change warranted or needed. The perceived delay decomposes into (a) the authored ~3.5 s gas cascade + (b), only on the pin branch, the watchdog confirmation window. The PC trigger chain is faithful to the N64 end-to-end.
|
||||
|
||||
**Optional tuning (deferred, user's call).** `D318_DEADLOCK_TICKS` 600→300 would halve the worst-case pin-branch pause (10 s → 5 s); the margin over the natural escape (<2 s; run B: 0 ticks) stays ≥2.5×. One-line `#define` in `port/src/d318watchdog.c`; not done pre-ship to avoid re-validating the watchdog on the release branch.
|
||||
|
||||
**Status:** CLOSED — trigger chain mapped and faithful; D318 watchdog validated in live play (fired + recovered in run A, correctly inert in run B). Cross-ref: D318 (pin mechanism + watchdog), D320 (sweep of the same race in 8 levels), D263 (ai_22 mechanism map), M-192 (release review).
|
||||
@@ -9994,6 +9994,35 @@ bool chrHasFlags2ById(ChrRecord *self, s32 chrNum, u8 flags2)
|
||||
*/
|
||||
void chrSetStageFlags(ChrRecord *self, s32 arg1)
|
||||
{
|
||||
#ifdef PORT
|
||||
/* D318-followup (diagnosis only, GE_OBJT=1): attribute writes to the LOW
|
||||
* bits of objectiveregisters1 (the Facility execution derail triggers:
|
||||
* 0x04 gas/combat, 0x20 surrender/monologue, 0x40 flee -- none of which
|
||||
* the level setup script ever sets; only character-action scripts do).
|
||||
* Logs the writing chr + its AI position so the writer's script state is
|
||||
* correlatable against a simultaneous D318T capture. Read-only, capped,
|
||||
* no behavior change; N64 build unaffected. */
|
||||
extern char *getenv(const char *);
|
||||
extern s32 chraiGetAIListID(AIRecord *AIList, bool *isGlobalAIList); /* chrai.c */
|
||||
static int s_objt = -1;
|
||||
static int s_objtn = 0;
|
||||
int objtlog;
|
||||
s32 aid;
|
||||
bool aig;
|
||||
|
||||
if (s_objt < 0) { s_objt = getenv("GE_OBJT") != NULL; }
|
||||
objtlog = s_objt && (arg1 & 0x0000FFFF) && (s_objtn < 500);
|
||||
if (objtlog)
|
||||
{
|
||||
aid = self->ailist ? chraiGetAIListID(self->ailist, &aig) : -1;
|
||||
osSyncPrintf("OBJT: t=%d SET mask=0x%08x by chr %d (act=%d off=%d aiid=0x%04x%s ailist=%p) reg=0x%08x\n",
|
||||
(int)g_GlobalTimer, (unsigned)arg1, (int)self->chrnum,
|
||||
(int)self->actiontype, (int)self->aioffset, (unsigned)aid,
|
||||
aig ? "G" : "", (void *)self->ailist,
|
||||
(unsigned)objectiveregisters1);
|
||||
s_objtn++;
|
||||
}
|
||||
#endif
|
||||
objectiveregisters1 |= arg1;
|
||||
}
|
||||
|
||||
@@ -10003,6 +10032,28 @@ void chrSetStageFlags(ChrRecord *self, s32 arg1)
|
||||
*/
|
||||
void chrUnsetStageFlags(ChrRecord *self, u32 flags)
|
||||
{
|
||||
#ifdef PORT
|
||||
extern char *getenv(const char *);
|
||||
extern s32 chraiGetAIListID(AIRecord *AIList, bool *isGlobalAIList); /* chrai.c */
|
||||
static int s_objtu = -1;
|
||||
static int s_objtun = 0;
|
||||
int objtulog;
|
||||
s32 aid;
|
||||
bool aig;
|
||||
|
||||
if (s_objtu < 0) { s_objtu = getenv("GE_OBJT") != NULL; }
|
||||
objtulog = s_objtu && (flags & 0x0000FFFF) && (s_objtun < 500);
|
||||
if (objtulog)
|
||||
{
|
||||
aid = self->ailist ? chraiGetAIListID(self->ailist, &aig) : -1;
|
||||
osSyncPrintf("OBJT: t=%d UNS mask=0x%08x by chr %d (act=%d off=%d aiid=0x%04x%s ailist=%p) reg=0x%08x\n",
|
||||
(int)g_GlobalTimer, (unsigned)flags, (int)self->chrnum,
|
||||
(int)self->actiontype, (int)self->aioffset, (unsigned)aid,
|
||||
aig ? "G" : "", (void *)self->ailist,
|
||||
(unsigned)objectiveregisters1);
|
||||
s_objtun++;
|
||||
}
|
||||
#endif
|
||||
objectiveregisters1 = ~flags & objectiveregisters1; //shorthand does not match
|
||||
}
|
||||
|
||||
|
||||
@@ -9427,6 +9427,24 @@ apply_damage:
|
||||
{
|
||||
if (objGetDestroyedLevel(obj) == 1)
|
||||
{
|
||||
#ifdef PORT
|
||||
/* D318-followup (diagnosis only, GE_OBJT=1): timestamp each
|
||||
* GAS_RELEASING prop destruction so the tank-blow tick is
|
||||
* correlatable against the OBJT stage-flag writes. Read-only,
|
||||
* capped, no behavior change; N64 build unaffected. */
|
||||
extern char *getenv(const char *);
|
||||
static int s_gas = -1;
|
||||
static int s_gasn = 0;
|
||||
|
||||
if (s_gas < 0) { s_gas = getenv("GE_OBJT") != NULL; }
|
||||
if (s_gas && (s_gasn < 200))
|
||||
{
|
||||
osSyncPrintf("OBJT: t=%d GAS prop destroyed at (%.0f,%.0f,%.0f) [gas effect armed]\n",
|
||||
(int)g_GlobalTimer, (double)obj->runtime_pos.x,
|
||||
(double)obj->runtime_pos.y, (double)obj->runtime_pos.z);
|
||||
s_gasn++;
|
||||
}
|
||||
#endif
|
||||
init_trigger_toxic_gas_effect(&obj->runtime_pos);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user