mirror of
https://github.com/OpenMW/openmw.git
synced 2025-02-28 13:09:42 +00:00
This commit is contained in:
parent
e7120f189b
commit
c294898246
13 changed files with 215 additions and 28 deletions
|
@ -16,6 +16,7 @@
|
|||
Bug #6645: Enemy block sounds align with animation instead of blocked hits
|
||||
Bug #6661: Saved games that have no preview screenshot cause issues or crashes
|
||||
Bug #6807: Ultimate Galleon is not working properly
|
||||
Bug #6893: Lua: Inconsistent behavior with actors affected by Disable and SetDelete commands
|
||||
Bug #6939: OpenMW-CS: ID columns are too short
|
||||
Bug #6949: Sun Damage effect doesn't work in quasi exteriors
|
||||
Bug #6964: Nerasa Dralor Won't Follow
|
||||
|
@ -34,6 +35,7 @@
|
|||
Bug #7088: Deleting last save game of last character doesn't clear character name/details
|
||||
Feature #5492: Let rain and snow collide with statics
|
||||
Feature #6447: Add LOD support to Object Paging
|
||||
Feature #6726: Lua API for creating new objects
|
||||
Feature #6922: Improve launcher appearance
|
||||
Feature #6933: Support high-resolution cursor textures
|
||||
Feature #6945: Support S3TC-compressed and BGR/BGRA NiPixelData
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "../mwworld/esmstore.hpp"
|
||||
#include "../mwworld/manualref.hpp"
|
||||
#include "../mwworld/nullaction.hpp"
|
||||
#include "../mwworld/worldmodel.hpp"
|
||||
|
||||
#include "../mwgui/tooltips.hpp"
|
||||
#include "../mwgui/ustring.hpp"
|
||||
|
@ -208,6 +209,7 @@ namespace MWClass
|
|||
newPtr.getRefData().setCount(count);
|
||||
}
|
||||
newPtr.getCellRef().unsetRefNum();
|
||||
MWBase::Environment::get().getWorldModel()->registerPtr(newPtr);
|
||||
|
||||
return newPtr;
|
||||
}
|
||||
|
|
|
@ -75,6 +75,8 @@ namespace MWLua
|
|||
const CellT& cell, sol::optional<sol::table> type) {
|
||||
ObjectIdList res = std::make_shared<std::vector<ObjectId>>();
|
||||
auto visitor = [&](const MWWorld::Ptr& ptr) {
|
||||
if (ptr.getRefData().isDeleted())
|
||||
return true;
|
||||
MWBase::Environment::get().getWorldModel()->registerPtr(ptr);
|
||||
if (getLiveCellRefType(ptr.mRef) == ptr.getType())
|
||||
res->push_back(getId(ptr));
|
||||
|
|
|
@ -23,6 +23,7 @@ namespace MWLua
|
|||
LocalScripts(LuaUtil::LuaState* lua, const LObject& obj);
|
||||
|
||||
MWBase::LuaManager::ActorControls* getActorControls() { return &mData.mControls; }
|
||||
const MWWorld::Ptr& getPtr() const { return mData.ptr(); }
|
||||
|
||||
struct SelfObject : public LObject
|
||||
{
|
||||
|
|
|
@ -7,7 +7,10 @@
|
|||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/statemanager.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
#include "../mwworld/manualref.hpp"
|
||||
#include "../mwworld/scene.hpp"
|
||||
#include "../mwworld/store.hpp"
|
||||
|
||||
#include "eventqueue.hpp"
|
||||
|
@ -48,7 +51,7 @@ namespace MWLua
|
|||
{
|
||||
auto* lua = context.mLua;
|
||||
sol::table api(lua->sol(), sol::create);
|
||||
api["API_REVISION"] = 32;
|
||||
api["API_REVISION"] = 33;
|
||||
api["quit"] = [lua]() {
|
||||
Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback();
|
||||
MWBase::Environment::get().getStateManager()->requestQuit();
|
||||
|
@ -83,7 +86,17 @@ namespace MWLua
|
|||
api["getExteriorCell"]
|
||||
= [](int x, int y) { return GCell{ MWBase::Environment::get().getWorldModel()->getExterior(x, y) }; };
|
||||
api["activeActors"] = GObjectList{ worldView->getActorsInScene() };
|
||||
// TODO: add world.placeNewObject(recordId, cell, pos, [rot])
|
||||
api["createObject"] = [](std::string_view recordId, sol::optional<int> count) -> GObject {
|
||||
// Doesn't matter which cell to use because the new object will be in disabled state.
|
||||
MWWorld::CellStore* cell = MWBase::Environment::get().getWorldScene()->getCurrentCell();
|
||||
|
||||
MWWorld::ManualRef mref(
|
||||
MWBase::Environment::get().getWorld()->getStore(), ESM::RefId::stringRefId(recordId));
|
||||
const MWWorld::Ptr& ptr = mref.getPtr();
|
||||
ptr.getRefData().disable();
|
||||
MWWorld::Ptr newPtr = ptr.getClass().copyToCell(ptr, *cell, count.value_or(1));
|
||||
return GObject(getId(newPtr));
|
||||
};
|
||||
return LuaUtil::makeReadOnly(api);
|
||||
}
|
||||
|
||||
|
|
|
@ -154,6 +154,9 @@ namespace MWLua
|
|||
|
||||
mWorldView.update();
|
||||
|
||||
std::erase_if(mActiveLocalScripts,
|
||||
[](const LocalScripts* l) { return l->getPtr().isEmpty() || l->getPtr().getRefData().isDeleted(); });
|
||||
|
||||
mGlobalScripts.statsNextFrame();
|
||||
for (LocalScripts* scripts : mActiveLocalScripts)
|
||||
scripts->statsNextFrame();
|
||||
|
|
|
@ -20,6 +20,8 @@ namespace MWLua
|
|||
std::string ptrToString(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
std::string res = "object";
|
||||
if (ptr.getRefData().isDeleted())
|
||||
res = "deleted object";
|
||||
res.append(idToString(getId(ptr)));
|
||||
res.append(" (");
|
||||
res.append(getLuaObjectTypeName(ptr));
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/containerstore.hpp"
|
||||
#include "../mwworld/player.hpp"
|
||||
#include "../mwworld/scene.hpp"
|
||||
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
|
||||
|
@ -87,6 +88,8 @@ namespace MWLua
|
|||
{
|
||||
MWWorld::Ptr newObj = world->moveObject(obj, cell, mPos);
|
||||
world->rotateObject(newObj, mRot);
|
||||
if (!newObj.getRefData().isEnabled())
|
||||
world->enable(newObj);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -198,6 +201,32 @@ namespace MWLua
|
|||
context.mLuaManager->addAction(std::make_unique<ActivateAction>(context.mLua, o.id(), actor.id()));
|
||||
};
|
||||
|
||||
auto isEnabled = [](const ObjectT& o) { return o.ptr().getRefData().isEnabled(); };
|
||||
auto setEnabled = [context](const GObject& object, bool enable) {
|
||||
if (enable && object.ptr().getRefData().isDeleted())
|
||||
throw std::runtime_error("Object is removed");
|
||||
context.mLuaManager->addAction([object, enable] {
|
||||
if (object.ptr().isInCell())
|
||||
{
|
||||
if (enable)
|
||||
MWBase::Environment::get().getWorld()->enable(object.ptr());
|
||||
else
|
||||
MWBase::Environment::get().getWorld()->disable(object.ptr());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (enable)
|
||||
object.ptr().getRefData().enable();
|
||||
else
|
||||
throw std::runtime_error("Objects in containers can't be disabled");
|
||||
}
|
||||
});
|
||||
};
|
||||
if constexpr (std::is_same_v<ObjectT, GObject>)
|
||||
objectT["enabled"] = sol::property(isEnabled, setEnabled);
|
||||
else
|
||||
objectT["enabled"] = sol::readonly_property(isEnabled);
|
||||
|
||||
if constexpr (std::is_same_v<ObjectT, GObject>)
|
||||
{ // Only for global scripts
|
||||
objectT["addScript"] = [context](const GObject& object, std::string_view path, sol::object initData) {
|
||||
|
@ -243,11 +272,74 @@ namespace MWLua
|
|||
localScripts->removeScript(*scriptId);
|
||||
};
|
||||
|
||||
objectT["teleport"] = [context](const GObject& object, std::string_view cell, const osg::Vec3f& pos,
|
||||
const sol::optional<osg::Vec3f>& optRot) {
|
||||
auto removeFn = [context](const GObject& object, int countToRemove) {
|
||||
MWWorld::Ptr ptr = object.ptr();
|
||||
int currentCount = ptr.getRefData().getCount();
|
||||
if (countToRemove <= 0 || countToRemove > currentCount)
|
||||
throw std::runtime_error("Can't remove " + std::to_string(countToRemove) + " of "
|
||||
+ std::to_string(currentCount) + " items");
|
||||
ptr.getRefData().setCount(currentCount - countToRemove); // Immediately change count
|
||||
if (ptr.getContainerStore() || currentCount == countToRemove)
|
||||
{
|
||||
// Delayed action to trigger side effects
|
||||
context.mLuaManager->addAction([object, countToRemove] {
|
||||
MWWorld::Ptr ptr = object.ptr();
|
||||
// Restore original count
|
||||
ptr.getRefData().setCount(ptr.getRefData().getCount() + countToRemove);
|
||||
// And now remove properly
|
||||
if (ptr.getContainerStore())
|
||||
ptr.getContainerStore()->remove(ptr, countToRemove);
|
||||
else
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->disable(object.ptr());
|
||||
MWBase::Environment::get().getWorld()->deleteObject(ptr);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
objectT["remove"] = [removeFn](const GObject& object, sol::optional<int> count) {
|
||||
removeFn(object, count.value_or(object.ptr().getRefData().getCount()));
|
||||
};
|
||||
objectT["split"] = [removeFn](const GObject& object, int count) -> GObject {
|
||||
removeFn(object, count);
|
||||
|
||||
// Doesn't matter which cell to use because the new instance will be in disabled state.
|
||||
MWWorld::CellStore* cell = MWBase::Environment::get().getWorldScene()->getCurrentCell();
|
||||
|
||||
const MWWorld::Ptr& ptr = object.ptr();
|
||||
MWWorld::Ptr splitted = ptr.getClass().copyToCell(ptr, *cell, count);
|
||||
splitted.getRefData().disable();
|
||||
return GObject(getId(splitted));
|
||||
};
|
||||
objectT["moveInto"] = [removeFn, context](const GObject& object, const Inventory<GObject>& inventory) {
|
||||
// Currently moving to or from containers makes a copy and removes the original.
|
||||
// TODO(#6148): actually move rather than copy and preserve RefNum
|
||||
int count = object.ptr().getRefData().getCount();
|
||||
removeFn(object, count);
|
||||
context.mLuaManager->addAction([item = object, count, cont = inventory.mObj] {
|
||||
auto& refData = item.ptr().getRefData();
|
||||
refData.setCount(count); // temporarily undo removal to run ContainerStore::add
|
||||
cont.ptr().getClass().getContainerStore(cont.ptr()).add(item.ptr(), count, false);
|
||||
refData.setCount(0);
|
||||
});
|
||||
};
|
||||
objectT["teleport"] = [removeFn, context](const GObject& object, std::string_view cell,
|
||||
const osg::Vec3f& pos, const sol::optional<osg::Vec3f>& optRot) {
|
||||
MWWorld::Ptr ptr = object.ptr();
|
||||
if (ptr.getRefData().isDeleted())
|
||||
throw std::runtime_error("Object is removed");
|
||||
if (ptr.getContainerStore())
|
||||
{
|
||||
// Currently moving to or from containers makes a copy and removes the original.
|
||||
// TODO(#6148): actually move rather than copy and preserve RefNum
|
||||
auto* cellStore = MWBase::Environment::get().getWorldModel()->getCellByPosition(pos, cell);
|
||||
MWWorld::Ptr newPtr = ptr.getClass().copyToCell(ptr, *cellStore, ptr.getRefData().getCount());
|
||||
newPtr.getRefData().disable();
|
||||
removeFn(object, ptr.getRefData().getCount());
|
||||
ptr = newPtr;
|
||||
}
|
||||
osg::Vec3f rot = optRot ? *optRot : ptr.getRefData().getPosition().asRotationVec3();
|
||||
auto action = std::make_unique<TeleportAction>(context.mLua, object.id(), cell, pos, rot);
|
||||
auto action = std::make_unique<TeleportAction>(context.mLua, getId(ptr), cell, pos, rot);
|
||||
if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr())
|
||||
context.mLuaManager->addTeleportPlayerAction(std::move(action));
|
||||
else
|
||||
|
@ -335,24 +427,40 @@ namespace MWLua
|
|||
return ObjectList<ObjectT>{ list };
|
||||
};
|
||||
|
||||
inventoryT["countOf"] = [](const InventoryT& inventory, const std::string& recordId) {
|
||||
inventoryT["countOf"] = [](const InventoryT& inventory, std::string_view recordId) {
|
||||
const MWWorld::Ptr& ptr = inventory.mObj.ptr();
|
||||
MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr);
|
||||
return store.count(ESM::RefId::stringRefId(recordId));
|
||||
};
|
||||
|
||||
if constexpr (std::is_same_v<ObjectT, GObject>)
|
||||
{ // Only for global scripts
|
||||
// TODO
|
||||
// obj.inventory:drop(obj2, [count])
|
||||
// obj.inventory:drop(recordId, [count])
|
||||
// obj.inventory:addNew(recordId, [count])
|
||||
// obj.inventory:remove(obj/recordId, [count])
|
||||
/*objectT["moveInto"] = [](const GObject& obj, const InventoryT& inventory) {};
|
||||
inventoryT["drop"] = [](const InventoryT& inventory) {};
|
||||
inventoryT["addNew"] = [](const InventoryT& inventory) {};
|
||||
inventoryT["remove"] = [](const InventoryT& inventory) {};*/
|
||||
}
|
||||
inventoryT["find"] = [](const InventoryT& inventory, std::string_view recordId) -> sol::optional<ObjectT> {
|
||||
const MWWorld::Ptr& ptr = inventory.mObj.ptr();
|
||||
MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr);
|
||||
auto itemId = ESM::RefId::stringRefId(recordId);
|
||||
for (const MWWorld::Ptr& item : store)
|
||||
{
|
||||
if (item.getCellRef().getRefId() == itemId)
|
||||
{
|
||||
MWBase::Environment::get().getWorldModel()->registerPtr(item);
|
||||
return ObjectT(getId(item));
|
||||
}
|
||||
}
|
||||
return sol::nullopt;
|
||||
};
|
||||
inventoryT["findAll"] = [](const InventoryT& inventory, std::string_view recordId) {
|
||||
const MWWorld::Ptr& ptr = inventory.mObj.ptr();
|
||||
MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr);
|
||||
auto itemId = ESM::RefId::stringRefId(recordId);
|
||||
ObjectIdList list = std::make_shared<std::vector<ObjectId>>();
|
||||
for (const MWWorld::Ptr& item : store)
|
||||
{
|
||||
if (item.getCellRef().getRefId() == itemId)
|
||||
{
|
||||
MWBase::Environment::get().getWorldModel()->registerPtr(item);
|
||||
list->push_back(getId(item));
|
||||
}
|
||||
}
|
||||
return ObjectList<ObjectT>{ list };
|
||||
};
|
||||
}
|
||||
|
||||
template <class ObjectT>
|
||||
|
|
|
@ -267,8 +267,7 @@ namespace
|
|||
iter->mData.enable();
|
||||
MWBase::Environment::get().getWorld()->disable(ptr);
|
||||
}
|
||||
else
|
||||
MWBase::Environment::get().getWorldModel()->registerPtr(ptr);
|
||||
MWBase::Environment::get().getWorldModel()->registerPtr(ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "inventorystore.hpp"
|
||||
#include "nullaction.hpp"
|
||||
#include "ptr.hpp"
|
||||
#include "worldmodel.hpp"
|
||||
|
||||
#include "../mwgui/tooltips.hpp"
|
||||
|
||||
|
@ -373,6 +374,7 @@ namespace MWWorld
|
|||
Ptr newPtr = copyToCellImpl(ptr, cell);
|
||||
newPtr.getCellRef().unsetRefNum(); // This RefNum is only valid within the original cell of the reference
|
||||
newPtr.getRefData().setCount(count);
|
||||
MWBase::Environment::get().getWorldModel()->registerPtr(newPtr);
|
||||
if (hasInventoryStore(newPtr))
|
||||
getInventoryStore(newPtr).setActor(newPtr);
|
||||
return newPtr;
|
||||
|
|
|
@ -788,8 +788,6 @@ namespace MWWorld
|
|||
|
||||
void World::enable(const Ptr& reference)
|
||||
{
|
||||
MWBase::Environment::get().getWorldModel()->registerPtr(reference);
|
||||
|
||||
if (!reference.isInCell())
|
||||
return;
|
||||
|
||||
|
@ -840,7 +838,6 @@ namespace MWWorld
|
|||
if (reference == getPlayerPtr())
|
||||
throw std::runtime_error("can not disable player object");
|
||||
|
||||
MWBase::Environment::get().getWorldModel()->deregisterPtr(reference);
|
||||
reference.getRefData().disable();
|
||||
|
||||
if (reference.getCellRef().getRefNum().hasContentFile())
|
||||
|
|
|
@ -80,7 +80,6 @@
|
|||
-- @usage
|
||||
-- # DataFiles/l10n/MyMod/en.yaml
|
||||
-- good_morning: 'Good morning.'
|
||||
--
|
||||
-- you_have_arrows: |-
|
||||
-- {count, plural,
|
||||
-- one {You have one arrow.}
|
||||
|
@ -107,11 +106,12 @@
|
|||
-- Any object that exists in the game world and has a specific location.
|
||||
-- Player, actors, items, and statics are game objects.
|
||||
-- @type GameObject
|
||||
-- @field #boolean enabled Whether the object is enabled or disabled. Global scripts can set the value. Items in containers or inventories can't be disabled.
|
||||
-- @field openmw.util#Vector3 position Object position.
|
||||
-- @field openmw.util#Vector3 rotation Object rotation (ZXY order).
|
||||
-- @field #Cell cell The cell where the object currently is. During loading a game and for objects in an inventory or a container `cell` is nil.
|
||||
-- @field #table type Type of the object (one of the tables from the package @{openmw.types#types}).
|
||||
-- @field #number count Count (makes sense if stored in a container).
|
||||
-- @field #number count Count (>1 means a stack of objects).
|
||||
-- @field #string recordId Returns record ID of the object in lowercase.
|
||||
|
||||
---
|
||||
|
@ -163,13 +163,39 @@
|
|||
|
||||
---
|
||||
-- Moves object to given cell and position.
|
||||
-- Can be called only from a global script.
|
||||
-- The effect is not immediate: the position will be updated only in the next
|
||||
-- frame. Can be called only from a global script.
|
||||
-- frame. Can be called only from a global script. Enables object if it was disabled.
|
||||
-- Can be used to move objects from an inventory or a container to the world.
|
||||
-- @function [parent=#GameObject] teleport
|
||||
-- @param self
|
||||
-- @param #string cellName Name of the cell to teleport into. For exteriors can be empty.
|
||||
-- @param openmw.util#Vector3 position New position
|
||||
-- @param openmw.util#Vector3 rotation New rotation. Optional argument. If missed, then the current rotation is used.
|
||||
-- @param openmw.util#Vector3 rotation New rotation. Optional argument. If missing, then the current rotation is used.
|
||||
|
||||
---
|
||||
-- Moves object into a container or an inventory. Enables if was disabled.
|
||||
-- Can be called only from a global script.
|
||||
-- @function [parent=#GameObject] moveInto
|
||||
-- @param self
|
||||
-- @param #Inventory dest
|
||||
-- @usage item:moveInto(types.Actor.inventory(actor))
|
||||
|
||||
---
|
||||
-- Removes an object or reduces a stack of objects.
|
||||
-- Can be called only from a global script.
|
||||
-- @function [parent=#GameObject] remove
|
||||
-- @param self
|
||||
-- @param #number count (optional) the number of items to remove (if not specified then the whole stack)
|
||||
|
||||
---
|
||||
-- Splits a stack of items. Original stack is reduced by `count`. Returns a new stack with `count` items.
|
||||
-- Can be called only from a global script.
|
||||
-- @function [parent=#GameObject] split
|
||||
-- @param self
|
||||
-- @param #number count The number of items to return.
|
||||
-- @usage -- take 50 coins from `money` and put to the container `cont`
|
||||
-- money:split(50):moveInto(types.Container.content(cont))
|
||||
|
||||
|
||||
---
|
||||
|
@ -246,6 +272,22 @@
|
|||
-- local all = playerInventory:getAll()
|
||||
-- local weapons = playerInventory:getAll(types.Weapon)
|
||||
|
||||
---
|
||||
-- Get first item with given recordId from the inventory. Returns nil if not found.
|
||||
-- @function [parent=#Inventory] find
|
||||
-- @param self
|
||||
-- @param #string recordId
|
||||
-- @return #GameObject
|
||||
-- @usage inventory:find('gold_001')
|
||||
|
||||
---
|
||||
-- Get all items with given recordId from the inventory.
|
||||
-- @function [parent=#Inventory] findAll
|
||||
-- @param self
|
||||
-- @param #string recordId
|
||||
-- @return #ObjectList
|
||||
-- @usage for _, item in ipairs(inventory:findAll('common_shirt_01')) do ... end
|
||||
|
||||
|
||||
return nil
|
||||
|
||||
|
|
|
@ -59,5 +59,19 @@
|
|||
-- @function [parent=#world] isWorldPaused
|
||||
-- @return #boolean
|
||||
|
||||
---
|
||||
-- Create a new instance of the given record.
|
||||
-- After creation the object is in the disabled state. Use :teleport to place to the world or :moveInto to put it into a container or an inventory.
|
||||
-- @function [parent=#world] createObject
|
||||
-- @param #string recordId Record ID in lowercase
|
||||
-- @param #number count (optional, 1 by default) The number of objects in stack
|
||||
-- @return openmw.core#GameObject
|
||||
-- @usage -- put 100 gold on the ground at the position of `actor`
|
||||
-- money = world.createObject('gold_001', 100)
|
||||
-- money:teleport(actor.cell.name, actor.position)
|
||||
-- @usage -- put 50 gold into the actor's inventory
|
||||
-- money = world.createObject('gold_001', 50)
|
||||
-- money:moveInto(types.Actor.inventory(actor))
|
||||
|
||||
return nil
|
||||
|
||||
|
|
Loading…
Reference in a new issue