Merge branch 'autodoor' into 'master'

Handle ESM4 doors with "AutomaticDoor" flag + some additional Lua bindings

See merge request OpenMW/openmw!3095
revert-6246b479
psi29a 2 years ago
commit 8b0ef546b8

@ -84,11 +84,34 @@ namespace MWLua
// api["resume"] = []() {};
}
static sol::table initContentFilesBindings(sol::state_view& lua)
{
const std::vector<std::string>& contentList = MWBase::Environment::get().getWorld()->getContentFiles();
sol::table list(lua, sol::create);
for (size_t i = 0; i < contentList.size(); ++i)
list[i + 1] = Misc::StringUtils::lowerCase(contentList[i]);
sol::table res(lua, sol::create);
res["list"] = LuaUtil::makeReadOnly(list);
res["indexOf"] = [&contentList](std::string_view contentFile) -> sol::optional<int> {
for (size_t i = 0; i < contentList.size(); ++i)
if (Misc::StringUtils::ciEqual(contentList[i], contentFile))
return i + 1;
return sol::nullopt;
};
res["has"] = [&contentList](std::string_view contentFile) -> bool {
for (size_t i = 0; i < contentList.size(); ++i)
if (Misc::StringUtils::ciEqual(contentList[i], contentFile))
return true;
return false;
};
return LuaUtil::makeReadOnly(res);
}
static sol::table initCorePackage(const Context& context)
{
auto* lua = context.mLua;
sol::table api(lua->sol(), sol::create);
api["API_REVISION"] = 38;
api["API_REVISION"] = 39;
api["quit"] = [lua]() {
Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback();
MWBase::Environment::get().getStateManager()->requestQuit();
@ -97,6 +120,14 @@ namespace MWLua
context.mLuaEvents->addGlobalEvent(
{ std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer) });
};
api["contentFiles"] = initContentFilesBindings(lua->sol());
api["getFormId"] = [](std::string_view contentFile, unsigned int index) -> std::string {
const std::vector<std::string>& contentList = MWBase::Environment::get().getWorld()->getContentFiles();
for (size_t i = 0; i < contentList.size(); ++i)
if (Misc::StringUtils::ciEqual(contentList[i], contentFile))
return ESM::RefId(ESM::FormIdRefId(ESM::FormId{ index, int(i) })).serializeText();
throw std::runtime_error("Content file not found: " + std::string(contentFile));
};
addTimeBindings(api, context, false);
api["magic"] = initCoreMagicBindings(context);
api["l10n"] = LuaUtil::initL10nLoader(lua->sol(), MWBase::Environment::get().getL10nManager());
@ -189,6 +220,12 @@ namespace MWLua
MWWorld::Ptr newPtr = ptr.getClass().copyToCell(ptr, *cell, count.value_or(1));
return GObject(newPtr);
};
api["getObjectByFormId"] = [](std::string_view formIdStr) -> GObject {
ESM::RefId refId = ESM::RefId::deserializeText(formIdStr);
if (!refId.is<ESM::FormIdRefId>())
throw std::runtime_error("FormId expected, got " + std::string(formIdStr) + "; use core.getFormId");
return GObject(refId.getIf<ESM::FormIdRefId>()->getValue());
};
// Creates a new record in the world database.
api["createRecord"] = sol::overload(

@ -124,6 +124,13 @@ namespace MWLua
});
};
api["getObjectByFormId"] = [](std::string_view formIdStr) -> LObject {
ESM::RefId refId = ESM::RefId::deserializeText(formIdStr);
if (!refId.is<ESM::FormIdRefId>())
throw std::runtime_error("FormId expected, got " + std::string(formIdStr) + "; use core.getFormId");
return LObject(refId.getIf<ESM::FormIdRefId>()->getValue());
};
api["activators"] = LObjectList{ worldView->getActivatorsInScene() };
api["actors"] = LObjectList{ worldView->getActorsInScene() };
api["containers"] = LObjectList{ worldView->getContainersInScene() };

@ -145,6 +145,13 @@ namespace MWLua
void addBasicBindings(sol::usertype<ObjectT>& objectT, const Context& context)
{
objectT["id"] = sol::readonly_property([](const ObjectT& o) -> std::string { return o.id().toString(); });
objectT["contentFile"] = sol::readonly_property([](const ObjectT& o) -> sol::optional<std::string> {
int contentFileIndex = o.id().mContentFile;
const std::vector<std::string>& contentList = MWBase::Environment::get().getWorld()->getContentFiles();
if (contentFileIndex < 0 || contentFileIndex >= static_cast<int>(contentList.size()))
return sol::nullopt;
return Misc::StringUtils::lowerCase(contentList[contentFileIndex]);
});
objectT["isValid"] = [](const ObjectT& o) { return !o.ptrOrNull().isEmpty(); };
objectT["recordId"] = sol::readonly_property(
[](const ObjectT& o) -> std::string { return o.ptr().getCellRef().getRefId().serializeText(); });

@ -106,5 +106,7 @@ namespace MWLua
record["model"] = sol::readonly_property([vfs](const ESM4::Door& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
});
record["isAutomatic"] = sol::readonly_property(
[](const ESM4::Door& rec) -> bool { return rec.mDoorFlags & ESM4::Door::Flag_AutomaticDoor; });
}
}

@ -47,7 +47,7 @@ namespace MWLua
return &mActivatorsInScene;
if (cls.isActor())
return &mActorsInScene;
if (cls.isDoor())
if (ptr.mRef->getType() == ESM::REC_DOOR || ptr.mRef->getType() == ESM::REC_DOOR4)
return &mDoorsInScene;
if (typeid(cls) == typeid(MWClass::Container))
return &mContainersInScene;

