Files
healer-man/scripts/combat-effects

Original WoW spell visuals

The runtime packages every WoW 3.3.5 visual model referenced by the current ability manifest, across all ten WoW classes. There is no class filter in package-wow-original.mjs. Ascension and RuneWaker entries retain their existing source mappings and sound behavior.

Current coverage: 483 packaged models; 5,066 of 5,066 source phase assets; 311 mesh models, 1,711 particle emitters, 249 ribbon emitters, and 479 unique source textures. The generated manifest includes per-class coverage. A spell rank without a source visual phase (for example, a passive or totem-management action) is not counted as a missing model. This does not invent original visuals for spells with no source mapping; the runtime still supplies its generated school fallback.

Generate and verify

npm run effects:wow
npm run effects:audit
npm run effects:test

effects:generate rebuilds the DBC/audio mappings before packaging all WoW visuals. effects:priest remains a compatibility alias for effects:wow so it cannot accidentally revert the main manifest to Priest-only coverage. The old Priest decoder and fixtures remain for compatibility tests.

The manifest collector also reads effectful boss spell IDs from dungeonCampaignCatalog.json. Regenerate the dungeon catalog first whenever boss spell semantics change. Missing or undecodable source audio falls back to the runtime's short spatial school cue; enabled combat audio is never silently dropped merely because a source file is absent.

Source assets resolve from the extracted sibling LadiksMPQEditor base-client directories, with the sibling wow335a/Data archive stack recovering missing files. Locale archives supply shared interface textures used by spell models. Recovered files go into .tools/wow-effects-source; source installations are read-only. BLPConverter8 converts the original BLPs to PNG. Model and texture paths are checked before resolution. An incomplete run reports failures and does not replace the active manifest.

Graph filenames hash the complete runtime payload, including source model, skin, textures and nested model dependencies. Changes therefore get new URLs instead of reusing an incompatible cached graph. The normal content publisher assigns the entire assets/game/combat-effects tree to core:combat-media. Publishing a new app/content release remains the normal deployment step. Development gameplay uses Vite's local source media directly; release builds retain the verified media-pack download requirement.

RuneWaker spell media mapping

npm run effects:rom refreshes only RuneWaker manifest entries and packages their sound cues without rebuilding WoW effects. It reads spell-to-image links from romPlayerCatalog.generated.json and magiccollectobject.db. It follows each direct MagicCollect component through magicobject.db to ImageObject ExtendACT target models, alongside the spell's own ImageObject paths. Exact same-name cues under the client's parallel sound/fx tree and zero-offset direct ROS audio events supply phase-tagged cues. Delayed and fade-out events stay in the batch report until their timeline has a matching runtime event. The combat audio player layers distinct RuneWaker cues on the same phase, including separate ROS appear and idle sounds; WoW sources retain weighted single-cue selection.

npm run effects:rom:particles packages a decoded ROS particle archive for the five primary classes and their accepted elite pairs. Build the native exporter with scripts/runewaker-pipeline/native/build-exporter.ps1 first. The exporter reads ACT motion controllers, where the texture names and particle keys live; the static entity tree alone contains default values. Set RUNEWAKER_RESOURCE_ROOT, RUNEWAKER_EXPORTER, or TEXCONV_BIN to override the default source and tool locations. The archive index at public/assets/game/combat-effects/original/runewaker/manifest.json records decoded and unsupported controller families per model. Its graphs contain the source shader identity, blend mode, billboard settings, particle tracks, and PNG versions of referenced DDS textures. The archive also extracts the SMUNI vertex and pixel HLSL from the Rune engine source with a source hash. Texture tile sizes are pixels, not atlas row and column counts.

npm run effects:rom:audit writes the first-five class and accepted-pair map to docs/ROM_SPELL_MEDIA_MAPPING.md. It distinguishes source ROS references, packaged audio, and browser-playable original visual graphs. The packager enables any graph whose sprite, emitter, texture, subtree alpha, and nonvisual event callback data meet the browser renderer's validated schema. Event IDs that spawn an effect or control projectile flight remain blocked. Urgent Heal's target rune and decal are an explicit partial-original exception; rom-particle-readiness.mjs holds hand-reviewed scale and fidelity overrides. Other ROS phases retain generated visual fallbacks until their controller families are implemented. Open scripts/combat-effects/rom-preview.html with the Vite dev server to inspect the decoded sprite models. The emitter integrates source rate keys over each rendered frame so short bursts survive slower frames. Whirlwind Shield uses a 2.5 presentation scale after live dungeon review.

