Merge branch 'herebedreugh' into 'master'

Spawn creatures in newly generated exteriors

Closes #7413

See merge request OpenMW/openmw!3126
revert-6246b479
psi29a 2 years ago
commit 3a707d8b98

@ -53,6 +53,7 @@
Bug #7243: Get Skyrim.esm loading
Bug #7298: Water ripples from projectiles sometimes are not spawned
Bug #7307: Alchemy "Magic Effect" search string does not match on tool tip for effects related to attributes
Bug #7413: Generated wilderness cells don't spawn fish
Bug #7415: Unbreakable lock discrepancies
Feature #3537: Shader-based water ripples
Feature #5492: Let rain and snow collide with statics

@ -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;

@ -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
{

@ -5,6 +5,8 @@
#include <components/esm3/cellref.hpp> // 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<OnNewGame, OnActive, OnInactive, OnConsume, OnActivate>;
struct OnNewExterior
{
MWWorld::CellStore& mCell;
};
using Event = std::variant<OnNewGame, OnActive, OnInactive, OnConsume, OnActivate, OnNewExterior>;
void clear() { mQueue.clear(); }
void addToQueue(Event e) { mQueue.push_back(std::move(e)); }

@ -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" };
};
}

@ -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;

@ -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<Cell, bool> 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<ESM4::Cell>().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<ESM::Cell>().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<Cell> 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
{

@ -49,6 +49,8 @@ Engine handler is a function defined by a script, that can be called by the engi
| Does not apply to items in inventories or containers.
* - onActivate(object, actor)
- Object is activated by an actor.
* - onNewExterior(cell)
- A new exterior cell not defined by a content file has been generated.
**Only for local scripts**

@ -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

@ -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

@ -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 }
}
Loading…
Cancel
Save