From 9f15f3b431e057232ada7fb39748a4a4e37a6d2e Mon Sep 17 00:00:00 2001
From: Mads Buvik Sandvei <madssandvei@zoho.com>
Date: Mon, 15 Jan 2024 21:48:19 +0100
Subject: [PATCH] Add engine handler for skill levelup, to dehardcode the
 book/trainer case.

---
 apps/openmw/mwbase/luamanager.hpp        | 3 ++-
 apps/openmw/mwgui/trainingwindow.cpp     | 6 ++----
 apps/openmw/mwlua/engineevents.cpp       | 9 +++++++++
 apps/openmw/mwlua/engineevents.hpp       | 8 +++++++-
 apps/openmw/mwlua/localscripts.cpp       | 2 +-
 apps/openmw/mwlua/localscripts.hpp       | 5 +++++
 apps/openmw/mwlua/luamanagerimp.cpp      | 5 +++++
 apps/openmw/mwlua/luamanagerimp.hpp      | 1 +
 apps/openmw/mwworld/actionread.cpp       | 8 ++------
 files/data/scripts/omw/skillhandlers.lua | 5 ++++-
 10 files changed, 38 insertions(+), 14 deletions(-)

diff --git a/apps/openmw/mwbase/luamanager.hpp b/apps/openmw/mwbase/luamanager.hpp
index 115416cd8c..7f7ee85127 100644
--- a/apps/openmw/mwbase/luamanager.hpp
+++ b/apps/openmw/mwbase/luamanager.hpp
@@ -62,8 +62,9 @@ namespace MWBase
         virtual void objectActivated(const MWWorld::Ptr& object, const MWWorld::Ptr& actor) = 0;
         virtual void useItem(const MWWorld::Ptr& object, const MWWorld::Ptr& actor, bool force) = 0;
         virtual void animationTextKey(const MWWorld::Ptr& actor, const std::string& key) = 0;
-        virtual void skillUse(const MWWorld::Ptr& actor, ESM::RefId skillId, int useType, float scale) = 0;
         virtual void playAnimation(const MWWorld::Ptr& object, const std::string& groupname,
+        virtual void skillLevelUp(const MWWorld::Ptr& actor, ESM::RefId skillId, std::string_view source) = 0;
+        virtual void skillUse(const MWWorld::Ptr& actor, ESM::RefId skillId, int useType, float scale) = 0;
             const MWRender::AnimPriority& priority, int blendMask, bool autodisable, float speedmult,
             std::string_view start, std::string_view stop, float startpoint, uint32_t loops, bool loopfallback)
             = 0;
diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp
index 5395f6db1c..fa4fd266b5 100644
--- a/apps/openmw/mwgui/trainingwindow.cpp
+++ b/apps/openmw/mwgui/trainingwindow.cpp
@@ -5,6 +5,7 @@
 #include <MyGUI_TextIterator.h>
 
 #include "../mwbase/environment.hpp"
+#include "../mwbase/luamanager.hpp"
 #include "../mwbase/mechanicsmanager.hpp"
 #include "../mwbase/windowmanager.hpp"
 #include "../mwbase/world.hpp"
@@ -174,10 +175,7 @@ namespace MWGui
         }
 
         // increase skill
-        MWWorld::LiveCellRef<ESM::NPC>* playerRef = player.get<ESM::NPC>();
-
-        const ESM::Class* class_ = store.get<ESM::Class>().find(playerRef->mBase->mClass);
-        pcStats.increaseSkill(skill->mId, *class_, true);
+        MWBase::Environment::get().getLuaManager()->skillLevelUp(player, skill->mId, "trainer");
 
         // remove gold
         player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price);
diff --git a/apps/openmw/mwlua/engineevents.cpp b/apps/openmw/mwlua/engineevents.cpp
index 7cc2b3db48..f9b9d461cc 100644
--- a/apps/openmw/mwlua/engineevents.cpp
+++ b/apps/openmw/mwlua/engineevents.cpp
@@ -104,6 +104,15 @@ namespace MWLua
                 scripts->onSkillUse(event.mSkill, event.useType, event.scale);
         }
 
+        void operator()(const OnSkillLevelUp& event) const
+        {
+            MWWorld::Ptr actor = getPtr(event.mActor);
+            if (actor.isEmpty())
+                return;
+            if (auto* scripts = getLocalScripts(actor))
+                scripts->onSkillLevelUp(event.mSkill, event.mSource);
+        }
+
     private:
         MWWorld::Ptr getPtr(ESM::RefNum id) const
         {
diff --git a/apps/openmw/mwlua/engineevents.hpp b/apps/openmw/mwlua/engineevents.hpp
index b9f4c25b39..862c09ef2a 100644
--- a/apps/openmw/mwlua/engineevents.hpp
+++ b/apps/openmw/mwlua/engineevents.hpp
@@ -70,8 +70,14 @@ namespace MWLua
             int useType;
             float scale;
         };
+        struct OnSkillLevelUp
+        {
+            ESM::RefNum mActor;
+            std::string mSkill;
+            std::string mSource;
+        };
         using Event = std::variant<OnActive, OnInactive, OnConsume, OnActivate, OnUseItem, OnNewExterior, OnTeleported,
-            OnAnimationTextKey, OnSkillUse>;
+            OnAnimationTextKey, OnSkillUse, OnSkillLevelUp>;
 
         void clear() { mQueue.clear(); }
         void addToQueue(Event e) { mQueue.push_back(std::move(e)); }
diff --git a/apps/openmw/mwlua/localscripts.cpp b/apps/openmw/mwlua/localscripts.cpp
index 0c0af5b902..8bd0d7c047 100644
--- a/apps/openmw/mwlua/localscripts.cpp
+++ b/apps/openmw/mwlua/localscripts.cpp
@@ -176,7 +176,7 @@ namespace MWLua
     {
         this->addPackage("openmw.self", sol::make_object(lua->sol(), &mData));
         registerEngineHandlers({ &mOnActiveHandlers, &mOnInactiveHandlers, &mOnConsumeHandlers, &mOnActivatedHandlers,
-            &mOnTeleportedHandlers, &mOnAnimationTextKeyHandlers, &mOnPlayAnimationHandlers, &mOnSkillUse });
+            &mOnTeleportedHandlers, &mOnAnimationTextKeyHandlers, &mOnPlayAnimationHandlers, &mOnSkillUse, &mOnSkillLevelUp });
     }
 
     void LocalScripts::setActive(bool active)
diff --git a/apps/openmw/mwlua/localscripts.hpp b/apps/openmw/mwlua/localscripts.hpp
index f5f740b1c6..2ec78860d1 100644
--- a/apps/openmw/mwlua/localscripts.hpp
+++ b/apps/openmw/mwlua/localscripts.hpp
@@ -83,6 +83,10 @@ namespace MWLua
         {
             callEngineHandlers(mOnSkillUse, skillId, useType, scale);
         }
+        void onSkillLevelUp(std::string_view skillId, std::string_view source)
+        {
+            callEngineHandlers(mOnSkillLevelUp, skillId, source);
+        }
 
         void applyStatsCache();
 
@@ -98,6 +102,7 @@ namespace MWLua
         EngineHandlerList mOnAnimationTextKeyHandlers{ "_onAnimationTextKey" };
         EngineHandlerList mOnPlayAnimationHandlers{ "_onPlayAnimation" };
         EngineHandlerList mOnSkillUse{ "_onSkillUse" };
+        EngineHandlerList mOnSkillLevelUp{ "_onSkillLevelUp" };
     };
 
 }
diff --git a/apps/openmw/mwlua/luamanagerimp.cpp b/apps/openmw/mwlua/luamanagerimp.cpp
index 0974856d25..5a743f41e3 100644
--- a/apps/openmw/mwlua/luamanagerimp.cpp
+++ b/apps/openmw/mwlua/luamanagerimp.cpp
@@ -411,6 +411,11 @@ namespace MWLua
         mEngineEvents.addToQueue(EngineEvents::OnSkillUse{ getId(actor), skillId.serializeText(), useType, scale });
     }
 
+    void LuaManager::skillLevelUp(const MWWorld::Ptr& actor, ESM::RefId skillId, std::string_view source)
+    {
+        mEngineEvents.addToQueue(EngineEvents::OnSkillLevelUp{ getId(actor), skillId.serializeText(), std::string(source) });
+    }
+
     void LuaManager::objectAddedToScene(const MWWorld::Ptr& ptr)
     {
         mObjectLists.objectAddedToScene(ptr); // assigns generated RefNum if it is not set yet.
diff --git a/apps/openmw/mwlua/luamanagerimp.hpp b/apps/openmw/mwlua/luamanagerimp.hpp
index 2e29ff272a..685fbbde4c 100644
--- a/apps/openmw/mwlua/luamanagerimp.hpp
+++ b/apps/openmw/mwlua/luamanagerimp.hpp
@@ -85,6 +85,7 @@ namespace MWLua
             std::string_view start, std::string_view stop, float startpoint, uint32_t loops,
             bool loopfallback) override;
         void skillUse(const MWWorld::Ptr& actor, ESM::RefId skillId, int useType, float scale) override;
+        void skillLevelUp(const MWWorld::Ptr& actor, ESM::RefId skillId, std::string_view source) override;
         void exteriorCreated(MWWorld::CellStore& cell) override
         {
             mEngineEvents.addToQueue(EngineEvents::OnNewExterior{ cell });
diff --git a/apps/openmw/mwworld/actionread.cpp b/apps/openmw/mwworld/actionread.cpp
index 3f48920dc0..477c92d2dd 100644
--- a/apps/openmw/mwworld/actionread.cpp
+++ b/apps/openmw/mwworld/actionread.cpp
@@ -5,6 +5,7 @@
 #include <components/esm3/loadskil.hpp>
 
 #include "../mwbase/environment.hpp"
+#include "../mwbase/luamanager.hpp"
 #include "../mwbase/windowmanager.hpp"
 
 #include "../mwmechanics/actorutil.hpp"
@@ -49,12 +50,7 @@ namespace MWWorld
         ESM::RefId skill = ESM::Skill::indexToRefId(ref->mBase->mData.mSkillId);
         if (!skill.empty() && !npcStats.hasBeenUsed(ref->mBase->mId))
         {
-            MWWorld::LiveCellRef<ESM::NPC>* playerRef = actor.get<ESM::NPC>();
-
-            const ESM::Class* class_
-                = MWBase::Environment::get().getESMStore()->get<ESM::Class>().find(playerRef->mBase->mClass);
-
-            npcStats.increaseSkill(skill, *class_, true, true);
+            MWBase::Environment::get().getLuaManager()->skillLevelUp(actor, skill, "book");
 
             npcStats.flagAsUsed(ref->mBase->mId);
         }
diff --git a/files/data/scripts/omw/skillhandlers.lua b/files/data/scripts/omw/skillhandlers.lua
index 975a8b984e..1f3b856344 100644
--- a/files/data/scripts/omw/skillhandlers.lua
+++ b/files/data/scripts/omw/skillhandlers.lua
@@ -265,9 +265,12 @@ return {
         },
     },
     engineHandlers = { 
+        -- Use the interface in these handlers so any overrides will receive the calls.
         _onSkillUse = function (skillid, useType, scale)
-            -- Use the interface here so any overrides of skillUsed will receive the call.
             I.SkillProgression.skillUsed(skillid, useType, scale)
         end,
+        _onSkillLevelUp = function (skillid, source)
+            I.SkillProgression.skillLevelUp(skillid, source)
+        end,
     },
 }