npm run effects:rom:plan creates docs/ROM_MEDIA_BATCH_PLAN.md and its machine-readable JSON companion from the current primary-five manifest and ROS archive. It groups models by unsupported controller family, lists fully decoded graphs awaiting visual review, and flags embedded one-shot audio paths absent from the manifest as candidates for timeline support. Run it after the audio and particle package steps. The generator attaches source direct audio events at the matching phase boundary when their motion and zero-offset timing are known; remaining candidates require a different timeline or phase mapping. npm run effects:rom:batch runs the manifest/audio pass, particle package, manifest attachment pass, media audit, and batch plan in that order. Build the native exporter before running it when its C++ source changes.

The Priest's holy act_casting_c_hand.ros has three renderable sprite layers and one CRuController_EventTrigger in its appear motion. The RuneWaker source identifies event 43 as looping direct 3D audio for sound/fx/skill/holy/act_casting_c_hand.wav, not another visual layer. The packager marks the model renderer-ready after validating the event's parameters. Its packaged WAV loops only during the matching Priest cast and stops on release or cancellation. The same source event rule applies to the five class cohort when IDs 1, 41, 43, 44, 50, or 61 carry valid data.

Urgent Heal's target effect is linked through MagicCollect spell 491147 to MagicObject component 501543, whose ImageObject 573048 supplies ExtendACT=model/fx/skill/holy/act_benedictory_heal_t_down.ros at p_down. The client creates the component's ExtendACT on the target after the heal resolves. Its explode motion contains two original sprite layers (holy line and gold stars), a nested rune_cylinder.ros particle entity with source rune texture, a kan_rune_circle.dds ground decal, and a one-shot direct 3D audio event for sound/fx/skill/holy/act_benedictory_heal_t_down.mp3. The browser plays the two sprite layers and source rune mesh emission, and animates the decal radius, rotation, color, and alpha. The decal lies on a level plane; the RuneWaker client projects it onto collision triangles. The graph remains partial-original for that terrain projection difference while replacing the generated target filler.

Rising Tide spell 490256 uses ImageObject 572061 for act_icecone01_c_hand.ros at cast start and act_icecone01_f.ros on release. MagicObject component 620408 links ImageObject 572062, whose target ExtendACT is act_riptide_t_freeze_hit.ros at freeze_hit. The source audio events name the projectile cue sound/fx/skill/ice/icenew/act_icecone01_f.ros.mp3 and target cue sound/fx/skill/ice/act_riptide_t_freeze_hit.mp3; the cast hand has its mirrored WAV. The verified models are attached to their respective combat presentation phases after their controller support and source motion checks.

Rendering

The wow-m2-model format composes source particles, animated/skinned geometry, material alpha and colors, UV transforms, and bone-driven ribbon trails. The renderer resolves full and upright billboards, texture layers, source-space pivots, global animation sequences, and Stand/Hold/Decay timelines. Original geometry particles use instance pools; secondary particle emitters are bounded to prevent recursive effects multiplying the scene budget.

The live combat path uses the same cast, projectile, impact and aura events as before. In original mode, an instant release also renders any source-side cast-start layers without publishing another gameplay or audio event. Instant terminal events prefer their exact phase, then use the source impact or aura-start counterpart when that is the only mapping. The source phase stays on the render event so attachment and aura lifetime rules remain correct. This covers all 1,106 castable instant rank records that have a packaged source visual, including all 287 latest-rank instant abilities with source visuals. The source catalog also contains three passive instant records with visuals; passives do not publish combat events. Abilities without a source mapping, or with no usable asset load, retain generated fallback. Particle budgets, placement anchors, and travel timing remain the game's presentation rules; this is not a claim of pixel-identical client simulation or exhaustive spell-by-spell gameplay QA.

Decoder reference for compressed bone quaternions, float UV quaternions, and source record layouts: wowlib M2 records.

Local visual inspection

Run npm run dev and open /scripts/combat-effects/preview.html. The development-only preview lists every directly referenced model and uses the same mesh, ribbon and particle renderer as live combat. Drag to orbit, scroll to zoom, replay an effect, or freeze its opening frame. The preview is not included in the production app entry point.

Runtime tests parse all shipped graphs, sample bone transforms, and verify geometry bounds, dependency textures and billboard behavior. Browser evidence and observed limitations are recorded in PLAYTEST_NOTES.md.