mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-04-17 11:43:25 +00:00
Merge branch 'autodoor' into 'master'
Handle ESM4 doors with "AutomaticDoor" flag + some additional Lua bindings See merge request OpenMW/openmw!3095
This commit is contained in:
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
|
||||
|
46
files/data/scripts/omw/mechanics/playercontroller.lua
Normal file
46
files/data/scripts/omw/mechanics/playercontroller.lua
Normal file
@ -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…
x
Reference in New Issue
Block a user