feat(vfx): bind effects to live animated poses
This commit is contained in:
parent
96ddfdf175
commit
542dcfc384
41 changed files with 3246 additions and 741 deletions
|
|
@ -291,9 +291,30 @@ serial FIFO per owner: duplicate plays append, owners progress independently,
|
|||
not advance; a new play for an already-created cell-less object is dropped as in
|
||||
retail. Attached children advance through their eligible parent. Every effect
|
||||
resource uses the canonical, globally unique `WorldEntity.Id`; static
|
||||
allocators fail before their namespace can wrap. Live anchors are refreshed from the current
|
||||
`WorldEntity` before script dispatch; Step 5 extends that root pose to exact
|
||||
animated part transforms for emitters and dynamic lights.
|
||||
allocators fail before their namespace can wrap. `EntityEffectPoseRegistry`
|
||||
publishes the final root and indexed rigid animated-part transforms after animation
|
||||
and equipped-child composition. Animation hooks are captured during sequence
|
||||
advance and drained only after those poses are current; PhysicsScripts then run,
|
||||
attached emitters and object lights refresh, and particle simulation advances.
|
||||
World-released particles retain their birth positions and parent-local particles
|
||||
follow their current owner while in-world. Pending spatial projections skip
|
||||
particle updates and drawing exactly like retail's cell-less object gate without
|
||||
ending emitter state or identity; absolute creation timestamps remain unchanged,
|
||||
so elapsed particles/durations expire on re-entry without a backlog burst. Their
|
||||
anchors may still accept authoritative pose correction before that update.
|
||||
Missing emitter DAT records fail diagnostically
|
||||
without a synthesized effect. `LiveEntityLightController` keeps live light
|
||||
projection/re-entry outside `GameWindow`; `LightingHookSink` remains the Core
|
||||
hook and per-frame pose consumer. It also owns retail's Lighting-bit/
|
||||
`SetLightHook` latch so spatial withdrawal preserves logical state while a
|
||||
true state transition creates or destroys the Setup lights. Stable indexed
|
||||
poses exclude Setup visual scale and remain separate from drawable `MeshRefs`.
|
||||
Light registration follows final runtime visibility edges; equipped-child
|
||||
updates are parent-before-child with retained per-child buffers and cascade
|
||||
withdrawal when an ancestor pose disappears. A later pose publication drains
|
||||
the waiting attachment graph transitively, so A→B→C recovers B before C in the
|
||||
same parent-first pass. Recovery is edge-triggered by object/appearance/pose
|
||||
publication; permanent missing DAT or holding parts are never polled per frame.
|
||||
|
||||
The remaining aggregation is primarily `_playerController`'s player-specific
|
||||
movement plus the separate `WorldEntity`/animation/physics component types.
|
||||
|
|
|
|||
|
|
@ -171,7 +171,6 @@ AP-94..AP-112 for the confirmed retail-UI completion gaps.
|
|||
| AP-64 | **Wield-reject rollback assumes `InventoryServerSaveFailed 0x00A0`** — an optimistic wield rolls back only if ACE emits `0x00A0` for a `GetAndWieldItem` rejection; otherwise corrected by the next authoritative message. Gate-verify via WireMCP. | `src/AcDream.Core.Net/GameEventWiring.cs` (0x00A0 handler) | If ACE uses a different reject opcode for wield, the optimistic state is left dangling until the next full update. | Rejected wield briefly shows the item as equipped in the doll — cosmetic flicker or stuck state if `0x00A0` is not the rejection path. | `InventoryServerSaveFailed 0x00A0`; WireMCP gate-verify |
|
||||
| AP-65 | **PickupEvent (0xF74A) no longer evicts the weenie from `ClientObjectTable`** — only `DeleteObject (0xF747)` evicts, matching the retail `object_table`-vs-`weenie_object_table` split. An item another player picks up near you (only ever gets `PickupEvent`, never `DeleteObject`) lingers as a data-only entry (`ContainerId 0`, not in any view) until teleport/relog `Clear()`. | `src/AcDream.Core.Net/ObjectTableWiring.cs` (EntityDeleted handler); `src/AcDream.Core.Net/WorldSession.cs` (PickupEvent branch) | The weenie entry for nearby pickups is a harmless data ghost — no UI shows it (no container, not wielded). Retail evicts it from `weenie_object_table` when the object fully leaves interest range via `DeleteObject`; acdream defers to teleport/relog clear. | Slight memory growth in long sessions if many world items are picked up by other players near you; item data ghosts cannot cause functional issues because no UI queries `ContainerId 0`. | `CACObjectMaint::DeleteObject` / `SmartBox::HandleDeleteObject`; ACE `Player_Inventory.cs TryDequipObjectWithNetworking` |
|
||||
| ~~AP-66~~ | **RETIRED 2026-07-13 — authored paperdoll empty-slot presentation.** The earlier “no silhouettes” conclusion inspected the ItemList elements' own media but missed `UIElement_ItemList::InternalCreateItem`, which clones a distinct `UIElement_UIItem` catalog prototype for each location. All 21 supported jewelry, weapon, ammo, shield, clothing, cloak, trinket, and armor lists now resolve their exact `ItemSlot_Empty` surface from live DAT; `PostInit` confirms non-armor lists remain visible while the nine armor lists toggle with Slots. | `src/AcDream.App/UI/Layout/PaperdollSlotBackgrounds.cs`; `ItemListCellTemplate.cs`; `PaperdollController.cs` | — | — | `gmPaperDollUI::GetLocationInfoFromElementID @ 0x004A37F0`; `PostInit @ 0x004A5360`; `UIElement_ItemList::InternalCreateItem @ 0x004E3570`; `LayoutDesc 0x21000037` |
|
||||
| AP-67 | Server-spawned weenie fixtures (lanterns, braziers, glowing items carrying `Setup.Lights`) register their lights at their SPAWN-frame world position via `RegisterOwnedLight` in `OnLiveEntitySpawnedLocked` (mirroring the dat-static path in `ApplyLoadedTerrainLocked`); the light does NOT follow the object if it later moves. Cell-scoped light presentation is removed by `LeaveWorldLiveEntityRuntimeComponents` and restored on world re-entry; logical teardown uses the same leave-world tail. | `src/AcDream.App/Rendering/GameWindow.cs` (`OnLiveEntitySpawnedLocked` weenie-light block; `LeaveWorldLiveEntityRuntimeComponents`; `TearDownLiveEntityRuntimeComponents`) | Closes the prior gap where ONLY dat-baked statics registered lights, so server-placed lanterns cast nothing; the overwhelming majority of light-bearing weenies (wall lanterns, braziers, candelabra) are stationary, so spawn-frame placement matches retail; retail's `add_dynamic_light` re-places the light from the object frame each frame | A moving light-bearing weenie (e.g. a torch-carrying NPC) leaves its light at the spawn position instead of following — rare; the faithful fix is to re-place on `OnLivePositionUpdated` | retail object-borne lights `insert_light` 0x0054d1b0 / `add_dynamic_light` 0x0054d420 (per-frame object frame) |
|
||||
| AP-68 | acdream keeps the 128 nearest-to-CAMERA point lights live (`MaxGlobalLights=128`, `BuildPointLightSnapshot`) and selects per cell CAMERA-INDEPENDENTLY (by the cell's own bounds), so a building interior stays lit at any distance within a town; retail keeps only the 40 nearest-to-PLAYER static lights (`Render::max_static_lights=0x28`, distance-sorted replace-farthest `insert_light`) and re-bakes a cell when its live light set changes, so distant interiors are baked dark and "light up" only as the player approaches and their torches enter the live 40. INTENTIONAL — acdream's always-lit interiors are the preferred behavior (no 1999-era light-budget pop-in); user-confirmed 2026-06-20. | `src/AcDream.Core/Lighting/LightManager.cs` (`MaxGlobalLights=128`, `BuildPointLightSnapshot`, `SelectForObject`) | The retail pop-in is a fixed-function light-budget artifact, not an intended aesthetic; revert to retail by clamping the global set to 40 + distance-to-player sort if ever desired | Distant town interiors are lit in acdream where retail's are dark until approached — a deliberate, user-preferred divergence | `Render::max_static_lights` 0x28; `insert_light` 0x0054d1b0 (distance-sorted, replace-farthest); bake re-trigger `SetStaticLightingVertexColors` cache `burnedInStaticLights != num_static_lights` |
|
||||
| AP-69 | On a landblock unload/reload, acdream retains the accepted live record and the same `WorldEntity`. Full unload moves non-persistent live projections to that landblock's pending bucket; Near→Far demotion drops only dat-static layers and leaves live projections resident. Neither path replays logical registration or reconstructs from a stale CreateObject. ACE will NOT re-broadcast objects whose guid remains in its per-player `KnownObjects` set, matching retail's retained object-table model. DIVERGENCE: acdream has NO retail 25 s / 384 m visibility cull — the retained record is pruned ONLY by an explicit server `DeleteObject` (0xF747) or session teardown. (#138) | `src/AcDream.App/World/LiveEntityRuntime.cs`; `src/AcDream.App/Streaming/GpuWorldState.cs`; `GameWindow.RehydrateServerEntitiesForLandblock`; `StreamingController` (`onLandblockLoaded`, Loaded + Promoted) | Stable logical identity is retail-faithful and prevents duplicated meshes, scripts, emitters, or stale-spawn teleports while remaining independent of an ACE re-broadcast | An object the server silently stopped tracking WITHOUT a `DeleteObject` (e.g. a creature that wandered out of PVS) can reappear at its last-known position, where retail's 25 s cull would have dropped it — a stale ghost until a real `DeleteObject` or the next session. Close it by porting holtburger's 25 s/384 m `ACE_DESTRUCTION_TIMEOUT` self-cull | `CPhysicsObj::change_cell` 0x00513390; `CPhysicsObj::leave_world` 0x005155A0; ACE `ObjectMaint.KnownObjects` never cleared on teleport (`Physics/Common/ObjectMaint.cs`, `WorldObjects/Player_Tracking.cs`); holtburger client 25 s cull `liveness.rs` `ACE_DESTRUCTION_TIMEOUT_SECS` |
|
||||
| AP-70 | `TeleportAnimSequencer.ComputeFadeAlpha` uses a smoothstep curve for all fade states; retail's `gmSmartBoxUI::UseTime` drives fade alpha through a 1024-entry `GetAnimLevel` lookup table whose contents are unrecovered | `src/AcDream.Core/World/TeleportAnimSequencer.cs` (`ComputeFadeAlpha`) | `GetAnimLevel` table address and contents not yet extracted via cdb; smoothstep is a perceptually-reasonable S-curve that closely approximates a typical gamma-corrected fade; the visual difference is imperceptible under normal teleport conditions | Fade timing differs subtly from retail — ramp-in or ramp-out may be slightly too fast/slow at the black-fade edges; retire by reading the `GetAnimLevel` table via cdb and replacing smoothstep with a direct 1024-entry lookup (spec §8) | `gmSmartBoxUI::UseTime` 0x004d6e30; `GetAnimLevel` 1024-entry table (address unrecovered — spec §8 cdb trace) |
|
||||
|
|
@ -228,9 +227,6 @@ AP-94..AP-112 for the confirmed retail-UI completion gaps.
|
|||
| TS-7 | SkyObject `weather_enabled` gate not honored — weather-flagged sky objects (bit 0x04) always instantiate | `src/AcDream.Core/World/SkyDescLoader.cs:50` | No weather_enabled toggle exists yet; IsWeather flag parsed + documented as the gate to wire | Weather-only sky meshes (rain cylinders) appear where retail-with-weather-off suppresses them | `GameSky::MakeObject` 0x00506ee0, guard at decomp:268630 |
|
||||
| TS-8 | `MagicUpdateEnchantment` (0x02C2) records carry no StatMod — mid-session buffs don't move vital max until relog (**#7/#12**) | `src/AcDream.Core/Spells/Spellbook.cs:150` | The wire parser hasn't been extended to the full ~60-64 byte Enchantment payload; PlayerDescription's block IS parsed | Vitals HUD percent reads differently from retail for the whole session after any buff cast | `EnchantAttribute` 0x00594570; holtburger magic/types.rs |
|
||||
| TS-9 | MP3 (0x55) and MS-ADPCM (0x02) waves undecoded — affected sounds skipped; retail decoded both via winmm ACM | `src/AcDream.Core/Audio/WaveDecoder.cs:33` | Managed decoder (NAudio or similar) deferred; PCM covers the vast majority of ~3500 waves | Any MP3 (common for music-ish clips) or ADPCM cue plays as silence where retail plays it | winmm ACM path (r05 §2.1) |
|
||||
| TS-10 | Setup lights anchored at entity root — per-light Frames not transformed through the animated part chain | `src/AcDream.Core/Lighting/LightInfoLoader.cs:31` | Per-part world transforms aren't exposed to the lighting layer; awaiting animation hook integration | A carried torch glows from the character origin, not the hand, and doesn't track swing/idle animations | LightInfo.ViewSpaceLocation per-part Frame (r13 §1) |
|
||||
| TS-11 | Retail's complete inherited `CreateBlockingParticleHook` payload is decoded by the narrow `DatCollection`-backed compatibility reader, but `ParticleHookSink` deliberately no-ops it until the live emitter-binding owner can apply same-live-nonzero-logical-ID suppression | `src/AcDream.Core/Vfx/ParticleHookSink.cs` (`RetailCreateBlockingParticleHook` case); decoder `src/AcDream.Content/Vfx/RetailAnimationHookReader.cs` | Decoding and cursor alignment are now exact for both PhysicsScripts and Animations; executing it as an ordinary replacement before Step 5 would encode the wrong lifetime rule | Blocking-particle hooks still emit nothing until Step 5; ordinary hooks following them are no longer cursor-misaligned | `CreateBlockingParticleHook::Execute` 0x00526EF0; `ParticleManager::CreateBlockingParticleEmitter` 0x0051B8A0; r04 §6 |
|
||||
| TS-12 | Animated entities' emitters use rest-pose part transforms anchored at entity root; retail attaches to the live animated part (per-tick refresh deferred; statics fixed by C.1.5b/#56) | `src/AcDream.Core/Vfx/ParticleHookSink.cs:80` (+ :20) | The renderer doesn't expose per-part world transforms to VFX; root + precomputed matrices reproduce retail placement for everything that doesn't animate | Effects hooked to animated parts (swinging hand, nodding head) emit from the rest pose / float at spawn offsets instead of tracking motion | `ParticleEmitter::UpdateParticles` 0x0051d2d4 |
|
||||
| TS-14 | Setup `Flatten` ignores ParentIndex part hierarchy (treats every placement as root-local); still in production use (GameWindow hydration, SkyRenderer) | `src/AcDream.Core/Meshing/SetupMesh.cs:15` | Most Setups are flat single-level rigs where root-local equals composed; hierarchical composition deferred ("Phase 3") | Any Setup with genuinely nested parts renders them at wrong offsets — mis-assembled multi-part objects in the Flatten paths | retail Setup ParentIndex chain composition |
|
||||
| TS-15 | No distance-driven degrade (LOD): always close-detail slot 0; plus the **#47** static `Degrades[0]` swap for 34-part humanoids only (structural sentinel detector) | `src/AcDream.Core/Meshing/GfxObjDegradeResolver.cs:57` (+ `src/AcDream.App/Rendering/GameWindow.cs:2608`) | LOD plumbing doesn't exist; slot 0 is correct for player + nearby NPCs; #47 closed the visible low-detail-arms bug without porting UpdateViewerDistance | Distant objects render max-detail (perf + wrong visuals where far meshes intentionally differ/hide parts); a future 34-part non-humanoid matching the sentinel gets the wrong mesh swap | `CPhysicsPart::UpdateViewerDistance` 0x0050E030; ::Draw 0x0050D7A0; ::LoadGfxObjArray 0x0050DCF0 |
|
||||
| TS-16 | Click picking is Stage A only: ray-vs-fixed-radius spheres (0.7–1.0 m) + screen rect matched to the indicator; retail's per-polygon refine deferred (**#71**); rect-over-circle is a user-approved UX divergence | `src/AcDream.Core/Selection/WorldPicker.cs:199` | Stage B only needed if visual testing surfaces Stage-A over-picks; sphere/rect + cell-BSP occlusion adequate so far | Clicks near (not on) an entity still select it; fixed radii can mis-prioritize overlapping candidates vs retail's polygon-accurate test | `CPolygon::polygon_hits_ray` 0x0054c889 |
|
||||
|
|
@ -304,8 +300,8 @@ TS-23 (PK bits), TS-25 (stance in MoveToState), TS-17 (AttackConditions),
|
|||
and revisit AP-13 (ComputeDamage) + AP-24 (jump-charge constant via the
|
||||
0x0056ADE0 decompile). Emote work must land TS-24 (command-list packing).
|
||||
Membership Stage 2 must land TS-18 (BuildingCellId).
|
||||
The audio phase lands TS-9/TS-29; the live-pose animation-hook layer lands
|
||||
TS-10/TS-11/TS-12/TS-14.
|
||||
The audio phase lands TS-9/TS-29; the remaining live-pose animation work lands
|
||||
TS-14.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue