diff --git a/AUTHORS.md b/AUTHORS.md index 2ddaa03cf6..ecfc03dc1e 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -218,6 +218,7 @@ Programmers tlmullis tri4ng1e Thoronador + Tobias Tribble (zackogenic) Tom Lowe (Vulpen) Tom Mason (wheybags) Torben Leif Carrington (TorbenC) @@ -234,7 +235,6 @@ Programmers Yuri Krupenin zelurker Noah Gooder - Documentation ------------- diff --git a/apps/openmw/mwbase/luamanager.hpp b/apps/openmw/mwbase/luamanager.hpp index 8c0c5d61de..7fa8b6df58 100644 --- a/apps/openmw/mwbase/luamanager.hpp +++ b/apps/openmw/mwbase/luamanager.hpp @@ -36,6 +36,7 @@ namespace MWBase virtual void objectAddedToScene(const MWWorld::Ptr& ptr) = 0; virtual void objectRemovedFromScene(const MWWorld::Ptr& ptr) = 0; virtual void appliedToObject(const MWWorld::Ptr& toPtr, std::string_view recordId, const MWWorld::Ptr& fromPtr) = 0; + virtual void objectActivated(const MWWorld::Ptr& object, const MWWorld::Ptr& actor) = 0; // TODO: notify LuaManager about other events // virtual void objectOnHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, // const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) = 0; diff --git a/apps/openmw/mwlua/actions.cpp b/apps/openmw/mwlua/actions.cpp index dda237fb4e..8135d14299 100644 --- a/apps/openmw/mwlua/actions.cpp +++ b/apps/openmw/mwlua/actions.cpp @@ -6,10 +6,13 @@ #include #include -#include "../mwworld/cellstore.hpp" -#include "../mwworld/class.hpp" -#include "../mwworld/inventorystore.hpp" -#include "../mwworld/player.hpp" +#include + +#include +#include +#include +#include +#include namespace MWLua { @@ -145,4 +148,24 @@ namespace MWLua tryEquipToSlot(anySlot, item); } + void ActivateAction::apply(WorldView& worldView) const + { + MWWorld::Ptr object = worldView.getObjectRegistry()->getPtr(mObject, true); + if (object.isEmpty()) + throw std::runtime_error(std::string("Object not found: " + idToString(mObject))); + MWWorld::Ptr actor = worldView.getObjectRegistry()->getPtr(mActor, true); + if (actor.isEmpty()) + throw std::runtime_error(std::string("Actor not found: " + idToString(mActor))); + + MWBase::Environment::get().getLuaManager()->objectActivated(object, actor); + std::shared_ptr action = object.getClass().activate(object, actor); + action->execute(actor); + } + + std::string ActivateAction::toString() const + { + return std::string("ActivateAction object=") + idToString(mObject) + + std::string(" actor=") + idToString(mActor); + } + } diff --git a/apps/openmw/mwlua/actions.hpp b/apps/openmw/mwlua/actions.hpp index e88b39e83c..97da953437 100644 --- a/apps/openmw/mwlua/actions.hpp +++ b/apps/openmw/mwlua/actions.hpp @@ -65,6 +65,20 @@ namespace MWLua Equipment mEquipment; }; + class ActivateAction final : public Action + { + public: + ActivateAction(LuaUtil::LuaState* state, ObjectId object, ObjectId actor) + : Action(state), mObject(object), mActor(actor) {} + + void apply(WorldView&) const override; + std::string toString() const override; + + private: + ObjectId mObject; + ObjectId mActor; + }; + } #endif // MWLUA_ACTIONS_H diff --git a/apps/openmw/mwlua/localscripts.cpp b/apps/openmw/mwlua/localscripts.cpp index 918970dc54..2b492c5c17 100644 --- a/apps/openmw/mwlua/localscripts.cpp +++ b/apps/openmw/mwlua/localscripts.cpp @@ -88,7 +88,7 @@ namespace MWLua : LuaUtil::ScriptsContainer(lua, "L" + idToString(obj.id()), autoStartMode), mData(obj) { this->addPackage("openmw.self", sol::make_object(lua->sol(), &mData)); - registerEngineHandlers({&mOnActiveHandlers, &mOnInactiveHandlers, &mOnConsumeHandlers}); + registerEngineHandlers({&mOnActiveHandlers, &mOnInactiveHandlers, &mOnConsumeHandlers, &mOnActivatedHandlers}); } void LocalScripts::receiveEngineEvent(const EngineEvent& event) @@ -106,6 +106,10 @@ namespace MWLua mData.mIsActive = false; callEngineHandlers(mOnInactiveHandlers); } + else if constexpr (std::is_same_v) + { + callEngineHandlers(mOnActivatedHandlers, arg.mActivatingActor); + } else { static_assert(std::is_same_v); diff --git a/apps/openmw/mwlua/localscripts.hpp b/apps/openmw/mwlua/localscripts.hpp index 68da0b8b03..10b9cd53af 100644 --- a/apps/openmw/mwlua/localscripts.hpp +++ b/apps/openmw/mwlua/localscripts.hpp @@ -33,11 +33,15 @@ namespace MWLua struct OnActive {}; struct OnInactive {}; + struct OnActivated + { + LObject mActivatingActor; + }; struct OnConsume { std::string mRecordId; }; - using EngineEvent = std::variant; + using EngineEvent = std::variant; void receiveEngineEvent(const EngineEvent&); @@ -48,6 +52,7 @@ namespace MWLua EngineHandlerList mOnActiveHandlers{"onActive"}; EngineHandlerList mOnInactiveHandlers{"onInactive"}; EngineHandlerList mOnConsumeHandlers{"onConsume"}; + EngineHandlerList mOnActivatedHandlers{"onActivated"}; }; } diff --git a/apps/openmw/mwlua/luamanagerimp.cpp b/apps/openmw/mwlua/luamanagerimp.cpp index 58784a4629..e9146b8af4 100644 --- a/apps/openmw/mwlua/luamanagerimp.cpp +++ b/apps/openmw/mwlua/luamanagerimp.cpp @@ -352,6 +352,11 @@ namespace MWLua mLocalEngineEvents.push_back({getId(toPtr), LocalScripts::OnConsume{std::string(recordId)}}); } + void LuaManager::objectActivated(const MWWorld::Ptr& object, const MWWorld::Ptr& actor) + { + mLocalEngineEvents.push_back({getId(object), LocalScripts::OnActivated{LObject(getId(actor), mWorldView.getObjectRegistry())}}); + } + MWBase::LuaManager::ActorControls* LuaManager::getActorControls(const MWWorld::Ptr& ptr) const { LocalScripts* localScripts = ptr.getRefData().getLuaScripts(); diff --git a/apps/openmw/mwlua/luamanagerimp.hpp b/apps/openmw/mwlua/luamanagerimp.hpp index 753cc6ca49..b292eb3c1b 100644 --- a/apps/openmw/mwlua/luamanagerimp.hpp +++ b/apps/openmw/mwlua/luamanagerimp.hpp @@ -49,6 +49,7 @@ namespace MWLua void deregisterObject(const MWWorld::Ptr& ptr) override; void inputEvent(const InputEvent& event) override { mInputEvents.push_back(event); } void appliedToObject(const MWWorld::Ptr& toPtr, std::string_view recordId, const MWWorld::Ptr& fromPtr) override; + void objectActivated(const MWWorld::Ptr& object, const MWWorld::Ptr& actor) override; MWBase::LuaManager::ActorControls* getActorControls(const MWWorld::Ptr&) const override; diff --git a/apps/openmw/mwlua/objectbindings.cpp b/apps/openmw/mwlua/objectbindings.cpp index 9857170b32..9543e306c1 100644 --- a/apps/openmw/mwlua/objectbindings.cpp +++ b/apps/openmw/mwlua/objectbindings.cpp @@ -137,6 +137,14 @@ namespace MWLua const MWWorld::Class& cls = o.ptr().getClass(); return cls.getWalkSpeed(o.ptr()); }; + objectT["activateBy"] = [context](const ObjectT& o, const ObjectT& actor) + { + uint32_t esmRecordType = actor.ptr().getType(); + if (esmRecordType != ESM::REC_CREA && esmRecordType != ESM::REC_NPC_) + throw std::runtime_error("The argument of `activateBy` must be an actor who activates the object. Got: " + + ptrToString(actor.ptr())); + context.mLuaManager->addAction(std::make_unique(context.mLua, o.id(), actor.id())); + }; if constexpr (std::is_same_v) { // Only for global scripts diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 6d7dcdda1d..0b9833e097 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -12,6 +12,7 @@ #include "../mwbase/scriptmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/inputmanager.hpp" +#include "../mwbase/luamanager.hpp" #include "../mwworld/action.hpp" #include "../mwworld/class.hpp" @@ -417,6 +418,7 @@ namespace MWScript void InterpreterContext::executeActivation(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) { + MWBase::Environment::get().getLuaManager()->objectActivated(ptr, actor); std::shared_ptr action = (ptr.getClass().activate(ptr, actor)); action->execute (actor); if (action->getTarget() != MWWorld::Ptr() && action->getTarget() != ptr) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 1aebe94b87..706ce4859f 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3827,7 +3827,8 @@ namespace MWWorld if (object.getRefData().activate()) { - std::shared_ptr action = (object.getClass().activate(object, actor)); + MWBase::Environment::get().getLuaManager()->objectActivated(object, actor); + std::shared_ptr action = object.getClass().activate(object, actor); action->execute (actor); } } diff --git a/docs/source/reference/lua-scripting/engine_handlers.rst b/docs/source/reference/lua-scripting/engine_handlers.rst index 46e648d620..d247803c08 100644 --- a/docs/source/reference/lua-scripting/engine_handlers.rst +++ b/docs/source/reference/lua-scripting/engine_handlers.rst @@ -43,6 +43,12 @@ Engine handler is a function defined by a script, that can be called by the engi | | | can not access anything nearby, but it is possible to send | | | | an event to global scripts. | +----------------------------------+----------------------------------------------------------------------+ +| onActivated(actor) | | Called on an object when an actor activates it. Note that picking | +| | | up an item is also an activation and works this way: (1) a copy of | +| | | the item is placed to the actor's inventory, (2) count of | +| | | the original item is set to zero, (3) and only then onActivated is | +| | | called on the original item, so self.count is already zero. | ++----------------------------------+----------------------------------------------------------------------+ | onConsume(recordId) | | Called if `recordId` (e.g. a potion) is consumed. | +----------------------------------+----------------------------------------------------------------------+ | **Only for local scripts attached to a player** | diff --git a/files/lua_api/openmw/core.lua b/files/lua_api/openmw/core.lua index 8415690067..95732af837 100644 --- a/files/lua_api/openmw/core.lua +++ b/files/lua_api/openmw/core.lua @@ -217,6 +217,14 @@ -- @param #string eventName -- @param eventData +------------------------------------------------------------------------------- +-- Activate the object. +-- @function [parent=#GameObject] activateBy +-- @param self +-- @param #GameObject actor The actor who activates the object +-- @usage local self = require('openmw.self') +-- object:activateBy(self) + ------------------------------------------------------------------------------- -- Returns `true` if the item is equipped on the object. -- @function [parent=#GameObject] isEquipped