mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-10 12:39:53 +00:00
Reorganize delayed Lua actions
This commit is contained in:
parent
fd6899e91d
commit
a65f8ebbc6
@ -57,7 +57,7 @@ add_openmw_dir (mwscript
|
||||
)
|
||||
|
||||
add_openmw_dir (mwlua
|
||||
luamanagerimp actions object worldview userdataserializer eventqueue
|
||||
luamanagerimp object worldview userdataserializer eventqueue
|
||||
luabindings localscripts playerscripts objectbindings cellbindings asyncbindings settingsbindings
|
||||
camerabindings uibindings inputbindings nearbybindings stats
|
||||
types/types types/door types/actor types/container types/weapon types/npc
|
||||
|
@ -1,180 +0,0 @@
|
||||
#include "actions.hpp"
|
||||
|
||||
#include "localscripts.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/lua/luastate.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include <apps/openmw/mwbase/luamanager.hpp>
|
||||
|
||||
#include <apps/openmw/mwworld/action.hpp>
|
||||
#include <apps/openmw/mwworld/cellstore.hpp>
|
||||
#include <apps/openmw/mwworld/class.hpp>
|
||||
#include <apps/openmw/mwworld/inventorystore.hpp>
|
||||
#include <apps/openmw/mwworld/player.hpp>
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
Action::Action(LuaUtil::LuaState* state)
|
||||
{
|
||||
static const bool luaDebug = Settings::Manager::getBool("lua debug", "Lua");
|
||||
if (luaDebug)
|
||||
mCallerTraceback = state->debugTraceback();
|
||||
}
|
||||
|
||||
void Action::safeApply(WorldView& w) const
|
||||
{
|
||||
try
|
||||
{
|
||||
apply(w);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
Log(Debug::Error) << "Error in " << this->toString() << ": " << e.what();
|
||||
|
||||
if (mCallerTraceback.empty())
|
||||
Log(Debug::Error) << "Set 'lua_debug=true' in settings.cfg to enable action tracebacks";
|
||||
else
|
||||
Log(Debug::Error) << "Caller " << mCallerTraceback;
|
||||
}
|
||||
}
|
||||
|
||||
void TeleportAction::apply(WorldView& worldView) const
|
||||
{
|
||||
MWWorld::CellStore* cell = worldView.findCell(mCell, mPos);
|
||||
if (!cell)
|
||||
throw std::runtime_error(std::string("cell not found: '") + mCell + "'");
|
||||
|
||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||
MWWorld::Ptr obj = worldView.getObjectRegistry()->getPtr(mObject, false);
|
||||
const MWWorld::Class& cls = obj.getClass();
|
||||
bool isPlayer = obj == world->getPlayerPtr();
|
||||
if (cls.isActor())
|
||||
cls.getCreatureStats(obj).land(isPlayer);
|
||||
if (isPlayer)
|
||||
{
|
||||
ESM::Position esmPos;
|
||||
static_assert(sizeof(esmPos) == sizeof(osg::Vec3f) * 2);
|
||||
std::memcpy(esmPos.pos, &mPos, sizeof(osg::Vec3f));
|
||||
std::memcpy(esmPos.rot, &mRot, sizeof(osg::Vec3f));
|
||||
world->getPlayer().setTeleported(true);
|
||||
if (cell->isExterior())
|
||||
world->changeToExteriorCell(esmPos, true);
|
||||
else
|
||||
world->changeToInteriorCell(mCell, esmPos, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
MWWorld::Ptr newObj = world->moveObject(obj, cell, mPos);
|
||||
world->rotateObject(newObj, mRot);
|
||||
}
|
||||
}
|
||||
|
||||
void SetEquipmentAction::apply(WorldView& worldView) const
|
||||
{
|
||||
MWWorld::Ptr actor = worldView.getObjectRegistry()->getPtr(mActor, false);
|
||||
MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor);
|
||||
std::array<bool, MWWorld::InventoryStore::Slots> usedSlots;
|
||||
std::fill(usedSlots.begin(), usedSlots.end(), false);
|
||||
|
||||
static constexpr int anySlot = -1;
|
||||
auto tryEquipToSlot = [&actor, &store, &usedSlots, &worldView](int slot, const Item& item) -> bool
|
||||
{
|
||||
auto old_it = slot != anySlot ? store.getSlot(slot) : store.end();
|
||||
MWWorld::Ptr itemPtr;
|
||||
if (std::holds_alternative<ObjectId>(item))
|
||||
{
|
||||
itemPtr = worldView.getObjectRegistry()->getPtr(std::get<ObjectId>(item), false);
|
||||
if (old_it != store.end() && *old_it == itemPtr)
|
||||
return true; // already equipped
|
||||
if (itemPtr.isEmpty() || itemPtr.getRefData().getCount() == 0 ||
|
||||
itemPtr.getContainerStore() != static_cast<const MWWorld::ContainerStore*>(&store))
|
||||
{
|
||||
Log(Debug::Warning) << "Object" << idToString(std::get<ObjectId>(item)) << " is not in inventory";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string& recordId = std::get<std::string>(item);
|
||||
if (old_it != store.end() && old_it->getCellRef().getRefId() == recordId)
|
||||
return true; // already equipped
|
||||
itemPtr = store.search(recordId);
|
||||
if (itemPtr.isEmpty() || itemPtr.getRefData().getCount() == 0)
|
||||
{
|
||||
Log(Debug::Warning) << "There is no object with recordId='" << recordId << "' in inventory";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto [allowedSlots, _] = itemPtr.getClass().getEquipmentSlots(itemPtr);
|
||||
bool requestedSlotIsAllowed = std::find(allowedSlots.begin(), allowedSlots.end(), slot) != allowedSlots.end();
|
||||
if (!requestedSlotIsAllowed)
|
||||
{
|
||||
auto firstAllowed = std::find_if(allowedSlots.begin(), allowedSlots.end(), [&](int s) { return !usedSlots[s]; });
|
||||
if (firstAllowed == allowedSlots.end())
|
||||
{
|
||||
Log(Debug::Warning) << "No suitable slot for " << ptrToString(itemPtr);
|
||||
return false;
|
||||
}
|
||||
slot = *firstAllowed;
|
||||
}
|
||||
|
||||
// TODO: Refactor InventoryStore to accept Ptr and get rid of this linear search.
|
||||
MWWorld::ContainerStoreIterator it = std::find(store.begin(), store.end(), itemPtr);
|
||||
if (it == store.end()) // should never happen
|
||||
throw std::logic_error("Item not found in container");
|
||||
|
||||
store.equip(slot, it, actor);
|
||||
return requestedSlotIsAllowed; // return true if equipped to requested slot and false if slot was changed
|
||||
};
|
||||
|
||||
for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)
|
||||
{
|
||||
auto old_it = store.getSlot(slot);
|
||||
auto new_it = mEquipment.find(slot);
|
||||
if (new_it == mEquipment.end())
|
||||
{
|
||||
if (old_it != store.end())
|
||||
store.unequipSlot(slot, actor);
|
||||
continue;
|
||||
}
|
||||
if (tryEquipToSlot(slot, new_it->second))
|
||||
usedSlots[slot] = true;
|
||||
}
|
||||
for (const auto& [slot, item] : mEquipment)
|
||||
if (slot >= MWWorld::InventoryStore::Slots)
|
||||
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::unique_ptr<MWWorld::Action> 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);
|
||||
}
|
||||
|
||||
void StatUpdateAction::apply(WorldView& worldView) const
|
||||
{
|
||||
LObject obj(mId, worldView.getObjectRegistry());
|
||||
LocalScripts* scripts = obj.ptr().getRefData().getLuaScripts();
|
||||
if (scripts)
|
||||
scripts->applyStatsCache();
|
||||
}
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
#ifndef MWLUA_ACTIONS_H
|
||||
#define MWLUA_ACTIONS_H
|
||||
|
||||
#include <variant>
|
||||
|
||||
#include "object.hpp"
|
||||
#include "worldview.hpp"
|
||||
|
||||
namespace LuaUtil
|
||||
{
|
||||
class LuaState;
|
||||
}
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
// Some changes to the game world can not be done from the scripting thread (because it runs in parallel with OSG Cull),
|
||||
// so we need to queue it and apply from the main thread. All such changes should be implemented as classes inherited
|
||||
// from MWLua::Action.
|
||||
|
||||
class Action
|
||||
{
|
||||
public:
|
||||
Action(LuaUtil::LuaState* state);
|
||||
virtual ~Action() {}
|
||||
|
||||
void safeApply(WorldView&) const;
|
||||
virtual void apply(WorldView&) const = 0;
|
||||
virtual std::string toString() const = 0;
|
||||
|
||||
private:
|
||||
std::string mCallerTraceback;
|
||||
};
|
||||
|
||||
class TeleportAction final : public Action
|
||||
{
|
||||
public:
|
||||
TeleportAction(LuaUtil::LuaState* state, ObjectId object, std::string cell, const osg::Vec3f& pos, const osg::Vec3f& rot)
|
||||
: Action(state), mObject(object), mCell(std::move(cell)), mPos(pos), mRot(rot) {}
|
||||
|
||||
void apply(WorldView&) const override;
|
||||
std::string toString() const override { return "TeleportAction"; }
|
||||
|
||||
private:
|
||||
ObjectId mObject;
|
||||
std::string mCell;
|
||||
osg::Vec3f mPos;
|
||||
osg::Vec3f mRot;
|
||||
};
|
||||
|
||||
class SetEquipmentAction final : public Action
|
||||
{
|
||||
public:
|
||||
using Item = std::variant<std::string, ObjectId>; // recordId or ObjectId
|
||||
using Equipment = std::map<int, Item>; // slot to item
|
||||
|
||||
SetEquipmentAction(LuaUtil::LuaState* state, ObjectId actor, Equipment equipment)
|
||||
: Action(state), mActor(actor), mEquipment(std::move(equipment)) {}
|
||||
|
||||
void apply(WorldView&) const override;
|
||||
std::string toString() const override { return "SetEquipmentAction"; }
|
||||
|
||||
private:
|
||||
ObjectId mActor;
|
||||
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;
|
||||
};
|
||||
|
||||
class StatUpdateAction final : public Action
|
||||
{
|
||||
ObjectId mId;
|
||||
public:
|
||||
StatUpdateAction(LuaUtil::LuaState* state, ObjectId id) : Action(state), mId(id) {}
|
||||
|
||||
void apply(WorldView& worldView) const override;
|
||||
|
||||
std::string toString() const override { return "StatUpdateAction"; }
|
||||
};
|
||||
}
|
||||
|
||||
#endif // MWLUA_ACTIONS_H
|
@ -501,4 +501,50 @@ namespace MWLua
|
||||
scripts->receiveEngineEvent(LocalScripts::OnActive());
|
||||
}
|
||||
|
||||
LuaManager::Action::Action(LuaUtil::LuaState* state)
|
||||
{
|
||||
static const bool luaDebug = Settings::Manager::getBool("lua debug", "Lua");
|
||||
if (luaDebug)
|
||||
mCallerTraceback = state->debugTraceback();
|
||||
}
|
||||
|
||||
void LuaManager::Action::safeApply(WorldView& w) const
|
||||
{
|
||||
try
|
||||
{
|
||||
apply(w);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
Log(Debug::Error) << "Error in " << this->toString() << ": " << e.what();
|
||||
|
||||
if (mCallerTraceback.empty())
|
||||
Log(Debug::Error) << "Set 'lua_debug=true' in settings.cfg to enable action tracebacks";
|
||||
else
|
||||
Log(Debug::Error) << "Caller " << mCallerTraceback;
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class FunctionAction final : public LuaManager::Action
|
||||
{
|
||||
public:
|
||||
FunctionAction(LuaUtil::LuaState* state, std::function<void()> fn, std::string_view name)
|
||||
: Action(state), mFn(std::move(fn)), mName(name) {}
|
||||
|
||||
void apply(WorldView&) const override { mFn(); }
|
||||
std::string toString() const override { return "FunctionAction " + mName; }
|
||||
|
||||
private:
|
||||
std::function<void()> mFn;
|
||||
std::string mName;
|
||||
};
|
||||
}
|
||||
|
||||
void LuaManager::addAction(std::function<void()> action, std::string_view name)
|
||||
{
|
||||
mActionQueue.push_back(std::make_unique<FunctionAction>(&mLua, std::move(action), name));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,7 +12,6 @@
|
||||
|
||||
#include "../mwbase/luamanager.hpp"
|
||||
|
||||
#include "actions.hpp"
|
||||
#include "object.hpp"
|
||||
#include "eventqueue.hpp"
|
||||
#include "globalscripts.hpp"
|
||||
@ -60,9 +59,28 @@ namespace MWLua
|
||||
|
||||
// Used only in Lua bindings
|
||||
void addCustomLocalScript(const MWWorld::Ptr&, int scriptId);
|
||||
void addAction(std::unique_ptr<Action>&& action) { mActionQueue.push_back(std::move(action)); }
|
||||
void addTeleportPlayerAction(std::unique_ptr<TeleportAction>&& action) { mTeleportPlayerAction = std::move(action); }
|
||||
void addUIMessage(std::string_view message) { mUIMessages.emplace_back(message); }
|
||||
|
||||
// Some changes to the game world can not be done from the scripting thread (because it runs in parallel with OSG Cull),
|
||||
// so we need to queue it and apply from the main thread. All such changes should be implemented as classes inherited
|
||||
// from MWLua::Action.
|
||||
class Action
|
||||
{
|
||||
public:
|
||||
Action(LuaUtil::LuaState* state);
|
||||
virtual ~Action() {}
|
||||
|
||||
void safeApply(WorldView&) const;
|
||||
virtual void apply(WorldView&) const = 0;
|
||||
virtual std::string toString() const = 0;
|
||||
|
||||
private:
|
||||
std::string mCallerTraceback;
|
||||
};
|
||||
|
||||
void addAction(std::function<void()> action, std::string_view name = "");
|
||||
void addAction(std::unique_ptr<Action>&& action) { mActionQueue.push_back(std::move(action)); }
|
||||
void addTeleportPlayerAction(std::unique_ptr<Action>&& action) { mTeleportPlayerAction = std::move(action); }
|
||||
|
||||
// Saving
|
||||
void write(ESM::ESMWriter& writer, Loading::Listener& progress) override;
|
||||
@ -149,7 +167,7 @@ namespace MWLua
|
||||
|
||||
// Queued actions that should be done in main thread. Processed by applyQueuedChanges().
|
||||
std::vector<std::unique_ptr<Action>> mActionQueue;
|
||||
std::unique_ptr<TeleportAction> mTeleportPlayerAction;
|
||||
std::unique_ptr<Action> mTeleportPlayerAction;
|
||||
std::vector<std::string> mUIMessages;
|
||||
|
||||
LuaUtil::LuaStorage mGlobalStorage{mLua.sol()};
|
||||
|
@ -2,9 +2,12 @@
|
||||
|
||||
#include <components/lua/luastate.hpp>
|
||||
|
||||
#include "../mwworld/action.hpp"
|
||||
#include "../mwworld/cellstore.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/containerstore.hpp"
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
#include "../mwworld/player.hpp"
|
||||
|
||||
#include "eventqueue.hpp"
|
||||
#include "luamanagerimp.hpp"
|
||||
@ -31,6 +34,83 @@ namespace MWLua
|
||||
|
||||
namespace {
|
||||
|
||||
class TeleportAction final : public LuaManager::Action
|
||||
{
|
||||
public:
|
||||
TeleportAction(LuaUtil::LuaState* state, ObjectId object, std::string cell, const osg::Vec3f& pos, const osg::Vec3f& rot)
|
||||
: Action(state), mObject(object), mCell(std::move(cell)), mPos(pos), mRot(rot) {}
|
||||
|
||||
void apply(WorldView& worldView) const override
|
||||
{
|
||||
MWWorld::CellStore* cell = worldView.findCell(mCell, mPos);
|
||||
if (!cell)
|
||||
throw std::runtime_error(std::string("cell not found: '") + mCell + "'");
|
||||
|
||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||
MWWorld::Ptr obj = worldView.getObjectRegistry()->getPtr(mObject, false);
|
||||
const MWWorld::Class& cls = obj.getClass();
|
||||
bool isPlayer = obj == world->getPlayerPtr();
|
||||
if (cls.isActor())
|
||||
cls.getCreatureStats(obj).land(isPlayer);
|
||||
if (isPlayer)
|
||||
{
|
||||
ESM::Position esmPos;
|
||||
static_assert(sizeof(esmPos) == sizeof(osg::Vec3f) * 2);
|
||||
std::memcpy(esmPos.pos, &mPos, sizeof(osg::Vec3f));
|
||||
std::memcpy(esmPos.rot, &mRot, sizeof(osg::Vec3f));
|
||||
world->getPlayer().setTeleported(true);
|
||||
if (cell->isExterior())
|
||||
world->changeToExteriorCell(esmPos, true);
|
||||
else
|
||||
world->changeToInteriorCell(mCell, esmPos, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
MWWorld::Ptr newObj = world->moveObject(obj, cell, mPos);
|
||||
world->rotateObject(newObj, mRot);
|
||||
}
|
||||
}
|
||||
|
||||
std::string toString() const override { return "TeleportAction"; }
|
||||
|
||||
private:
|
||||
ObjectId mObject;
|
||||
std::string mCell;
|
||||
osg::Vec3f mPos;
|
||||
osg::Vec3f mRot;
|
||||
};
|
||||
|
||||
class ActivateAction final : public LuaManager::Action
|
||||
{
|
||||
public:
|
||||
ActivateAction(LuaUtil::LuaState* state, ObjectId object, ObjectId actor)
|
||||
: Action(state), mObject(object), mActor(actor) {}
|
||||
|
||||
void apply(WorldView& worldView) const override
|
||||
{
|
||||
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::unique_ptr<MWWorld::Action> action = object.getClass().activate(object, actor);
|
||||
action->execute(actor);
|
||||
}
|
||||
|
||||
std::string toString() const override
|
||||
{
|
||||
return std::string("ActivateAction object=") + idToString(mObject) +
|
||||
std::string(" actor=") + idToString(mActor);
|
||||
}
|
||||
|
||||
private:
|
||||
ObjectId mObject;
|
||||
ObjectId mActor;
|
||||
};
|
||||
|
||||
template <typename ObjT>
|
||||
using Cell = std::conditional_t<std::is_same_v<ObjT, LObject>, LCell, GCell>;
|
||||
|
||||
|
@ -79,6 +79,26 @@ namespace
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
namespace
|
||||
{
|
||||
class StatUpdateAction final : public LuaManager::Action
|
||||
{
|
||||
ObjectId mId;
|
||||
public:
|
||||
StatUpdateAction(LuaUtil::LuaState* state, ObjectId id) : Action(state), mId(id) {}
|
||||
|
||||
void apply(WorldView& worldView) const override
|
||||
{
|
||||
LObject obj(mId, worldView.getObjectRegistry());
|
||||
LocalScripts* scripts = obj.ptr().getRefData().getLuaScripts();
|
||||
if (scripts)
|
||||
scripts->applyStatsCache();
|
||||
}
|
||||
|
||||
std::string toString() const override { return "StatUpdateAction"; }
|
||||
};
|
||||
}
|
||||
|
||||
class LevelStat
|
||||
{
|
||||
StatObject mObject;
|
||||
|
@ -6,7 +6,6 @@
|
||||
#include <apps/openmw/mwworld/inventorystore.hpp>
|
||||
#include <apps/openmw/mwworld/class.hpp>
|
||||
|
||||
#include "../actions.hpp"
|
||||
#include "../luabindings.hpp"
|
||||
#include "../localscripts.hpp"
|
||||
#include "../luamanagerimp.hpp"
|
||||
@ -14,6 +13,102 @@
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
namespace
|
||||
{
|
||||
class SetEquipmentAction final : public LuaManager::Action
|
||||
{
|
||||
public:
|
||||
using Item = std::variant<std::string, ObjectId>; // recordId or ObjectId
|
||||
using Equipment = std::map<int, Item>; // slot to item
|
||||
|
||||
SetEquipmentAction(LuaUtil::LuaState* state, ObjectId actor, Equipment equipment)
|
||||
: Action(state), mActor(actor), mEquipment(std::move(equipment)) {}
|
||||
|
||||
void apply(WorldView& worldView) const override
|
||||
{
|
||||
MWWorld::Ptr actor = worldView.getObjectRegistry()->getPtr(mActor, false);
|
||||
MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor);
|
||||
std::array<bool, MWWorld::InventoryStore::Slots> usedSlots;
|
||||
std::fill(usedSlots.begin(), usedSlots.end(), false);
|
||||
|
||||
static constexpr int anySlot = -1;
|
||||
auto tryEquipToSlot = [&actor, &store, &usedSlots, &worldView](int slot, const Item& item) -> bool
|
||||
{
|
||||
auto old_it = slot != anySlot ? store.getSlot(slot) : store.end();
|
||||
MWWorld::Ptr itemPtr;
|
||||
if (std::holds_alternative<ObjectId>(item))
|
||||
{
|
||||
itemPtr = worldView.getObjectRegistry()->getPtr(std::get<ObjectId>(item), false);
|
||||
if (old_it != store.end() && *old_it == itemPtr)
|
||||
return true; // already equipped
|
||||
if (itemPtr.isEmpty() || itemPtr.getRefData().getCount() == 0 ||
|
||||
itemPtr.getContainerStore() != static_cast<const MWWorld::ContainerStore*>(&store))
|
||||
{
|
||||
Log(Debug::Warning) << "Object" << idToString(std::get<ObjectId>(item)) << " is not in inventory";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string& recordId = std::get<std::string>(item);
|
||||
if (old_it != store.end() && old_it->getCellRef().getRefId() == recordId)
|
||||
return true; // already equipped
|
||||
itemPtr = store.search(recordId);
|
||||
if (itemPtr.isEmpty() || itemPtr.getRefData().getCount() == 0)
|
||||
{
|
||||
Log(Debug::Warning) << "There is no object with recordId='" << recordId << "' in inventory";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto [allowedSlots, _] = itemPtr.getClass().getEquipmentSlots(itemPtr);
|
||||
bool requestedSlotIsAllowed = std::find(allowedSlots.begin(), allowedSlots.end(), slot) != allowedSlots.end();
|
||||
if (!requestedSlotIsAllowed)
|
||||
{
|
||||
auto firstAllowed = std::find_if(allowedSlots.begin(), allowedSlots.end(), [&](int s) { return !usedSlots[s]; });
|
||||
if (firstAllowed == allowedSlots.end())
|
||||
{
|
||||
Log(Debug::Warning) << "No suitable slot for " << ptrToString(itemPtr);
|
||||
return false;
|
||||
}
|
||||
slot = *firstAllowed;
|
||||
}
|
||||
|
||||
// TODO: Refactor InventoryStore to accept Ptr and get rid of this linear search.
|
||||
MWWorld::ContainerStoreIterator it = std::find(store.begin(), store.end(), itemPtr);
|
||||
if (it == store.end()) // should never happen
|
||||
throw std::logic_error("Item not found in container");
|
||||
|
||||
store.equip(slot, it, actor);
|
||||
return requestedSlotIsAllowed; // return true if equipped to requested slot and false if slot was changed
|
||||
};
|
||||
|
||||
for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)
|
||||
{
|
||||
auto old_it = store.getSlot(slot);
|
||||
auto new_it = mEquipment.find(slot);
|
||||
if (new_it == mEquipment.end())
|
||||
{
|
||||
if (old_it != store.end())
|
||||
store.unequipSlot(slot, actor);
|
||||
continue;
|
||||
}
|
||||
if (tryEquipToSlot(slot, new_it->second))
|
||||
usedSlots[slot] = true;
|
||||
}
|
||||
for (const auto& [slot, item] : mEquipment)
|
||||
if (slot >= MWWorld::InventoryStore::Slots)
|
||||
tryEquipToSlot(anySlot, item);
|
||||
}
|
||||
|
||||
std::string toString() const override { return "SetEquipmentAction"; }
|
||||
|
||||
private:
|
||||
ObjectId mActor;
|
||||
Equipment mEquipment;
|
||||
};
|
||||
}
|
||||
|
||||
using SelfObject = LocalScripts::SelfObject;
|
||||
|
||||
void addActorBindings(sol::table actor, const Context& context)
|
||||
|
@ -10,14 +10,13 @@
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
#include "actions.hpp"
|
||||
#include "luamanagerimp.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
namespace
|
||||
{
|
||||
class UiAction final : public Action
|
||||
class UiAction final : public LuaManager::Action
|
||||
{
|
||||
public:
|
||||
enum Type
|
||||
@ -81,7 +80,7 @@ namespace MWLua
|
||||
std::shared_ptr<LuaUi::Element> mElement;
|
||||
};
|
||||
|
||||
class InsertLayerAction final : public Action
|
||||
class InsertLayerAction final : public LuaManager::Action
|
||||
{
|
||||
public:
|
||||
InsertLayerAction(std::string_view name, size_t index,
|
||||
|
Loading…
x
Reference in New Issue
Block a user