Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # Spell Ready VFX Bug — Diagnosis and Fix
- **Mod:** Spell Framework Plus
- **File affected:** `scripts/OMW_MagExp/magexp_player.lua`
- **Date fixed:** 2026-09-07
- **Reference versions:** `Spell Framework Plus` (bugged, patched) vs `Spell Framework Plus - NoVisualBug` (older, working)
- ## Issue
- In the latest version, merely readying hands for casting (entering magic stance) plays the selected spell's cast visual effects, centered on the player. In first person this repeatedly obscures the player's vision.
- ## Root Cause
- The new version's text-key handler (`onTextKey`) added `or g == "spellcast"` to the VFX start trigger, apparently for SpeedyMagick compatibility:
- ```lua
- -- OLD (working version)
- if k == "start" then
- if shouldTriggerVfxOnStart(g) and not vfxTriggeredThisCast then
- vfxTriggeredThisCast = true
- castStartLatched[g] = true
- sendCastStartNow("textKey:start", g)
- end
- end
- -- NEW (buggy version)
- if action == "start" then
- if (shouldTriggerVfxOnStart(g) or g == "spellcast") and not vfxTriggeredThisCast then
- vfxTriggeredThisCast = true
- castStartLatched[g] = true
- sendCastStartNow("textKey:start", g)
- end
- end
- ```
- The problem: in OpenMW/Morrowind, the "hands readied" magic pose **is** the `spellcast` animation group. Entering magic stance plays `spellcast` and fires its `start` text key, so the new condition treated every stance-ready as the beginning of a cast.
- Event chain when the bug fires:
- 1. `spellcast` group's `start` text key fires on magic stance ready
- 2. `onTextKey` calls `sendCastStartNow()` (magexp_player.lua)
- 3. Which sends the `MagExp_CastStart` global event
- 4. `MagExp_CastStart` handler in `magexp_global.lua` (~line 3665) spawns the cast VFX model for **every effect** of the currently selected spell, attached to the actor's root (`boneName = ""`) — i.e. centered on the player/camera, which is what obscures vision
- 5. The `stop`/`release` handler (which now also matches `spellcast`) resets the `vfxTriggeredThisCast` latch, so the VFX re-fires on every re-ready
- Notably, the whitelist comment in the same file already documented the correct design:
- ```lua
- -- ============================================================
- -- [CAST START GROUP WHITELIST]
- -- Only these groups trigger casting VFX on their 'start' key.
- -- We deliberately exclude "spellcast" and release-phase groups.
- -- ============================================================
- local VFX_CAST_GROUPS = {
- quickcast = true,
- quickbuff = true,
- qcconj = true,
- qctouch = true,
- qcalt = true,
- qcalts = true,
- qcill = true,
- qcsnap = true,
- qcdrain = true,
- qcskrow = true,
- }
- ```
- Secondary latent bug: `speedymagickLatched` and `speedymagickCastTime` were declared as `local` *after* `onTextKey` was defined, so `onTextKey` was actually referencing an undeclared global (always nil). This meant the SpeedyMagick release/stop processing inside `onTextKey` could never fire, and any gating on that variable from `onTextKey` would not work.
- ## Fix Applied
- Three edits to `scripts/OMW_MagExp/magexp_player.lua`:
- ### 1. Moved the SpeedyMagick latch declarations above `onTextKey` (fixes scoping)
- All references must share one upvalue; previously `onTextKey` saw a nil global while `updateSpeedyMagickCompat` and `handleCastResult` used the local.
- ```lua
- return result
- end
- local speedymagickLatched = false
- local speedymagickCastTime = 0
- -- ============================================================
- -- [CORE] Animation Sync & Lifecycle
- -- ============================================================
- -- ============================================================
- -- CORE TEXT KEY HANDLER
- -- ============================================================
- local function onTextKey(groupname, key)
- ```
- ### 2. Gated the `spellcast` trigger on an actual cast being in progress
- ```lua
- -- Parse compound text keys like "self start", "target release", "touch stop"
- local fst, snd = string.match(k, "([^ ]+) ([^ ]+)")
- local action = snd or k -- "start", "release", "stop", etc.
- local targetType = snd and fst or nil -- "self", "target", "touch"
- -- VFX ONLY on the initial cast group's 'start' key.
- -- The engine plays the "spellcast" group whenever magic stance is readied,
- -- so only treat "spellcast" as a cast start when one is actually in progress.
- if action == "start" then
- if (shouldTriggerVfxOnStart(g) or (g == "spellcast" and (hasQueuedLaunch or speedymagickLatched))) and not vfxTriggeredThisCast then
- vfxTriggeredThisCast = true
- castStartLatched[g] = true
- sendCastStartNow("textKey:start", g)
- end
- -- Accelerate OSSC cast animation speed if SpeedyMagick is active
- if isSpeedyMagickInstalled() and isWhitelistedCastGroup(g) then
- local speedMult = getSpeedyMagickSpeedMult()
- pcall(function() anim.setSpeed(self, g, speedMult) end)
- end
- end
- ```
- Ready-magic stance alone no longer triggers VFX; genuine casts still do (whitelisted quickcast groups, or `spellcast` when a MagExp quick-cast pipeline launch is queued or a SpeedyMagick cast is latched).
- ### 3. Removed the now-duplicate declarations from the SpeedyMagick handler section
- ```lua
- -- ============================================================
- -- [SPEEDYMAGICK COMPATIBILITY HANDLER]
- -- Detects casts initiated by omwSpeedyMagick (controls.use==1 in magic
- -- stance) and ensures MagExp cast VFX + skill progression fires, while
- -- letting the engine handle the actual spell effect via controls.use=1.
- -- NO changes to SpeedyMagick required.
- -- ============================================================
- local function updateSpeedyMagickCompat(dt)
- ```
- ## Verification
- - All 13 references to `speedymagickLatched` / `speedymagickCastTime` (lines 265–444) now resolve to the single `local` declared at line 243
- - Parenthesis balance check passed (final depth 0, never negative)
- - No Lua interpreter was available locally, so in-game testing is still recommended:
- - Ready/unready magic stance repeatedly with a spell selected — no VFX should appear
- - Cast normally, quickcast, and (if installed) cast via SpeedyMagick — VFX should still appear
Advertisement
Add Comment
Please, Sign In to add comment