Fix FIELD minimap camera pan

This commit is contained in:
gamer147
2026-08-16 10:31:01 -04:00
parent 3289948ce7
commit d4d9def8f4
9 changed files with 87 additions and 24 deletions

View File

@@ -1289,13 +1289,12 @@ Native `gfx_object_blit_d3d9@0x4774c0` clips the explicit source rectangle and r
`right<=left || bottom<=top`; mode-1's tiled blit likewise visits no children for an empty rectangle. The
port now preserves that empty draw rather than leaking the complete SO005 sheet.
#### Selected retained-object range transform — opcodes `0x229`/`0x22a`/`0x22c`/`0x22d` (2026-07-21)
#### Selected retained-object range transform — opcodes `0x229`/`0x22a`/`0x22c`/`0x22d`/`0x22f` (2026-07-21; corrected 2026-08-16)
`0x229` was formerly misclassified as a second per-object position setter. Native
`op_0x229_set_gfx_range_transform@0x423700` instead resets an embedded gfx-object record at retained-gfx
owner `+0x428`, writes `(first_handle,count)` to owner `+0x420/+0x424`, and writes operands 3..5 as that
embedded object's anchor at owner `+0x440..+0x448`. The actual per-object direct-position opcode remains
`0x22f`.
embedded object's anchor at owner `+0x440..+0x448`.
On every render frame, `gfx_range_transform_sample_frame@0x476df0` samples the embedded object's ordinary
scale/rotation/translation channels into owner matrix `+0xb5b4`. `gfx_object_composite@0x47f650`
@@ -1306,7 +1305,10 @@ post-multiplies an object's normal matrix by this shared matrix only when its ha
- `0x22b`: current axis-angle rotation (present in the native dispatch table, zero Himegari corpus calls);
- `0x22c`: current translation in pixels;
- `0x22d`: delayed/duration scale target, using the embedded object's ordinary one-shot scale channel;
- `0x22e`: delayed/duration axis-angle target (native-dispatch-only, zero Himegari corpus calls).
- `0x22e`: delayed/duration axis-angle target (native-dispatch-only, zero Himegari corpus calls);
- `0x22f`: delayed/duration translation target. Its handler loads ECX with retained-gfx owner
`ctx+0x46614`, and worker `gfx_range_transform_set_translation_target@0x472e90` writes the embedded
object's start/delay/duration fields and target matrix. Operand 1 is delay, not an object handle.
FIELD's camera helper selects handles `[1,50000)`, anchors the transform at the current camera world
coordinate `(G[0x767e],G[0x767f])`, sets translation to `(400-camera_x,300-camera_y,0)`, and applies the
@@ -1316,6 +1318,14 @@ camera helper. Across the corpus, `0x229` occurs 693 times in 309 scripts: 590 a
identity-range selections, and the two FIELD/LOOK camera selections. Correcting the contract therefore
removes spurious object-zero mutations without changing established ADV output.
FIELD's minimap-click helper at `FIELD@0xb3fe` changes the camera world coordinate, then issues two
synchronized one-shot translations: `0x22f` pans the selected world range and `0x220` moves the ordinary
minimap viewport-marker object. The former was previously mistaken for a direct position write to ordinary
object handle 0, so the marker moved immediately while the world stayed fixed until the next camera-helper
rebuild snapped it to the new coordinate. Routing `0x22f` to the embedded range translation channel restores
the shared pan duration and keeps both representations aligned. The user confirmed the natural FIELD minimap
path now pans the world and viewport marker together on 2026-08-16.
### Blend & transparency — colorkey + `0x202`/`0x203` color/alpha (2026-07-08)
Reversed for graphics slice A (spec `docs/superpowers/specs/2026-07-08-blend-transparency-design.md`;
@@ -1430,7 +1440,7 @@ annotated in Ghidra, saved.
**In scope (built this slice):**
| op | handler / worker | semantics |
|---|---|---|
| `0x22f` | `gfx_op_0x22f_set_position_anim` → `gfx_worker_set_translation` | set object **position** (translation vec `obj+0x5d4`); base transform, not a ping-pong channel. Operand 2 is also retained as channel control state; the port currently ignores that field. |
| `0x22f` | `op_0x22f_set_gfx_range_translation_target` → `gfx_range_transform_set_translation_target` | set the shared selected-range **translation target** with independent delay/duration (embedded object target matrix `owner+0x5d4` = `obj+0x1ac`); FIELD pairs it with the minimap marker's ordinary-object `0x220` tween. |
| `0x229` | `op_0x229_set_gfx_range_transform` → `gfx_range_transform_reset` / `select_handles` / `set_anchor` | reset/select the shared **retained-object range transform**; not a per-object position setter (superseded finding above) |
| `0x239` | `gfx_op_0x239_set_srcrect_cell` → `gfx_worker_set_srcrect_cell` | one-shot **spritesheet-cell** channel: delay/duration `obj+0x48/+0x5c`, total frames/columns `obj+0x238/+0x23c`, target frame `obj+0x234` |
| `0x231` | `gfx_op_0x231_anim_srcrect` → `gfx_worker_anim_srcrect` | looping **spritesheet-cell** channel: milliseconds per frame `obj+0x230`, total frames `obj+0x238`, columns `obj+0x23c`; row-major and wraps, not ping-pong |

View File

@@ -1024,10 +1024,10 @@ Implemented through IHost.PlayModalMovieToSurface. Its operand uses the same nat
- **grounding:** source=investigation, confidence=high
- **evidence:** Ghidra /v2: op_0x22d_set_gfx_range_scale_target@0x423990 divides operands 3..5 by 100 and calls gfx_range_transform_set_scale_target@0x472d50. The worker arms the embedded transform object's ordinary scale channel (delay obj+0x3c, duration +0x50, target matrix +0xac), which gfx_range_transform_sample_frame@0x476df0 samples before range composition. FIELD has the sole corpus call, a 300 ms camera zoom.
### 0x22f `set-gfx-position-current` (u00421DD0, argc 5)
- **summary:** (handle)(control)(x)(y)(z) — set the object's current/base translation matrix. The native worker also retains operand 2 as channel control state; the port currently applies x/y/z but ignores that control operand.
### 0x22f `set-gfx-range-translation-target` (u00421DD0, argc 5)
- **summary:** (delay_ms)(duration_ms)(target_x)(target_y)(target_z) — animate the selected retained-gfx range transform's translation from its current matrix to the target. FIELD pairs this with an ordinary-object tween for the minimap viewport marker so both camera representations pan together.
- **grounding:** source=investigation, confidence=high
- **evidence:** Ghidra /v2: gfx_op_0x22f_set_position_anim@0x423b00 fetches x/y/z as floats and passes all five operands to gfx_worker_set_translation@0x472e90. The worker builds the current translation matrix with x/y/z and stores operand 2 in the channel record. This is distinct from the shared range transform at 0x229. The C# handler sets V24 directly and is therefore position-correct but does not yet model operand 2.
- **evidence:** Ghidra /v2 assembly: op_0x22f_set_gfx_range_translation_target@0x423b00 loads ECX with retained-gfx owner ctx+0x46614 before calling gfx_range_transform_set_translation_target@0x472e90. The worker writes owner+0x428's embedded object fields: active bit, shared start obj+0x34, delay obj+0x44, duration obj+0x58, and target translation matrix obj+0x1ac. It never resolves an ordinary object handle. FIELD@0xb4d8 supplies (0, pan_duration, target_x, target_y, 0) while the following 0x220 animates the minimap viewport marker over the same duration.
### 0x230 `reset-gfx-cyclic-animations` (u00421E70, argc 1)
- **summary:** (handle) — get or create a retained gfx object, clear its cyclic-animation active flag, and zero all five looping-channel start/period pairs without changing current/base transforms.