Documentation upgrade
This commit is contained in:
parent
73931629ae
commit
56f6099820
23 changed files with 1112 additions and 115 deletions
|
|
@ -41,6 +41,24 @@ Current strongest structural claims:
|
|||
- tail region around `+0x1300..+0x1318` holds runtime budget/default metadata plus the owner-resource helper pointer
|
||||
- helper attachment lives at `+0x1315/+0x1317`
|
||||
|
||||
Live runtime-helper classification added during the 2026-04-05 MCP-upgrade pass:
|
||||
|
||||
- `1420:167c Remorse::EntityVmRuntime::AcquireSlotForEntity`
|
||||
- scans the `0x80`-entry slot table for an existing entity match or the first free slot
|
||||
- can evict the currently selected slot via the owner-row iterator when the runtime needs to recycle one
|
||||
- `1420:1866 Remorse::EntityVmRuntime::InitSlotOwnerBuffers`
|
||||
- reads owner-resource metadata for one slot id
|
||||
- allocates the two slot-local working buffers later stored at `+0x1e/+0x20` and `+0x22/+0x24`
|
||||
- seeds the sentinel-filled chunk-state array used by later lazy chunk loads
|
||||
- `1420:19fd Remorse::EntityVmRuntime::EnsureSlotChunkLoaded`
|
||||
- lazily materializes one indexed owner chunk for a slot into runtime memory
|
||||
- marks the chunk present through the slot-local state arrays and updates the runtime-wide budget counters
|
||||
- `1420:1f24 entity_vm_runtime_apply_to_matching_owner_rows`
|
||||
- runtime-owned iterator over the owner list, used both for broad cleanup and for filtered slot/category work
|
||||
- `1420:2040 entity_vm_slot_entry_create_or_clear`
|
||||
- allocates or clears one `0x26`-byte slot entry record
|
||||
- gives the current strongest live evidence for the stable slot-entry size and the buffer/cache lane at `+0x1e..+0x24`
|
||||
|
||||
Current safe class role:
|
||||
|
||||
- long-lived VM root object that owns slot state, owner resource, category-base words, and runtime-wide value budgets
|
||||
|
|
@ -148,6 +166,103 @@ Why this order:
|
|||
- runtime has clear ownership over the helper and slot table
|
||||
- context has the richest semantics but also the most unresolved dispatcher behavior
|
||||
|
||||
## Live Ghidra Authoring Status
|
||||
|
||||
Verified first batch landed in the live `CRUSADER.EXE` session on 2026-04-05.
|
||||
|
||||
- Created namespace `Remorse` and class owners `Remorse::EntityVmOwnerResource`, `Remorse::EntityVmRuntime`, and `Remorse::EntityVmContext`.
|
||||
- Created provisional datatype `/Remorse/EntityVmOwnerResource` with current stable anchors:
|
||||
- `+0x10 owner_row_table`
|
||||
- `+0x14 entry_count`
|
||||
- `+0x18 entry_word_table`
|
||||
- Created provisional datatype `/Remorse/EntityVmRuntime` with size `0x1319` and only the currently stable tail anchors around the owner-resource attachment lane.
|
||||
- Created provisional datatype `/Remorse/EntityVmSlotEntry` with size `0x26` and only the currently stable tail buffer fields named:
|
||||
- `+0x1e owner_buffer_offset`
|
||||
- `+0x20 owner_buffer_segment`
|
||||
- `+0x22 chunk_state_offset`
|
||||
- `+0x24 chunk_state_segment`
|
||||
- Moved `1430:0000` under `Remorse::EntityVmOwnerResource::Create`.
|
||||
- Moved `1430:00fd` under `Remorse::EntityVmOwnerResource::Destroy`.
|
||||
- Moved `1420:1499` under `Remorse::EntityVmRuntime::Create`.
|
||||
- Moved `1420:1536` under `Remorse::EntityVmRuntime::InitSlots`.
|
||||
- Moved `1420:1575` under `Remorse::EntityVmRuntime::ReleaseSlots`.
|
||||
- Moved `1420:1601` under `Remorse::EntityVmRuntime::Destroy`.
|
||||
- Moved `1420:167c` under `Remorse::EntityVmRuntime::AcquireSlotForEntity` after live decompilation showed a `0x80`-entry scan over the runtime slot table with free-slot fallback and eviction of the currently selected slot.
|
||||
- Moved `1420:1866` under `Remorse::EntityVmRuntime::InitSlotOwnerBuffers` after live decompilation showed owner-resource reads plus the two slot-local buffer allocations and initial sentinel fill.
|
||||
- Moved `1420:19fd` under `Remorse::EntityVmRuntime::EnsureSlotChunkLoaded` after live decompilation showed per-slot chunk materialization and cache-presence marking.
|
||||
- Renamed `1420:1cca` to `entity_vm_runtime_debug_dump_slot_memory` after live decompilation showed a debug-gated walk of the runtime slot list with slot memory usage output.
|
||||
- Renamed `1420:1f24` to `entity_vm_runtime_apply_to_matching_owner_rows` after live decompilation showed filtered iteration over the runtime owner-row list.
|
||||
- Renamed `1420:2040` to `entity_vm_slot_entry_create_or_clear` after live decompilation showed allocation and zeroing of one `0x26`-byte slot record.
|
||||
- Added short decompiler comments at `1430:0000` and `1430:00fd` to preserve the raw `000d:7000` / `000d:70fd` provenance.
|
||||
- Added short decompiler comments at `1420:1499`, `1420:1536`, `1420:1575`, and `1420:1601` to preserve the runtime-lifecycle provenance and current layout claims.
|
||||
- Added short decompiler comments at `1420:167c`, `1420:1866`, `1420:19fd`, `1420:1cca`, `1420:1f24`, and `1420:2040` so the slot-table evidence stays visible in the live database.
|
||||
- Repaired the decompiler health of `1420:1499 Remorse::EntityVmRuntime::Create` after the delete/recreate cycle left it throwing `Low-level Error: Symbol $$undef00000006 extends beyond the end of the address space`; the root cause was the shared allocator helper at `1000:42e2`, whose pointer-return signature decompiled with a hidden `__return_storage_ptr__` and poisoned the caller stack model until it was normalized to an explicit `dword` return plus explicit stack storage.
|
||||
- Verified second batch landed in the live `CRUSADER.EXE` session on 2026-04-06.
|
||||
- Moved `1420:0eec` under `Remorse::EntityVmContext::CreateFromSlotIndex`.
|
||||
- Moved `1420:10b6` under `Remorse::EntityVmContext::FreeBuffer`.
|
||||
- Moved `1420:10da` under `Remorse::EntityVmContext::SyncGlobalValueAndDispatch`.
|
||||
- Moved `1420:1162` under `Remorse::EntityVmContext::Destroy`.
|
||||
- Moved `1420:118f` under `Remorse::EntityVmContext::Save`.
|
||||
- Moved `1420:1278` under `Remorse::EntityVmContext::Load`.
|
||||
- Added short decompiler comments at `1420:0eec`, `1420:10b6`, `1420:10da`, `1420:1162`, `1420:118f`, and `1420:1278` to preserve the raw `000d:46ec`, `000d:48b6`, `000d:48da`, `000d:4962`, `000d:498f`, and `000d:4a78` provenance after the class-owner move.
|
||||
- Verified third batch landed through local PyGhidra on 2026-04-06 after the live `run_write_script(...)` route still returned `404 No context found for request` against the active GUI session.
|
||||
- Created provisional datatype `/Remorse/EntityVmContext` with size `0x124` and the currently safest stable anchors:
|
||||
- `+0x32 slot_index`
|
||||
- `+0x34 value_add_offset`
|
||||
- `+0xd6/+0xd8 source_stream_offset/source_stream_segment`
|
||||
- `+0x10c/+0x10e derived_pair_lo/derived_pair_hi`
|
||||
- `+0x117/+0x119 owner_source_offset/owner_source_segment`
|
||||
- `+0x123 busy_flag`
|
||||
- Updated `1420:2040 entity_vm_slot_entry_create_or_clear` to `EntityVmSlotEntry * __cdecl16far entity_vm_slot_entry_create_or_clear(EntityVmSlotEntry * slot_entry)` so the slot-record helper no longer falls back to anonymous `byte *` parameters.
|
||||
- Updated `1420:167c Remorse::EntityVmRuntime::AcquireSlotForEntity` to return `EntityVmSlotEntry *`, while leaving the third `param_3` entity-like pointer conservative until caller-side role recovery is tighter.
|
||||
- Updated `1420:1866 Remorse::EntityVmRuntime::InitSlotOwnerBuffers` so the third parameter is now `EntityVmSlotEntry * slot_entry`.
|
||||
- Updated `1420:1536 Remorse::EntityVmRuntime::InitSlots` to `void __cdecl16far InitSlots(EntityVmRuntime * this)`.
|
||||
- Updated `1420:1575 Remorse::EntityVmRuntime::ReleaseSlots` to `void __cdecl16far ReleaseSlots(EntityVmRuntime * this)`.
|
||||
- Tried the same typed-`this` collapse on `1420:1499 Remorse::EntityVmRuntime::Create`, but the pointer-sized `this` variant reintroduced a hidden `__return_storage_ptr__`; the function was restored immediately to the known-good split-word custom-storage signature `dword __cdecl16far Create(word this, word runtime_segment, word owner_type, word owner_id)`.
|
||||
- Verified fourth live batch landed on 2026-04-06.
|
||||
- Updated the `local_a` decompiler local in `1420:19fd Remorse::EntityVmRuntime::EnsureSlotChunkLoaded` to `EntityVmSlotEntry *`, so the slot-entry cache path now renders the stable `owner_buffer_offset` and `chunk_state_offset` fields directly instead of anonymous `undefined4` pairs.
|
||||
- Retried the `EntityVmContext` lifecycle typing pass through live MCP. `apply_class_layout` dry-run for `/Remorse/EntityVmContext` now returns a normal structured preview instead of the earlier null failure, but the real apply path still fails with `Failed to apply this type: Storage size does not match data type size: 2`, and direct live `set_function_this_type` calls on `FreeBuffer`, `SyncGlobalValueAndDispatch`, `Destroy`, `Save`, and `Load` hit the same storage-size mismatch.
|
||||
- Retried live `run_write_script(...)` with and without explicit target selectors on `CRUSADER.EXE`, but the route still returned `404 No context found for request`, so there is still no live in-session fallback for forcing the dynamic-storage rewrite on the context methods.
|
||||
- Verified fifth batch landed through local PyGhidra on 2026-04-06 after the live write-side routes still blocked the context pass.
|
||||
- Updated `1420:0eec Remorse::EntityVmContext::CreateFromSlotIndex` so the first parameter is now `EntityVmContext * this` while preserving the existing `UsecodeProcess *` return type until the constructor/factory semantics are tighter.
|
||||
- Updated `1420:10b6 FreeBuffer`, `1420:10da SyncGlobalValueAndDispatch`, `1420:1162 Destroy`, `1420:118f Save`, and `1420:1278 Load` so the first parameter is now `EntityVmContext * this` instead of `UsecodeProcess *`.
|
||||
- That local fallback confirms the newer dynamic-storage rewrite is sufficient for the context lifecycle cluster when applied outside the live GUI session. The remaining MCP issue is deployment/session parity, not whether the typing model itself works.
|
||||
- Verified sixth analysis-only live batch on 2026-04-06.
|
||||
- Exercised the new storage-aware prototype route against the two known 16-bit repair cases (`1000:42e2` and `1420:1499`) through the active MCP session. The checked-in source has the new route wiring, but the live GUI plugin still answered with legacy behavior: `/set_function_prototype_storage` returned the old `set_function_prototype` failure body, and `/set_storage_aware_prototype` returned `404 No context found for request`. That confirms the remaining issue is live deployment parity, not endpoint design.
|
||||
- Rechecked the direct callers of `CreateFromSlotIndex`: `Usecode_ItemCallEvent` plus two `Interpreter_NextUsecodeOp` call sites. The `Usecode_ItemCallEvent` path explicitly calls `CreateFromSlotIndex((EntityVmContext *)0x0,0,...)` as an allocate-and-return factory, and the current caller-side uses immediately consume only base `Process`-style fields such as `procid` and termination flags. The two interpreter call sites likewise just store the returned far pointer in `DX:AX` scratch pairs before later base-process handling.
|
||||
- That caller evidence is enough to keep the current conservative return type for now: `CreateFromSlotIndex` is clearly manufacturing an `EntityVmContext`, but promoting the return to `EntityVmContext *` before the inheritance/base-process datatype story is explicit would probably make current caller decompilation less clear rather than more clear.
|
||||
|
||||
Current live datatype state:
|
||||
|
||||
- `/Remorse/EntityVmOwnerResource` is the cleanest landed class in this lane so far.
|
||||
- `/Remorse/EntityVmRuntime` currently only freezes the stable tail fields and helper pointer, not the full slot-entry schema.
|
||||
- `/Remorse/EntityVmSlotEntry` now exists as a bounded helper datatype, but only the stable tail buffer-pair fields are named so far.
|
||||
- `/Remorse/EntityVmContext` now exists and matches the current owned lifecycle cluster, but it still only records the safest field anchors rather than the full embedded mini-VM layout.
|
||||
- `apply_class_layout` succeeded for `Remorse::EntityVmOwnerResource` but failed for `Remorse::EntityVmRuntime` when the binder tried to apply a `this` type, even though plain ownership moves worked.
|
||||
- The old `apply_class_layout` dry-run null failure for `Remorse::EntityVmContext` no longer reproduces on the current live server, but the actual write-side `this` typing path is still effectively old-build behavior: the real apply and direct `set_function_this_type` calls still fail on the existing `UsecodeProcess *` lifecycle signatures with `Storage size does not match data type size: 2`.
|
||||
- The `EntityVmContext` lifecycle signatures are now locally repaired through PyGhidra: `CreateFromSlotIndex` plus `FreeBuffer` / `SyncGlobalValueAndDispatch` / `Destroy` / `Save` / `Load` all carry `EntityVmContext * this` as their first parameter.
|
||||
- `CreateFromSlotIndex` should still keep its conservative `UsecodeProcess *` return type for the moment. The allocate-and-return behavior is clear, but the known callers currently consume it through base-process fields, and the repo does not yet have an inheritance-aware `EntityVmContext : UsecodeProcess` datatype model that would make a promoted return cleaner across the call sites.
|
||||
- The runtime lane is now split more accurately: `InitSlots` and `ReleaseSlots` can carry a direct `EntityVmRuntime * this`, while `Create` still needs the split-word custom-storage form to avoid hidden return-storage breakage.
|
||||
- The first slot-entry prototype batch is tighter now that `EnsureSlotChunkLoaded` carries a real `EntityVmSlotEntry *` local on the acquired-slot path, but the wider slot-entry model is still improved rather than finished.
|
||||
|
||||
Current scope of that batch stayed intentionally conservative:
|
||||
|
||||
- no final source-format schema naming for the owner rows
|
||||
- no speculative promotion of additional seg069/070 helper callbacks into owned methods yet
|
||||
- no speculative promotion of the masked-create wrapper ladder into `EntityVmContext` methods
|
||||
- no speculative typing yet for the entity-like pointer parameter on `AcquireSlotForEntity`
|
||||
- no attempt yet to force slot-entry field names beyond the stable `+0x1e..+0x24` tail region and the current conservative helper prototypes
|
||||
|
||||
Best immediate next moves after this landed:
|
||||
|
||||
- inspect `EnsureSlotChunkLoaded` and adjacent `1420:` helpers again now that `AcquireSlotForEntity` returns `EntityVmSlotEntry *`, and push the slot-entry type one step deeper only where the resulting local/object read is genuinely clearer
|
||||
- decide whether `CreateFromSlotIndex` can safely promote its return type from `UsecodeProcess *` to `EntityVmContext *`, or whether it should stay a factory-style bridge that only types `this`
|
||||
- if the context/base-process inheritance story becomes explicit in datatypes, revisit `CreateFromSlotIndex` return typing then; until that point, keep the current `UsecodeProcess *` return even though the body itself clearly builds an `EntityVmContext`
|
||||
- recover a storage-aware `this`-typing path for `Create` specifically; `InitSlots` and `ReleaseSlots` no longer need to stay in the unresolved set
|
||||
- redeploy or otherwise verify the live storage-fallback `set_function_this_type` / `apply_class_layout` build, then retry the `EntityVmContext` lifecycle typing pass in-session before dropping back to local PyGhidra
|
||||
- identify one or two additional strongly owned runtime or owner-resource helpers if the live session exposes them cleanly
|
||||
- keep the masked-create hub and offset-specialized wrapper ladder outside the class until caller-side role recovery is tighter
|
||||
|
||||
## Source-Emission Guidance
|
||||
|
||||
If emitted as provisional C++ later, safest early skeleton is:
|
||||
|
|
|
|||
306
docs/movable-wall-trigger-investigation.md
Normal file
306
docs/movable-wall-trigger-investigation.md
Normal file
|
|
@ -0,0 +1,306 @@
|
|||
# Movable Wall Trigger Investigation
|
||||
|
||||
This note records the current evidence for movable walls that do not advertise their opener through the viewer's older helper-arrow rules.
|
||||
|
||||
## Result
|
||||
|
||||
The checked cases fit one local cluster pattern:
|
||||
|
||||
- a family-4 usecode trigger egg sits beside the wall
|
||||
- a nearby `0x04B1` CMD_LINK helper uses the egg id from `mapNum` as its `QLo` link byte
|
||||
- that helper is the practical local opener for the wall face
|
||||
|
||||
The viewer now draws that cluster as two links when the pattern is present:
|
||||
|
||||
- egg -> CMD_LINK
|
||||
- CMD_LINK -> movable wall
|
||||
|
||||
The rule is intentionally narrow. It only applies to checked `TRIGEGG` / `ONCEEGG` style eggs when a nearby CMD helper and one of the verified movable-wall target shapes are both present.
|
||||
|
||||
## Checked Cases
|
||||
|
||||
### Map 13
|
||||
|
||||
- wall: `fixed:3964`
|
||||
- visible wall face near `49790, 50206, 0`
|
||||
- nearby trigger egg: `fixed:3970`, shape `0x0011`, `mapNum = 5`, near `49794, 50598, 0`
|
||||
- second nearby trigger egg with the same egg id: `fixed:3955`, shape `0x0011`, `mapNum = 5`, near `49604, 50516, 0`
|
||||
- stacked CMD helpers in the same local lane:
|
||||
- `fixed:3972`, shape `0x04B1`, near `49790, 50558, 0`, `mapNum = 38`, `npcNum = 155`, `quality = 0x0105`
|
||||
- `fixed:3956`, shape `0x04B1`, near `49790, 50558, 4`, `mapNum = 46`, `npcNum = 155`, `quality = 0x0005`
|
||||
- `fixed:3976`, shape `0x04B1`, near `49790, 50558, 8`, `mapNum = 134`, `npcNum = 208`, `quality = 0x0005`
|
||||
|
||||
This is still the cleanest same-column movable-wall cluster, but the deeper map-13 pass shows that it is not just one egg wired straight to one wall.
|
||||
|
||||
#### Map 13: Proven Trigger Chain
|
||||
|
||||
The currently proven chain is:
|
||||
|
||||
- avatar crosses the hidden family-4 `TRIGEGG` footprint for egg id `5`
|
||||
- leaving that footprint runs `TRIGEGG::unhatch`, not `hatch`
|
||||
- `TRIGEGG::unhatch` spawns `TRIGGER.slot_20(..., 0x81, ...)`
|
||||
- `TRIGGER.slot_20` scans nearby `0x04B1` CMD helpers and keeps only the phase-1 lane (`mapNum & 8`)
|
||||
- in this local stack, that selects `fixed:3956`
|
||||
- the selected CMD then routes into the item-targeting trigger worker path
|
||||
|
||||
So the wall-opening logic for `fixed:3964` is definitely tied to the `0x81` unhatch path, meaning the authored event fires when the player leaves the trigger region rather than when entering it.
|
||||
|
||||
#### Map 13: Why This Still Looks Like A Set Piece
|
||||
|
||||
Several nearby placements suggest a more specific authored sequence than a simple hidden strip trigger:
|
||||
|
||||
- there are two separate `TRIGEGG` items using the same egg id `5` in the same local area
|
||||
- there are three stacked `0x04B1` helpers at the same `x/y` with different `z` values and different encoded payloads
|
||||
- nearby helper/door-like pieces also sit in the same cluster, including shape `1278` and several `353` / `354` objects
|
||||
|
||||
That combination looks more like a local puzzle or staged control cluster than a generic walk-near-door opener.
|
||||
|
||||
#### Map 13: Current Open Gap
|
||||
|
||||
The last mutation step is still unresolved, but the current gap is narrower than before.
|
||||
|
||||
The relevant phase-1 CMD path does reach `TRIGGER.slot_23`, and the selected helper in the checked stack (`fixed:3956`, `mapNum = 46`, `quality = 0x0005`) now decompiles cleanly enough to read its top-level lane. Its `mapNum & 3` mode selects `slot_23` case `2`, and that case is not a hidden mutation body. It is currently just a scan over nearby decoded target-shape items with two filters:
|
||||
|
||||
- `Item.getQLo(item) == baseLink`
|
||||
- `ref != item`
|
||||
|
||||
and no direct action on the matches.
|
||||
|
||||
That point is now supported by both the repaired high-level pseudocode and the lower semantic-layer output for `TRIGGER.slot_23`, `slot_24`, and `slot_26`: all three families still reduce their case-`2` lane to a bodyless match scan.
|
||||
|
||||
For the checked map-13 cluster, that scan target is now known more precisely:
|
||||
|
||||
- `fixed:3956` and its phase-0 sibling `fixed:3972` both decode to target shape `0x019B` (`shape:411`)
|
||||
- the only nearby non-CMD object with low-byte link `5` is `fixed:3968`, a `shape:411` helper-geometry record at `49918, 50238, 48`
|
||||
- the upper stacked helper `fixed:3976` decodes to target shape `0x04D0` (`shape:1232`), but there are no nearby `shape:1232` targets in this local cluster at all
|
||||
|
||||
So the local `QLo = 5` objects around the wall reduce to four items:
|
||||
|
||||
- `fixed:3972` phase-0 CMD helper
|
||||
- `fixed:3956` phase-1 CMD helper
|
||||
- `fixed:3976` upper CMD helper with no local target
|
||||
- `fixed:3968` helper-geometry target
|
||||
|
||||
Current best interpretation:
|
||||
|
||||
- the local trigger source is known
|
||||
- the relevant phase-1 helper is known
|
||||
- the selected phase-1 helper now looks like an intentional scan-only selector aimed at helper target `fixed:3968`, not a direct wall mutator
|
||||
- the upper stacked helper currently looks inactive for this cluster because it has no local `shape:1232` target to scan
|
||||
- that makes the final wall mutation for `fixed:3964` more likely to live in a sibling helper, another usecode family that reacts to the scanned helper-geometry target, or a different edge/phase of the local trigger cluster, not inside this specific case-2 body
|
||||
|
||||
So map 13 should still be documented as a verified trigger cluster with an unresolved final mutation, but the strongest current read is no longer “empty loop probably means bad decompilation.” The stronger read is “the chosen phase-1 CMD helper appears to be a deliberate scan-only lane.”
|
||||
|
||||
#### Map 13: Current Best Gameplay Trigger Path
|
||||
|
||||
Even without the final helper-consumer body, the local egg geometry now narrows the likely player action down substantially.
|
||||
|
||||
The hidden family-4 trigger eggs beside the wall form an ordered west-to-east run by egg id:
|
||||
|
||||
- `fixed:3910` = egg id `1`
|
||||
- `fixed:3945` = egg id `2`
|
||||
- `fixed:3944` = egg id `3`
|
||||
- `fixed:3959` = egg id `4`
|
||||
- `fixed:3955` = egg id `5`
|
||||
- `fixed:3970` = egg id `5`
|
||||
|
||||
The last two id-`5` eggs are narrow strips that sit directly in the northbound approach lane for wall `fixed:3964`.
|
||||
|
||||
- `fixed:3955` covers `x = 49540..49668`, `y = 50196..50836`
|
||||
- `fixed:3970` covers `x = 49730..49858`, `y = 50278..50918`
|
||||
- wall `fixed:3964` sits at `49790, 50206`, which lines up with the eastern strip and lies just beyond its north boundary
|
||||
|
||||
That means the most likely gameplay action is:
|
||||
|
||||
- approach the wall from the south side, not from the north
|
||||
- enter the eastern id-`5` strip aligned with the wall face
|
||||
- keep moving north toward the wall until the avatar leaves that strip across its north edge
|
||||
|
||||
That northbound exit is the exact movement that matches the proven `TRIGEGG::unhatch -> TRIGGER.slot_20(..., 0x81, ...) -> fixed:3956` chain.
|
||||
|
||||
So the current best practical trigger instruction is not just “walk through an egg.” It is “come up from the south, get into the narrow id-`5` lane in front of the wall, and cross out of that lane northward toward the wall.”
|
||||
|
||||
What is still not closed is whether the earlier id-`1..4` strips are mandatory setup for the wall or just sibling lanes in the same authored corridor. The current code evidence proves the final northbound id-`5` exit, but does not yet prove that ids `1..4` must be traversed first.
|
||||
|
||||
For gameplay testing, the screen-space direction should be stated more clearly:
|
||||
|
||||
- decreasing world `y` is not plain screen-up; in this isometric projection it reads as up-right on the screen
|
||||
- increasing world `x` with roughly stable `y` reads as rightward on the screen
|
||||
|
||||
So the current best practical route on screen is:
|
||||
|
||||
- sweep left-to-right across the lower hidden trigger corridor for ids `1 -> 2 -> 3 -> 4`
|
||||
- continue a little farther right into the first narrow id-`5` strip (`fixed:3955`)
|
||||
- drift slightly down-right into the second narrow id-`5` strip (`fixed:3970`)
|
||||
- then cut up-right out of that second strip toward the wall face at `fixed:3964`
|
||||
|
||||
This wording matches the actual screen anchors in the authored scene cache:
|
||||
|
||||
- id `4` strip anchor: `(15555, 11793)`
|
||||
- first id `5` strip anchor: `(15645, 11796)`
|
||||
- second id `5` strip anchor: `(15672, 11830)`
|
||||
- wall face anchor: `(15741, 11781)`
|
||||
|
||||
That makes the final move leg visually specific: from the lower-left side of the wall, move up-right toward the wall after entering the second id-`5` strip.
|
||||
|
||||
#### Map 13: Practical Clarifications
|
||||
|
||||
##### Do the wall shooters matter?
|
||||
|
||||
Current best read: probably not for the wall opener itself.
|
||||
|
||||
The visible wall launcher near this setup is `fixed:3958` at `49246, 50942, 48`. Its low quality byte is `7`, while the checked movable-wall trigger lane uses local link ids `1..5` and the final wall-facing lane uses id `5`.
|
||||
|
||||
Nearby authored objects support that split:
|
||||
|
||||
- wall lane helpers: `fixed:3972`, `fixed:3956`, `fixed:3976` with local ids centered on the id-`5` corridor
|
||||
- wall-facing helper target: `fixed:3968` with low byte `5`
|
||||
- launcher object: `fixed:3958` with low byte `7`
|
||||
|
||||
No nearby id-`7` trigger egg or id-`7` `0x04B1` controller helper has been found in the same local corridor. So the current best interpretation is that the launcher is a sibling trap lane, not the wall-opening lane.
|
||||
|
||||
That means destroying the launcher should not be expected to disable the wall trigger path. This is still an evidence-backed inference rather than a fully closed runtime proof, but the current local link data points strongly that way.
|
||||
|
||||
##### Can the wall-trigger sequence be retried?
|
||||
|
||||
On the trigger side, yes.
|
||||
|
||||
The last two wall-facing id-`5` eggs are plain `TRIGEGG` subtype eggs (`quality low byte = 0`), not `ONCEEGG`. The live egg-hatcher runner sets the internal hatched flag when the avatar enters the trigger footprint and clears it again on exit, calling `hatch` and `unhatch` on those boundary crossings.
|
||||
|
||||
So the trigger geometry is inherently reusable:
|
||||
|
||||
- enter the strip -> `hatch`
|
||||
- leave the strip -> `unhatch`
|
||||
- walk back in and out again -> the same boundary logic can fire again
|
||||
|
||||
Also, the selected phase-1 helper `fixed:3956` uses `mapNum = 46`, which does not set the `0x10` low-priority self-clear bit in `TRIGGER.slot_20`. So the helper itself does not look one-shot from the currently recovered routing path.
|
||||
|
||||
The remaining caveat is only the still-unresolved final consumer after the scan lane. The trigger side is repeatable; the last unclosed world-change side might still have an authored one-shot elsewhere, but there is no current evidence for that in the id-`5` egg path itself.
|
||||
|
||||
##### How do the last two id-`5` eggs work if they are lines?
|
||||
|
||||
They are narrow trigger strips, not square areas.
|
||||
|
||||
For both wall-facing id-`5` eggs, `npcNum = 4`, which decodes to:
|
||||
|
||||
- `xRange = 0`
|
||||
- `yRange = 4`
|
||||
- `worldXRange = 0`
|
||||
- `worldYRange = 256`
|
||||
- `zWindow ~= +/- 48`
|
||||
|
||||
So in world-space they are centered on a single `x` line and extend along `y`. In the isometric screen projection that reads as a narrow diagonal strip running roughly down-left / up-right on screen.
|
||||
|
||||
The runner compares the avatar footprint against that trigger window, not just a single point. So a zero-width `xRange` does not mean the player has to hit one impossible exact pixel. It behaves like a thin line trigger that the avatar body can cross.
|
||||
|
||||
Practical movement consequence:
|
||||
|
||||
- if you move along the strip, you stay inside it and nothing new happens after the first entry
|
||||
- if you cross into it, `hatch` fires
|
||||
- if you cross back out of it, `unhatch` fires
|
||||
|
||||
For the wall case, the proven useful event is the `unhatch` on the second id-`5` strip. So the movement that matters is not "stand in the line" but "pass through the line and leave it on the wall-facing side."
|
||||
|
||||
##### Are ids `1..4` actually required?
|
||||
|
||||
Current best read: no, they do not look required.
|
||||
|
||||
This is now supported by the recovered trigger code and the authored local lane layout.
|
||||
|
||||
Locally, ids `1..4` are four copies of the same controller pattern:
|
||||
|
||||
- each egg is a plain `TRIGEGG` subtype egg
|
||||
- each one routes into a pair of nearby `0x04B1` helpers with the same `mapNum` values as the id-`5` lane (`38` at `z=0`, `46` at `z=4`)
|
||||
- each helper pair targets exactly one nearby `shape:411` helper-geometry object whose `QLo` matches the egg id
|
||||
|
||||
The code path for those helpers does not show any accumulating setup behavior.
|
||||
|
||||
- `TRIGEGG::hatch` and `unhatch` temporarily rewrite the egg item's local `QLo` to the egg id and call `TRIGGER.slot_20`
|
||||
- `TRIGGER.slot_20` carries that id as the current `baseLink`
|
||||
- the selected `slot_21 -> slot_23 case 2` path for ids `1..5` is a scan-only lane over nearby matching targets
|
||||
- that lane returns `process_result = baseLink` unchanged
|
||||
- none of the id-`1..4` helpers use the add/subtract cases that would roll the link id forward to the next lane
|
||||
- none of the id-`1..4` helpers set the `mapNum & 16` self-clear bit either
|
||||
|
||||
So crossing ids `1..4` does not currently show any recovered mechanism that would arm id `5`, advance a shared link counter, or mutate a helper object.
|
||||
|
||||
The strongest current interpretation is:
|
||||
|
||||
- ids `1..4` are sibling scan lanes in the same authored corridor
|
||||
- they are not the required setup for wall `fixed:3964`
|
||||
- the only lane that still lines up with the wall is the paired id-`5` trigger near the wall face
|
||||
|
||||
So for practical gameplay testing, ids `1..4` can be dropped from the step list. If the wall opens at all through this local trigger cluster, the critical movement should be the paired id-`5` strip crossing beside the wall, not a full `1 -> 2 -> 3 -> 4 -> 5` sweep.
|
||||
|
||||
#### Map 13: Door-Family Closure And Remaining Gap
|
||||
|
||||
The wall face itself is now better closed than the opener.
|
||||
|
||||
- `fixed:3964` is shape `0x01AB`
|
||||
- recovered `DOOR.slot_21` handles shape `0x01AB` by dispatching `DOOR2.slot_2C`
|
||||
- `DOOR2.slot_2C` is the actual open animation for this wall family: it advances frames `1..11`, then changes the item to shape `0x0215`
|
||||
- shape `0x0215` is a non-solid, non-drawn terrain state in the exported reference data, so this is a real hidden-wall-open transition rather than just a cosmetic frame swap
|
||||
|
||||
So the unresolved part is no longer what the wall does when opened. The unresolved part is still the caller that reaches `DOOR.slot_20 -> DOOR.slot_21 -> DOOR2.slot_2C` for this specific wall.
|
||||
|
||||
The most important newly ruled-out direct-caller families on map 13 are:
|
||||
|
||||
- no controller-family object near this wall room with local `QLo = 202`
|
||||
- no family-4 egg on map 13 whose encoded egg id is `202`
|
||||
- no nearby `SECRTEGG` or `DOOREGG` close enough to target this wall through the recovered secret-door scan radius
|
||||
- no nearby watcher / secret-door-post family object in the wall room
|
||||
|
||||
That leaves the current state as:
|
||||
|
||||
- the local id-`5` trigger strip is proven to fire a real local trigger lane
|
||||
- the wall face is proven to have a real hidden-door open path
|
||||
- the exact authored bridge between those two facts is still not closed from the current recovered data
|
||||
|
||||
So the exact player steps for opening `fixed:3964` are still unresolved. The current local egg/CMD corridor remains evidence-bearing, but it can no longer be promoted to a verified opener sequence.
|
||||
|
||||
### Map 1
|
||||
|
||||
- wall: `fixed:6850`
|
||||
- visible wall face near `53278, 61054, 96`
|
||||
- nearby trigger egg: shape `0x0011`, `mapNum = 17`, near `53662, 61086, 104`
|
||||
- nearby CMD helper: shape `0x04B1`, `quality = 0x0111`, near `53566, 61054, 96`
|
||||
|
||||
This case gives the strongest byte-level confirmation because the CMD helper `QLo` is `17`, which matches the egg id `mapNum = 17` exactly.
|
||||
|
||||
### Map 27
|
||||
|
||||
- wall: `fixed:6510`
|
||||
- visible wall face near `20938, 23870, 3`
|
||||
- nearby trigger egg: shape `0x0011`, `mapNum = 15`, near `19966, 23582, 0`
|
||||
- nearby CMD helper: shape `0x04B1`, `quality low byte = 15`, near `19934, 23614, 0`
|
||||
|
||||
This is the loosest of the three examples, but it still fits the same egg-id -> CMD `QLo` neighborhood pattern and falls inside the wider movable-wall cluster distance used by the overlay.
|
||||
|
||||
## Viewer Rule
|
||||
|
||||
The current implementation does not try to solve every movable wall in the game. It only draws this verified cluster:
|
||||
|
||||
- source egg class: `TRIGEGG` or `ONCEEGG`
|
||||
- match byte: egg `mapNum` == nearby CMD helper `QLo`
|
||||
- CMD helper must be item-targeting and locally near the egg
|
||||
- CMD helper must also be locally near one of the checked movable-wall face shapes
|
||||
|
||||
Current movable-wall target shape whitelist:
|
||||
|
||||
- `0x01AB`
|
||||
- `0x0393`
|
||||
- `0x03E8`
|
||||
|
||||
Those shapes came from the inspected examples above. If future investigations recover more walls using the same cluster, extend the whitelist only with checked placements.
|
||||
|
||||
## Why Not Reuse The Existing Door Rule
|
||||
|
||||
The older door overlay path expects a direct same-egg-id or same-`QLo` match against door-family targets.
|
||||
|
||||
These walls did not fit that assumption cleanly:
|
||||
|
||||
- the opener signal is carried by a nearby usecode egg id
|
||||
- the local `0x04B1` helper is the observable routing object beside the wall
|
||||
- at least one checked wall face does not expose the same `QLo` as the helper
|
||||
|
||||
That is why the viewer now treats this as a separate trigger-cluster overlay instead of quietly broadening the generic door whitelist.
|
||||
Loading…
Add table
Add a link
Reference in a new issue