No OnSkillFailed implementation and subsequent exposure to lua scripts
## Issue Description **Feature request: expose skill-failure events to Lua.** Today the Lua skill-progression API only fires for **successful** uses (`I.SkillProgression.addSkillUsedHandler` / `OnSkillUse`). There is no equivalent for **failed** uses - weapon misses, fizzled spells, failed lockpicks, sprung traps, and detected pickpockets are entirely invisible to scripts. I'd like to add a parallel `addSkillUsedFailedHandler` and the engine call-sites that drive it. ### Why engine exposure is needed The failure paths live entirely in C++ and never call `skillUsageSucceeded`, so there is currently no observable seam from Lua: - **Melee miss** - `MWClass::Npc::hit`, after the to-hit roll fails. No script hook. - **Ranged miss** - `MWMechanics::projectileHit` / `combat.cpp`. No script hook. - **Spellcast failure** - `MWMechanics::CastSpell::cast`, returns early when the success roll fails. No script hook. - **Lockpick / disarm-trap failure** - `MWMechanics::Security::pickLock` / `probeTrap`, again early-return. No script hook. - **Caught pickpocket** - `PickpocketItemModel::pick` / `finish` flag detection but emit no Lua event. Workarounds in pure Lua (polling NPC health, watching magicka deltas, inspecting container contents after a steal) are unreliable, expensive, or simply impossible for events like a missed swing. ### What this unlocks for Lua mods - **Failure-XP mods** - grant a fraction of normal skill XP on failure to soften vanilla Morrowind's "use it, fail, learn nothing" feel. (This is the use case I built - see below.) - **Difficulty / immersion mods** - extra stamina drain on a missed swing, a brief debuff after a fizzled spell, custom sound/UI on a caught pickpocket. - **Telemetry / analytics mods** - accurate per-skill success rates for balance tuning. - **Tutorial / onboarding mods** - surface contextual hints the first time the player fails an action. - **Achievement / streak mods** - "no-miss" runs, etc. None of these need the engine to ship gameplay logic, they just need a hook. ### Proposed API Mirror the existing success path. New Lua interface method: ```lua I.SkillProgression.addSkillUsedFailedHandler(function(skillid, params) -- params.useType : same SKILL_USE_TYPES enum as the success handler -- params.skillGain: the gain that *would* have been awarded on success end) ``` `SKILL_USE_TYPES` stays as the single shared enum - the handler list differentiates success vs failure, so no new enum values are needed for existing categories. Interface version bumps from 2 -\> 3. ### Proposed engine changes Minimal parallel plumbing to the existing success chain: | Layer | File | Change | |-------|------|--------| | Lua manager interface | `apps/openmw/mwbase/luamanager.hpp` | Add virtual `skillUseFailed(...)` | | Lua manager impl | `apps/openmw/mwlua/luamanagerimp.{hpp,cpp}` | Queue `OnSkillUseFailed` event | | Engine event | `apps/openmw/mwlua/engineevents.{hpp,cpp}` | New `OnSkillUseFailed` struct + variant + visitor | | Local scripts | `apps/openmw/mwlua/localscripts.{hpp,cpp}` | New `EngineHandlerList mOnSkillUseFailed` + register it | | World class | `apps/openmw/mwworld/class.{hpp,cpp}` | Virtual `skillUsageFailed(...)` | | NPC class | `apps/openmw/mwclass/npc.{hpp,cpp}` | Override forwarding to `LuaManager` | | Built-in script | `files/data/scripts/omw/skillhandlers.lua` | `skillUsedFailed`, `addSkillUsedFailedHandler`, `_onSkillUseFailed`; bump interface to v3 | | Docs | `docs/source/reference/lua-scripting/` | Document new handler | Call sites that fire the new event (player-only, gated on the same conditions as the success counterparts): - `apps/openmw/mwclass/npc.cpp` - melee miss - `apps/openmw/mwmechanics/combat.cpp` - ranged miss - `apps/openmw/mwmechanics/spellcasting.cpp` - cast fail (gated on `spellIncreasesSkill`) - `apps/openmw/mwmechanics/security.cpp` - failed lockpick and failed disarm - `apps/openmw/mwgui/pickpocketitemmodel.cpp` - caught pickpocket (both `pick()` and `finish()`) The dispatch is symmetric to the success path, so there is no new ordering or threading model — `LocalScripts::onSkillUseFailed` runs on the same queue as `onSkillUse`. ### Implementation status I've implemented all of the above against current `master` and wrote a working mod, FailureXP, that subscribes to the new handler and routes a configurable fraction of the would-be successful XP back through `I.SkillProgression.skillUsed` so the standard XP/level-up chain applies the gain. Verified in-game on melee miss, ranged miss, failed spellcast, failed lockpick, failed disarm trap, and caught pickpocket — screenshots attached below. If the direction looks good, I'll open an MR with just the engine changes + docs + interface version bump (no FailureXP gameplay — that stays as a separate user-side mod). ## Steps to Reproduce Demonstrating the current gap (vanilla `master`, no patches): 1. Open the game with a Lua script that registers `I.SkillProgression.addSkillUsedHandler(function(id, p) print('use', id, p.useType) end)`. 2. Swing at and miss an enemy → no event. 3. Cast a spell that fizzles → no event. 4. Attempt and fail a lockpick → no event. 5. Trip a trap during disarm → no event. 6. Get caught pickpocketing → no event. Successful versions of all of the above fire `OnSkillUse` as expected — only the failure paths are unreachable from Lua. ## System Information * **OpenMW Version:** <!--fill in: e.g. master @ <commit-sha> from your local checkout--> * **Operating System & Version:** Windows 11 Home (10.0.26200) * **GPU:** <!--fill in--> * **Morrowind Version:** <!--Retail CD / Steam / GOG--> * **Language:** English * **Addons:** <!--Tribunal, Bloodmoon?--> ## Mods * **Are you using mods?** Yes — FailureXP pictured working below (the mod I wrote for this proposed API; depends on the patched engine described above). ![Screenshot 2026-05-05 144512.png](/uploads/a0f78ef66dde98e700693992b13150c7/Screenshot_2026-05-05_144512.png){width="900" height="492"} ![Screenshot 2026-05-05 144556.png](/uploads/03e5a8f923388b8b84e20c5a6339af0d/Screenshot_2026-05-05_144556.png){width="900" height="515"} ![Screenshot 2026-05-05 144728.png](/uploads/10d6dab93b274c971ee9ce6bebedb6cd/Screenshot_2026-05-05_144728.png){width="900" height="503"} * **Are you using any shader mods?** No * **Does it happen in a clean vanilla install?** N/A — this is a feature request, not a bug. The "issue" (missing API surface) is reproducible on a clean vanilla `master` build. ## Logs and Settings N/A for a feature request. [Engine patch here](https://gitlab.com/salahmagoosh/openmw/-/commit/0f0da63140081ec79fb07cc857f68988c9c815f1) and [consumer-mod available here](https://www.nexusmods.com/games/morrowind/mods/58893); screenshots of the new handler firing in-game are attached.
issue