research(vfx): pin retail projectile and effect oracle
Establish the executable-backed PhysicsDesc, sequence-gate, PhysicsScript, CreateBlocking, particle-anchor, projectile, and Hidden-state behavior before changing runtime code. Correct stale blocking/threshold claims and synchronize the project instructions with the current UI architecture and matching retail binary. Add copyright-safe packet and DAT-container fixtures plus a failing installed-DAT conformance audit for projectile shapes, typed tables, recall motion, default scripts, and raw CreateBlocking inventory. Co-Authored-By: Codex <noreply@openai.com>
This commit is contained in:
parent
bb5acab9e6
commit
d53fe30ffe
14 changed files with 1545 additions and 53 deletions
|
|
@ -516,7 +516,7 @@ per-part transforms. Each frame can carry zero or many hooks. The full
|
|||
| `0x17` | TextureVelocity | Scrolling texture (water) |
|
||||
| `0x18` | TextureVelocityPart | per-part |
|
||||
| `0x19` | SetLight | Dynamic light on/off |
|
||||
| `0x1A` | CreateBlockingParticle | Same as CreateParticle but blocks anim progression until done |
|
||||
| `0x1A` | CreateBlockingParticle | Same payload as CreateParticle; suppresses creation while the same nonzero logical emitter ID remains live. It does not pause animation. |
|
||||
|
||||
### 5.2 Firing semantics
|
||||
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ PhysicsScriptData {
|
|||
| Hook | Fields | Role |
|
||||
|------|--------|------|
|
||||
| `CreateParticleHook` | `EmitterInfoId`, `PartIndex`, `Offset (Frame)`, `EmitterId` | Spawn a named emitter bound to a body part |
|
||||
| `CreateBlockingParticleHook` | — | Identical to CreateParticle but blocks motion-table advance until the emitter retires (used to gate spell cast sequences) |
|
||||
| `CreateBlockingParticleHook` | Same fields as CreateParticle | Suppresses creation while the same nonzero logical emitter ID remains live; it does not pause motion-table advance |
|
||||
| `StopParticleHook` | `EmitterId` | Mark emitter stopped (no new spawns, existing particles finish) |
|
||||
| `DestroyParticleHook` | `EmitterId` | Instantly despawn all remaining particles |
|
||||
| `DefaultScriptHook` | — | Used inside animation tables; implicit |
|
||||
|
|
@ -211,7 +211,7 @@ PhysicsScriptTable {
|
|||
}
|
||||
|
||||
PhysicsScriptTableData {
|
||||
List<ScriptAndModData> Scripts // sorted by Mod descending
|
||||
List<ScriptAndModData> Scripts // preserve stored DAT order
|
||||
}
|
||||
|
||||
ScriptAndModData {
|
||||
|
|
@ -221,12 +221,11 @@ ScriptAndModData {
|
|||
```
|
||||
|
||||
The server sends `GameMessageScript(objectGuid, PlayScript, mod)` to clients.
|
||||
The client picks the `Scripts` entry whose `Mod` is the largest value ≤ the
|
||||
incoming `mod`. That's how a Frost I missile and a Frost VII missile can
|
||||
share a `PlayScript.Launch` entry but show different-intensity effects: the
|
||||
spell sets mod=0.3 vs mod=0.95, and the table picks the matching
|
||||
`PhysicsScript` DID. This is the dispatch lookup done by
|
||||
`ParticleViewer.GetModIdx()` (`ACViewer/ParticleViewer.cs:46-53`).
|
||||
Retail scans the stored `Scripts` entries and returns the first entry for which
|
||||
the incoming intensity is less than or equal to `Mod`. Equality matches; the
|
||||
client neither sorts the entries nor falls back to the first. ACViewer's
|
||||
`ParticleViewer.GetModIdx()` is only an interpretation aid where it differs
|
||||
from `PhysicsScriptTableData::GetScript` (`0x00521A20`).
|
||||
|
||||
### PlayScript enum — the well-known effect namespace
|
||||
|
||||
|
|
@ -460,14 +459,14 @@ mapping is setup-specific; for humanoids the well-known indices are:
|
|||
|
||||
### Offset Frame
|
||||
|
||||
`CreateParticleHook.Offset` is a `Frame` (position + quaternion) applied
|
||||
inside the parent-part's local space. For a torch: `PartIndex=0` (torch
|
||||
mesh), `Offset=(0, 0, 0.45)` spawns the fire emitter at the top of the
|
||||
torch regardless of how the torch is held. The frame is stored in dat
|
||||
coordinates; our port multiplies it into the part-local transform with
|
||||
standard `Matrix4x4.CreateFromQuaternion(Offset.Orientation) *
|
||||
Matrix4x4.CreateTranslation(Offset.Origin)` and composes with the part's
|
||||
world matrix each frame.
|
||||
`CreateParticleHook.Offset` is encoded as a complete `Frame` (position plus
|
||||
quaternion), but retail `Particle::Init` (`0x0051C930`) consumes only its
|
||||
origin for particle emission. For a torch: `PartIndex=0` (torch mesh),
|
||||
`Offset.Origin=(0, 0, 0.45)` spawns the fire emitter at the top of the torch
|
||||
regardless of how the torch is held. Retail transforms that origin and the
|
||||
emitter's own vectors through the current parent-part/root frame; it does not
|
||||
apply `Offset.Orientation`. The quaternion is still decoded and retained for
|
||||
schema fidelity, not multiplied into the particle anchor.
|
||||
|
||||
### Retail `set_parent` call
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue