Guest User

Untitled

a guest
Sep 7th, 2026
28
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.29 KB | None | 0 0
  1. # Spell Ready VFX Bug — Diagnosis and Fix
  2.  
  3. **Mod:** Spell Framework Plus
  4. **File affected:** `scripts/OMW_MagExp/magexp_player.lua`
  5. **Date fixed:** 2026-09-07
  6. **Reference versions:** `Spell Framework Plus` (bugged, patched) vs `Spell Framework Plus - NoVisualBug` (older, working)
  7.  
  8. ## Issue
  9.  
  10. 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.
  11.  
  12. ## Root Cause
  13.  
  14. The new version's text-key handler (`onTextKey`) added `or g == "spellcast"` to the VFX start trigger, apparently for SpeedyMagick compatibility:
  15.  
  16. ```lua
  17. -- OLD (working version)
  18. if k == "start" then
  19. if shouldTriggerVfxOnStart(g) and not vfxTriggeredThisCast then
  20. vfxTriggeredThisCast = true
  21. castStartLatched[g] = true
  22. sendCastStartNow("textKey:start", g)
  23. end
  24. end
  25.  
  26. -- NEW (buggy version)
  27. if action == "start" then
  28. if (shouldTriggerVfxOnStart(g) or g == "spellcast") and not vfxTriggeredThisCast then
  29. vfxTriggeredThisCast = true
  30. castStartLatched[g] = true
  31. sendCastStartNow("textKey:start", g)
  32. end
  33. end
  34. ```
  35.  
  36. 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.
  37.  
  38. Event chain when the bug fires:
  39.  
  40. 1. `spellcast` group's `start` text key fires on magic stance ready
  41. 2. `onTextKey` calls `sendCastStartNow()` (magexp_player.lua)
  42. 3. Which sends the `MagExp_CastStart` global event
  43. 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
  44. 5. The `stop`/`release` handler (which now also matches `spellcast`) resets the `vfxTriggeredThisCast` latch, so the VFX re-fires on every re-ready
  45.  
  46. Notably, the whitelist comment in the same file already documented the correct design:
  47.  
  48. ```lua
  49. -- ============================================================
  50. -- [CAST START GROUP WHITELIST]
  51. -- Only these groups trigger casting VFX on their 'start' key.
  52. -- We deliberately exclude "spellcast" and release-phase groups.
  53. -- ============================================================
  54. local VFX_CAST_GROUPS = {
  55. quickcast = true,
  56. quickbuff = true,
  57. qcconj = true,
  58. qctouch = true,
  59. qcalt = true,
  60. qcalts = true,
  61. qcill = true,
  62. qcsnap = true,
  63. qcdrain = true,
  64. qcskrow = true,
  65. }
  66. ```
  67.  
  68. 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.
  69.  
  70. ## Fix Applied
  71.  
  72. Three edits to `scripts/OMW_MagExp/magexp_player.lua`:
  73.  
  74. ### 1. Moved the SpeedyMagick latch declarations above `onTextKey` (fixes scoping)
  75.  
  76. All references must share one upvalue; previously `onTextKey` saw a nil global while `updateSpeedyMagickCompat` and `handleCastResult` used the local.
  77.  
  78. ```lua
  79. return result
  80. end
  81.  
  82. local speedymagickLatched = false
  83. local speedymagickCastTime = 0
  84.  
  85. -- ============================================================
  86. -- [CORE] Animation Sync & Lifecycle
  87. -- ============================================================
  88. -- ============================================================
  89. -- CORE TEXT KEY HANDLER
  90. -- ============================================================
  91. local function onTextKey(groupname, key)
  92. ```
  93.  
  94. ### 2. Gated the `spellcast` trigger on an actual cast being in progress
  95.  
  96. ```lua
  97. -- Parse compound text keys like "self start", "target release", "touch stop"
  98. local fst, snd = string.match(k, "([^ ]+) ([^ ]+)")
  99. local action = snd or k -- "start", "release", "stop", etc.
  100. local targetType = snd and fst or nil -- "self", "target", "touch"
  101.  
  102. -- VFX ONLY on the initial cast group's 'start' key.
  103. -- The engine plays the "spellcast" group whenever magic stance is readied,
  104. -- so only treat "spellcast" as a cast start when one is actually in progress.
  105. if action == "start" then
  106. if (shouldTriggerVfxOnStart(g) or (g == "spellcast" and (hasQueuedLaunch or speedymagickLatched))) and not vfxTriggeredThisCast then
  107. vfxTriggeredThisCast = true
  108. castStartLatched[g] = true
  109. sendCastStartNow("textKey:start", g)
  110. end
  111.  
  112. -- Accelerate OSSC cast animation speed if SpeedyMagick is active
  113. if isSpeedyMagickInstalled() and isWhitelistedCastGroup(g) then
  114. local speedMult = getSpeedyMagickSpeedMult()
  115. pcall(function() anim.setSpeed(self, g, speedMult) end)
  116. end
  117. end
  118. ```
  119.  
  120. 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).
  121.  
  122. ### 3. Removed the now-duplicate declarations from the SpeedyMagick handler section
  123.  
  124. ```lua
  125. -- ============================================================
  126. -- [SPEEDYMAGICK COMPATIBILITY HANDLER]
  127. -- Detects casts initiated by omwSpeedyMagick (controls.use==1 in magic
  128. -- stance) and ensures MagExp cast VFX + skill progression fires, while
  129. -- letting the engine handle the actual spell effect via controls.use=1.
  130. -- NO changes to SpeedyMagick required.
  131. -- ============================================================
  132. local function updateSpeedyMagickCompat(dt)
  133. ```
  134.  
  135. ## Verification
  136.  
  137. - All 13 references to `speedymagickLatched` / `speedymagickCastTime` (lines 265–444) now resolve to the single `local` declared at line 243
  138. - Parenthesis balance check passed (final depth 0, never negative)
  139. - No Lua interpreter was available locally, so in-game testing is still recommended:
  140. - Ready/unready magic stance repeatedly with a spell selected — no VFX should appear
  141. - Cast normally, quickcast, and (if installed) cast via SpeedyMagick — VFX should still appear
  142.  
Advertisement
Add Comment
Please, Sign In to add comment