From f02dd0ef03fa549c2d6bb5b16c622da695b32f3a Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Tue, 13 Jun 2023 17:04:22 +0200 Subject: [PATCH] Implement onNewExterior to spawn fish in generated exteriors --- apps/openmw/mwbase/luamanager.hpp | 2 + apps/openmw/mwlua/engineevents.cpp | 2 + apps/openmw/mwlua/engineevents.hpp | 8 ++- apps/openmw/mwlua/globalscripts.hpp | 4 +- apps/openmw/mwlua/luamanagerimp.hpp | 4 ++ apps/openmw/mwworld/worldmodel.cpp | 19 ++++--- files/data/CMakeLists.txt | 1 + files/data/builtin.omwscripts | 1 + files/data/scripts/omw/cellhandlers.lua | 69 +++++++++++++++++++++++++ 9 files changed, 102 insertions(+), 8 deletions(-) create mode 100644 files/data/scripts/omw/cellhandlers.lua diff --git a/apps/openmw/mwbase/luamanager.hpp b/apps/openmw/mwbase/luamanager.hpp index ccbbd7d4cd..ca0220c6fa 100644 --- a/apps/openmw/mwbase/luamanager.hpp +++ b/apps/openmw/mwbase/luamanager.hpp @@ -11,6 +11,7 @@ namespace MWWorld { + class CellStore; class Ptr; } @@ -47,6 +48,7 @@ namespace MWBase virtual void objectRemovedFromScene(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; // 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/engineevents.cpp b/apps/openmw/mwlua/engineevents.cpp index 0c5abe6cef..13d3f033e7 100644 --- a/apps/openmw/mwlua/engineevents.cpp +++ b/apps/openmw/mwlua/engineevents.cpp @@ -71,6 +71,8 @@ namespace MWLua scripts->onConsume(LObject(consumable)); } + void operator()(const OnNewExterior& event) const { mGlobalScripts.onNewExterior(GCell{ &event.mCell }); } + private: MWWorld::Ptr getPtr(const ESM::RefNum& id) const { diff --git a/apps/openmw/mwlua/engineevents.hpp b/apps/openmw/mwlua/engineevents.hpp index 93b3c16164..78892ae19b 100644 --- a/apps/openmw/mwlua/engineevents.hpp +++ b/apps/openmw/mwlua/engineevents.hpp @@ -5,6 +5,8 @@ #include // defines RefNum that is used as a unique id +#include "../mwworld/cellstore.hpp" + namespace MWLua { class GlobalScripts; @@ -38,7 +40,11 @@ namespace MWLua ESM::RefNum mActor; ESM::RefNum mConsumable; }; - using Event = std::variant; + struct OnNewExterior + { + MWWorld::CellStore& mCell; + }; + using Event = std::variant; void clear() { mQueue.clear(); } void addToQueue(Event e) { mQueue.push_back(std::move(e)); } diff --git a/apps/openmw/mwlua/globalscripts.hpp b/apps/openmw/mwlua/globalscripts.hpp index bc2c19e8ee..0190000586 100644 --- a/apps/openmw/mwlua/globalscripts.hpp +++ b/apps/openmw/mwlua/globalscripts.hpp @@ -20,7 +20,7 @@ namespace MWLua : LuaUtil::ScriptsContainer(lua, "Global") { registerEngineHandlers({ &mObjectActiveHandlers, &mActorActiveHandlers, &mItemActiveHandlers, - &mNewGameHandlers, &mPlayerAddedHandlers, &mOnActivateHandlers }); + &mNewGameHandlers, &mPlayerAddedHandlers, &mOnActivateHandlers, &mOnNewExteriorHandlers }); } void newGameStarted() { callEngineHandlers(mNewGameHandlers); } @@ -32,6 +32,7 @@ namespace MWLua { callEngineHandlers(mOnActivateHandlers, obj, actor); } + void onNewExterior(const GCell& cell) { callEngineHandlers(mOnNewExteriorHandlers, cell); } private: EngineHandlerList mObjectActiveHandlers{ "onObjectActive" }; @@ -40,6 +41,7 @@ namespace MWLua EngineHandlerList mNewGameHandlers{ "onNewGame" }; EngineHandlerList mPlayerAddedHandlers{ "onPlayerAdded" }; EngineHandlerList mOnActivateHandlers{ "onActivate" }; + EngineHandlerList mOnNewExteriorHandlers{ "onNewExterior" }; }; } diff --git a/apps/openmw/mwlua/luamanagerimp.hpp b/apps/openmw/mwlua/luamanagerimp.hpp index 247772bff6..2b23182f41 100644 --- a/apps/openmw/mwlua/luamanagerimp.hpp +++ b/apps/openmw/mwlua/luamanagerimp.hpp @@ -71,6 +71,10 @@ namespace MWLua { mEngineEvents.addToQueue(EngineEvents::OnActivate{ getId(actor), getId(object) }); } + void exteriorCreated(MWWorld::CellStore& cell) override + { + mEngineEvents.addToQueue(EngineEvents::OnNewExterior{ cell }); + } MWBase::LuaManager::ActorControls* getActorControls(const MWWorld::Ptr&) const override; diff --git a/apps/openmw/mwworld/worldmodel.cpp b/apps/openmw/mwworld/worldmodel.cpp index 7f67ad6380..acf2c8bc1a 100644 --- a/apps/openmw/mwworld/worldmodel.cpp +++ b/apps/openmw/mwworld/worldmodel.cpp @@ -19,6 +19,9 @@ #include "cellstore.hpp" #include "esmstore.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/luamanager.hpp" + namespace MWWorld { namespace @@ -55,7 +58,7 @@ namespace MWWorld return store.insert(record); } - Cell createExteriorCell(ESM::ExteriorCellLocation location, ESMStore& store) + std::tuple createExteriorCell(ESM::ExteriorCellLocation location, ESMStore& store) { if (ESM::isEsm4Ext(location.mWorldspace)) { @@ -63,17 +66,19 @@ namespace MWWorld throw std::runtime_error( "Exterior ESM4 world is not found: " + location.mWorldspace.toDebugString()); const ESM4::Cell* cell = store.get().searchExterior(location); - if (cell == nullptr) + bool created = cell == nullptr; + if (created) cell = createEsm4Cell(location, store); assert(cell != nullptr); - return MWWorld::Cell(*cell); + return { MWWorld::Cell(*cell), created }; } const ESM::Cell* cell = store.get().search(location.mX, location.mY); - if (cell == nullptr) + bool created = cell == nullptr; + if (created) cell = createEsmCell(location, store); assert(cell != nullptr); - return Cell(*cell); + return { Cell(*cell), created }; } std::optional createCell(ESM::RefId id, const ESMStore& store) @@ -165,10 +170,12 @@ namespace MWWorld if (it == mExteriors.end()) { - Cell cell = createExteriorCell(location, mStore); + auto [cell, created] = createExteriorCell(location, mStore); const ESM::RefId id = cell.getId(); cellStore = &emplaceCellStore(id, std::move(cell), mStore, mReaders, mCells); mExteriors.emplace(location, cellStore); + if (created) + MWBase::Environment::get().getLuaManager()->exteriorCreated(*cellStore); } else { diff --git a/files/data/CMakeLists.txt b/files/data/CMakeLists.txt index eb61f3c118..ef50b4c783 100644 --- a/files/data/CMakeLists.txt +++ b/files/data/CMakeLists.txt @@ -65,6 +65,7 @@ set(BUILTIN_DATA_FILES scripts/omw/activationhandlers.lua scripts/omw/ai.lua + scripts/omw/cellhandlers.lua scripts/omw/camera/camera.lua scripts/omw/camera/head_bobbing.lua scripts/omw/camera/third_person.lua diff --git a/files/data/builtin.omwscripts b/files/data/builtin.omwscripts index cbc94496cd..23cee0c2d8 100644 --- a/files/data/builtin.omwscripts +++ b/files/data/builtin.omwscripts @@ -7,6 +7,7 @@ PLAYER: scripts/omw/settings/player.lua # Mechanics GLOBAL: scripts/omw/activationhandlers.lua +GLOBAL: scripts/omw/cellhandlers.lua PLAYER: scripts/omw/mechanics/playercontroller.lua PLAYER: scripts/omw/playercontrols.lua PLAYER: scripts/omw/camera/camera.lua diff --git a/files/data/scripts/omw/cellhandlers.lua b/files/data/scripts/omw/cellhandlers.lua new file mode 100644 index 0000000000..dc93af7e05 --- /dev/null +++ b/files/data/scripts/omw/cellhandlers.lua @@ -0,0 +1,69 @@ +local types = require('openmw.types') +local util = require('openmw.util') +local world = require('openmw.world') + +local CELL_SIZE = 8192 + +local function getRandomPosition(cellX, cellY) + local x = math.random(7892) + local y = math.random(7892) + local z = -math.random(1748) + return util.vector3(cellX + x, cellY + y, z) +end + +local function getRandomOffset() + local x = math.random(1000) + local y = math.random(1000) + local z = math.random(1000) + local v = util.vector3(x, y, z) + return v:normalize() * 100 +end + +local function getPlayerLevel() + for i, actor in pairs(world.activeActors) do + if (types.Player.objectIsInstance(actor)) then + return types.Player.stats.level(actor).current + end + end +end + +local function spawnFish(cell) + if (cell.worldSpaceId ~= 'sys::default') then + return + end + local spawnCount = math.random(0, 10) + if (spawnCount < 1) then + return + end + local list = types.LevelledCreature.record('h2o_all_lev-2') + if (list == nil) then + return + end + local cellX = cell.gridX * CELL_SIZE + local cellY = cell.gridY * CELL_SIZE + local level = getPlayerLevel() + if (spawnCount <= 5) then -- spawn a number of random creatures selected from the list + while(spawnCount > 0) do + local id = list:getRandomId(level) + if (id ~= '') then + local ref = world.createObject(id) + ref:teleport(cell, getRandomPosition(cellX, cellY)) + end + spawnCount = spawnCount - 1 + end + else -- spawn a horde of a single creature selected from the list + local id = list:getRandomId(level) + if (id ~= '') then + local basePos = getRandomPosition(cellX, cellY) + while(spawnCount > 0) do + local ref = world.createObject(id) + ref:teleport(cell, basePos + getRandomOffset()) + spawnCount = spawnCount - 1 + end + end + end +end + +return { + engineHandlers = { onNewExterior = spawnFish } +}