@ -74,6 +74,7 @@ set(BUILTIN_DATA_FILES
scripts/omw/console/player.lua
scripts/omw/console/global.lua
scripts/omw/console/local.lua
scripts/omw/mechanics/playercontroller.lua
scripts/omw/playercontrols.lua
scripts/omw/settings/player.lua
scripts/omw/settings/global.lua

@ -7,6 +7,7 @@ PLAYER: scripts/omw/settings/player.lua
# Mechanics
GLOBAL: scripts/omw/activationhandlers.lua
PLAYER: scripts/omw/mechanics/playercontroller.lua
PLAYER: scripts/omw/playercontrols.lua
PLAYER: scripts/omw/camera/camera.lua
NPC,CREATURE: scripts/omw/ai.lua

@ -0,0 +1,46 @@
local core = require('openmw.core')
local nearby = require('openmw.nearby')
local self = require('openmw.self')
local types = require('openmw.types')
local cell = nil
local autodoors = {}
local function onCellChange()
autodoors = {}
for _, door in ipairs(nearby.doors) do
if door.type == types.ESM4Door and types.ESM4Door.record(door).isAutomatic then
autodoors[#autodoors + 1] = door
end
end
end
local autodoorActivationDist = 300
local lastAutoActivation = 0
local function processAutomaticDoors()
if core.getRealTime() - lastAutoActivation < 2 then
return
end
for _, door in ipairs(autodoors) do
if door.enabled and (door.position - self.position):length() < autodoorActivationDist then
print('Automatic activation of', door)
door:activateBy(self)
lastAutoActivation = core.getRealTime()
end
end
end
local function onUpdate()
if self.cell ~= cell then
cell = self.cell
onCellChange()
end
processAutomaticDoors()
end
return {
engineHandlers = {
onUpdate = onUpdate,
},
}

@ -102,12 +102,53 @@
-- print( myMsg('Hello {name}!', {name='World'}) )
---
-- @{#ContentFiles}: functions working with the list of currently loaded content files.
-- @field [parent=#core] #ContentFiles contentFiles
---
-- Functions working with the list of currently loaded content files.
-- @type ContentFiles
-- @field #list<#string> list The current load order (list of content file names).
---
-- Return the index of a specific content file in the load order (or `nil` if there is no such content file).
-- @function [parent=#ContentFiles] indexOf
-- @param #string contentFile
-- @return #number
---
-- Check if the content file with given name present in the load order.
-- @function [parent=#ContentFiles] has
-- @param #string contentFile
-- @return #boolean
---
-- Construct FormId string from content file name and the index in the file.
-- In ESM3 games (e.g. Morrowind) FormIds are used to reference game objects.
-- In ESM4 games (e.g. Skyrim) FormIds are used both for game objects and as record ids.
-- @function [parent=#core] getFormId
-- @param #string contentFile
-- @param #number index
-- @return #string
-- @usage if obj.recordId == core.getFormId('Skyrim.esm', 0x4d7da) then ... end
-- @usage -- In ESM3 content files (e.g. Morrowind) ids are human-readable strings
-- obj.ownerFactionId = 'blades'
-- -- In ESM4 (e.g. Skyrim) ids should be constructed using `core.getFormId`:
-- obj.ownerFactionId = core.getFormId('Skyrim.esm', 0x72834)
-- @usage -- local scripts
-- local obj = nearby.getObjectByFormId(core.getFormId('Morrowind.esm', 128964))
-- @usage -- global scripts
-- local obj = world.getObjectByFormId(core.getFormId('Morrowind.esm', 128964))
---
-- Any object that exists in the game world and has a specific location.
-- Player, actors, items, and statics are game objects.
-- @type GameObject
-- @extends #userdata
-- @field #string id A unique id of this object (not record id), can be used as a key in a table.
-- @field #string contentFile Lower cased file name of the content file that defines this object; nil for dynamically created objects.
-- @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).

@ -26,6 +26,16 @@
-- Everything that can be picked up in the nearby.
-- @field [parent=#nearby] openmw.core#ObjectList items
---
-- Return an object by RefNum/FormId.
-- Note: the function always returns @{openmw.core#GameObject} and doesn't validate that
-- the object exists in the game world. If it doesn't exist or not yet loaded to memory),
-- then `obj:isValid()` will be `false`.
-- @function [parent=#nearby] getObjectByFormId
-- @param #string formId String returned by `core.getFormId`
-- @return openmw.core#GameObject
-- @usage local obj = nearby.getObjectByFormId(core.getFormId('Morrowind.esm', 128964))
---
-- @type COLLISION_TYPE
-- @field [parent=#COLLISION_TYPE] #number World

@ -65,6 +65,16 @@
-- @function [parent=#world] isWorldPaused
-- @return #boolean
---
-- Return an object by RefNum/FormId.
-- Note: the function always returns @{openmw.core#GameObject} and doesn't validate that
-- the object exists in the game world. If it doesn't exist or not yet loaded to memory),
-- then `obj:isValid()` will be `false`.
-- @function [parent=#world] getObjectByFormId
-- @param #string formId String returned by `core.getFormId`
-- @return openmw.core#GameObject
-- @usage local obj = world.getObjectByFormId(core.getFormId('Morrowind.esm', 128964))
---
-- 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.

Loading…
Cancel
Save