diff --git a/apps/openmw/mwbase/luamanager.hpp b/apps/openmw/mwbase/luamanager.hpp index d9ce24a119..fcbb6289f9 100644 --- a/apps/openmw/mwbase/luamanager.hpp +++ b/apps/openmw/mwbase/luamanager.hpp @@ -47,6 +47,7 @@ namespace MWBase virtual void gameLoaded() = 0; virtual void objectAddedToScene(const MWWorld::Ptr& ptr) = 0; virtual void objectRemovedFromScene(const MWWorld::Ptr& ptr) = 0; + virtual void objectTeleported(const MWWorld::Ptr& ptr) = 0; virtual void itemConsumed(const MWWorld::Ptr& consumable, const MWWorld::Ptr& actor) = 0; virtual void objectActivated(const MWWorld::Ptr& object, const MWWorld::Ptr& actor) = 0; virtual void exteriorCreated(MWWorld::CellStore& cell) = 0; diff --git a/apps/openmw/mwlua/engineevents.cpp b/apps/openmw/mwlua/engineevents.cpp index 13d3f033e7..569eb22bcf 100644 --- a/apps/openmw/mwlua/engineevents.cpp +++ b/apps/openmw/mwlua/engineevents.cpp @@ -22,8 +22,6 @@ namespace MWLua { } - void operator()(const OnNewGame&) const { mGlobalScripts.newGameStarted(); } - void operator()(const OnActive& event) const { MWWorld::Ptr ptr = getPtr(event.mObject); @@ -50,6 +48,12 @@ namespace MWLua scripts->setActive(false); } + void operator()(const OnTeleported& event) const + { + if (auto* scripts = getLocalScripts(event.mObject)) + scripts->onTeleported(); + } + void operator()(const OnActivate& event) const { MWWorld::Ptr obj = getPtr(event.mObject); diff --git a/apps/openmw/mwlua/engineevents.hpp b/apps/openmw/mwlua/engineevents.hpp index 78892ae19b..ac854abd4a 100644 --- a/apps/openmw/mwlua/engineevents.hpp +++ b/apps/openmw/mwlua/engineevents.hpp @@ -19,9 +19,6 @@ namespace MWLua { } - struct OnNewGame - { - }; struct OnActive { ESM::RefNum mObject; @@ -30,6 +27,10 @@ namespace MWLua { ESM::RefNum mObject; }; + struct OnTeleported + { + ESM::RefNum mObject; + }; struct OnActivate { ESM::RefNum mActor; @@ -44,7 +45,7 @@ namespace MWLua { MWWorld::CellStore& mCell; }; - using Event = std::variant; + using Event = std::variant; 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 26f6b75299..8cf383e985 100644 --- a/apps/openmw/mwlua/localscripts.cpp +++ b/apps/openmw/mwlua/localscripts.cpp @@ -169,8 +169,8 @@ namespace MWLua , mData(obj) { this->addPackage("openmw.self", sol::make_object(lua->sol(), &mData)); - registerEngineHandlers( - { &mOnActiveHandlers, &mOnInactiveHandlers, &mOnConsumeHandlers, &mOnActivatedHandlers }); + registerEngineHandlers({ &mOnActiveHandlers, &mOnInactiveHandlers, &mOnConsumeHandlers, &mOnActivatedHandlers, + &mOnTeleportedHandlers }); } void LocalScripts::setActive(bool active) diff --git a/apps/openmw/mwlua/localscripts.hpp b/apps/openmw/mwlua/localscripts.hpp index ea54321697..6b1555868d 100644 --- a/apps/openmw/mwlua/localscripts.hpp +++ b/apps/openmw/mwlua/localscripts.hpp @@ -70,6 +70,7 @@ namespace MWLua void setActive(bool active); void onConsume(const LObject& consumable) { callEngineHandlers(mOnConsumeHandlers, consumable); } void onActivated(const LObject& actor) { callEngineHandlers(mOnActivatedHandlers, actor); } + void onTeleported() { callEngineHandlers(mOnTeleportedHandlers); } void applyStatsCache(); @@ -81,6 +82,7 @@ namespace MWLua EngineHandlerList mOnInactiveHandlers{ "onInactive" }; EngineHandlerList mOnConsumeHandlers{ "onConsume" }; EngineHandlerList mOnActivatedHandlers{ "onActivated" }; + EngineHandlerList mOnTeleportedHandlers{ "onTeleported" }; }; } diff --git a/apps/openmw/mwlua/luamanagerimp.cpp b/apps/openmw/mwlua/luamanagerimp.cpp index e4828167c4..f7386ca61d 100644 --- a/apps/openmw/mwlua/luamanagerimp.cpp +++ b/apps/openmw/mwlua/luamanagerimp.cpp @@ -178,6 +178,20 @@ namespace MWLua } } + void LuaManager::objectTeleported(const MWWorld::Ptr& ptr) + { + if (ptr == mPlayer) + { + // For player run the onTeleported handler immediately, + // so it can adjust camera position after teleporting. + PlayerScripts* playerScripts = dynamic_cast(mPlayer.getRefData().getLuaScripts()); + if (playerScripts) + playerScripts->onTeleported(); + } + else + mEngineEvents.addToQueue(EngineEvents::OnTeleported{ getId(ptr) }); + } + void LuaManager::questUpdated(const ESM::RefId& questId, int stage) { if (mPlayer.isEmpty()) @@ -194,6 +208,14 @@ namespace MWLua if (mPlayer.isEmpty()) return; // The game is not started yet. + if (mNewGameStarted) + { + mNewGameStarted = false; + // Run onNewGame handler in synchronizedUpdate (at the beginning of the frame), so it + // can teleport the player to the starting location before the first frame is rendered. + mGlobalScripts.newGameStarted(); + } + // We apply input events in `synchronizedUpdate` rather than in `update` in order to reduce input latency. mProcessingInputEvents = true; PlayerScripts* playerScripts = dynamic_cast(mPlayer.getRefData().getLuaScripts()); @@ -237,6 +259,7 @@ namespace MWLua mWorldView.clear(); mGlobalScripts.removeAllScripts(); mGlobalScriptsStarted = false; + mNewGameStarted = false; if (!mPlayer.isEmpty()) { mPlayer.getCellRef().unsetRefNum(); @@ -273,7 +296,7 @@ namespace MWLua mInputEvents.clear(); mGlobalScripts.addAutoStartedScripts(); mGlobalScriptsStarted = true; - mEngineEvents.addToQueue(EngineEvents::OnNewGame{}); + mNewGameStarted = true; } void LuaManager::gameLoaded() diff --git a/apps/openmw/mwlua/luamanagerimp.hpp b/apps/openmw/mwlua/luamanagerimp.hpp index e73a850d63..01315bb9df 100644 --- a/apps/openmw/mwlua/luamanagerimp.hpp +++ b/apps/openmw/mwlua/luamanagerimp.hpp @@ -75,6 +75,7 @@ namespace MWLua { mEngineEvents.addToQueue(EngineEvents::OnNewExterior{ cell }); } + void objectTeleported(const MWWorld::Ptr& ptr) override; void questUpdated(const ESM::RefId& questId, int stage) override; MWBase::LuaManager::ActorControls* getActorControls(const MWWorld::Ptr&) const override; @@ -141,6 +142,7 @@ namespace MWLua bool mInitialized = false; bool mGlobalScriptsStarted = false; bool mProcessingInputEvents = false; + bool mNewGameStarted = false; LuaUtil::ScriptsConfiguration mConfiguration; LuaUtil::LuaState mLua; LuaUi::ResourceManager mUiResourceManager; diff --git a/apps/openmw/mwlua/objectbindings.cpp b/apps/openmw/mwlua/objectbindings.cpp index 84301fe805..7c0606b1d1 100644 --- a/apps/openmw/mwlua/objectbindings.cpp +++ b/apps/openmw/mwlua/objectbindings.cpp @@ -95,6 +95,7 @@ namespace MWLua world->rotateObject(newPtr, rot); if (placeOnGround) world->adjustPosition(newPtr, true); + MWBase::Environment::get().getLuaManager()->objectTeleported(newPtr); } void teleportNotPlayer(const MWWorld::Ptr& ptr, MWWorld::CellStore* destCell, const osg::Vec3f& pos, @@ -119,6 +120,7 @@ namespace MWLua } if (!newPtr.getRefData().isEnabled()) world->enable(newPtr); + MWBase::Environment::get().getLuaManager()->objectTeleported(newPtr); } template diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 5f142856bd..614fbbe26f 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -11,6 +11,7 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/luamanager.hpp" #include "../mwbase/world.hpp" #include "../mwworld/cellstore.hpp" @@ -436,6 +437,7 @@ namespace MWScript bool cellActive = MWBase::Environment::get().getWorldScene()->isCellActive(*ptr.getCell()); ptr.getClass().adjustPosition(ptr, isPlayer || !cellActive); + MWBase::Environment::get().getLuaManager()->objectTeleported(ptr); } }; @@ -492,6 +494,7 @@ namespace MWScript world->rotateObject(ptr, rot); bool cellActive = MWBase::Environment::get().getWorldScene()->isCellActive(*ptr.getCell()); ptr.getClass().adjustPosition(ptr, isPlayer || !cellActive); + MWBase::Environment::get().getLuaManager()->objectTeleported(ptr); } }; diff --git a/apps/openmw/mwworld/actionteleport.cpp b/apps/openmw/mwworld/actionteleport.cpp index 2aaa16cd0b..95c136ac86 100644 --- a/apps/openmw/mwworld/actionteleport.cpp +++ b/apps/openmw/mwworld/actionteleport.cpp @@ -4,6 +4,7 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/luamanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/world.hpp" @@ -65,7 +66,6 @@ namespace MWWorld actor.getClass().getCreatureStats(actor).getAiSequence().stopCombat(); return; } - else teleported = world->moveObject(actor, &worldModel->getCell(mCellId), mPosition.asVec3(), true, true); } @@ -75,6 +75,8 @@ namespace MWWorld .getCreatureStats(teleported) .getActiveSpells() .purgeEffect(actor, ESM::MagicEffect::WaterWalking); + + MWBase::Environment::get().getLuaManager()->objectTeleported(teleported); } void ActionTeleport::getFollowers( diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index e3c35ef1cf..00465cc69a 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -974,7 +974,6 @@ namespace MWWorld removeContainerScripts(getPlayerPtr()); mWorldScene->changeToInteriorCell(cellName, position, adjustPlayerPos, changeEvent); addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell()); - mRendering->getCamera()->instantTransition(); } void World::changeToCell( @@ -999,7 +998,6 @@ namespace MWWorld else mWorldScene->changeToInteriorCell(destinationCell->getNameId(), position, adjustPlayerPos, changeEvent); addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell()); - mRendering->getCamera()->instantTransition(); } float World::getMaxActivationDistance() const diff --git a/docs/source/reference/lua-scripting/engine_handlers.rst b/docs/source/reference/lua-scripting/engine_handlers.rst index 5668bf7cba..64bfe061ef 100644 --- a/docs/source/reference/lua-scripting/engine_handlers.rst +++ b/docs/source/reference/lua-scripting/engine_handlers.rst @@ -64,6 +64,8 @@ Engine handler is a function defined by a script, that can be called by the engi - | Object became inactive. Since it is inactive the handler | can not access anything nearby, but it is possible to send | an event to global scripts. + * - onTeleported() + - Object was teleported. * - 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 diff --git a/files/data/scripts/omw/camera/camera.lua b/files/data/scripts/omw/camera/camera.lua index 1280a574fa..6fcb46bb2f 100644 --- a/files/data/scripts/omw/camera/camera.lua +++ b/files/data/scripts/omw/camera/camera.lua @@ -281,6 +281,9 @@ return { end move360.onInputAction(action) end, + onTeleported = function() + camera.instantTransition() + end, onActive = init, onLoad = function(data) if data and data.distance then third_person.baseDistance = data.distance end