From c63c1e69cface352088a973ed13b9130b604c654 Mon Sep 17 00:00:00 2001 From: Zackhasacat Date: Mon, 13 May 2024 14:14:44 +0000 Subject: [PATCH] Lua: Add missing light flags, allow creating light record via world.createRecord --- apps/openmw/mwlua/types/light.cpp | 76 +++++++++++++++++++ apps/openmw/mwlua/worldbindings.cpp | 5 ++ apps/openmw/mwstate/statemanagerimp.cpp | 1 + apps/openmw/mwworld/esmstore.cpp | 4 +- files/lua_api/openmw/types.lua | 17 ++++- files/lua_api/openmw/world.lua | 3 +- .../integration_tests/test_lua_api/test.lua | 26 ++++++- 7 files changed, 128 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwlua/types/light.cpp b/apps/openmw/mwlua/types/light.cpp index 5a357994a3..2784e5a770 100644 --- a/apps/openmw/mwlua/types/light.cpp +++ b/apps/openmw/mwlua/types/light.cpp @@ -15,6 +15,65 @@ namespace sol }; } +namespace +{ + void setRecordFlag(const sol::table& rec, const std::string& key, int flag, ESM::Light& record) + { + if (auto luaFlag = rec[key]; luaFlag != sol::nil) + { + if (luaFlag) + { + record.mData.mFlags |= flag; + } + else + { + record.mData.mFlags &= ~flag; + } + } + } + // Populates a light struct from a Lua table. + ESM::Light tableToLight(const sol::table& rec) + { + ESM::Light light; + if (rec["template"] != sol::nil) + light = LuaUtil::cast(rec["template"]); + else + light.blank(); + if (rec["name"] != sol::nil) + light.mName = rec["name"]; + if (rec["model"] != sol::nil) + light.mModel = Misc::ResourceHelpers::meshPathForESM3(rec["model"].get()); + if (rec["icon"] != sol::nil) + light.mIcon = rec["icon"]; + if (rec["mwscript"] != sol::nil) + { + std::string_view scriptId = rec["mwscript"].get(); + light.mScript = ESM::RefId::deserializeText(scriptId); + } + if (rec["weight"] != sol::nil) + light.mData.mWeight = rec["weight"]; + if (rec["value"] != sol::nil) + light.mData.mValue = rec["value"]; + if (rec["duration"] != sol::nil) + light.mData.mTime = rec["duration"]; + if (rec["radius"] != sol::nil) + light.mData.mRadius = rec["radius"]; + if (rec["color"] != sol::nil) + light.mData.mColor = rec["color"]; + setRecordFlag(rec, "isCarriable", ESM::Light::Carry, light); + setRecordFlag(rec, "isDynamic", ESM::Light::Dynamic, light); + setRecordFlag(rec, "isFire", ESM::Light::Fire, light); + setRecordFlag(rec, "isFlicker", ESM::Light::Flicker, light); + setRecordFlag(rec, "isFlickerSlow", ESM::Light::FlickerSlow, light); + setRecordFlag(rec, "isNegative", ESM::Light::Negative, light); + setRecordFlag(rec, "isOffByDefault", ESM::Light::OffDefault, light); + setRecordFlag(rec, "isPulse", ESM::Light::Pulse, light); + setRecordFlag(rec, "isPulseSlow", ESM::Light::PulseSlow, light); + + return light; + } +} + namespace MWLua { void addLightBindings(sol::table light, const Context& context) @@ -22,6 +81,7 @@ namespace MWLua auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); addRecordFunctionBinding(light, context); + light["createRecordDraft"] = tableToLight; sol::usertype record = context.mLua->sol().new_usertype("ESM3_Light"); record[sol::meta_function::to_string] @@ -45,5 +105,21 @@ namespace MWLua record["color"] = sol::readonly_property([](const ESM::Light& rec) -> int { return rec.mData.mColor; }); record["isCarriable"] = sol::readonly_property( [](const ESM::Light& rec) -> bool { return rec.mData.mFlags & ESM::Light::Carry; }); + record["isDynamic"] = sol::readonly_property( + [](const ESM::Light& rec) -> bool { return rec.mData.mFlags & ESM::Light::Dynamic; }); + record["isFire"] + = sol::readonly_property([](const ESM::Light& rec) -> bool { return rec.mData.mFlags & ESM::Light::Fire; }); + record["isFlicker"] = sol::readonly_property( + [](const ESM::Light& rec) -> bool { return rec.mData.mFlags & ESM::Light::Flicker; }); + record["isFlickerSlow"] = sol::readonly_property( + [](const ESM::Light& rec) -> bool { return rec.mData.mFlags & ESM::Light::FlickerSlow; }); + record["isNegative"] = sol::readonly_property( + [](const ESM::Light& rec) -> bool { return rec.mData.mFlags & ESM::Light::Negative; }); + record["isOffByDefault"] = sol::readonly_property( + [](const ESM::Light& rec) -> bool { return rec.mData.mFlags & ESM::Light::OffDefault; }); + record["isPulse"] = sol::readonly_property( + [](const ESM::Light& rec) -> bool { return rec.mData.mFlags & ESM::Light::Pulse; }); + record["isPulseSlow"] = sol::readonly_property( + [](const ESM::Light& rec) -> bool { return rec.mData.mFlags & ESM::Light::PulseSlow; }); } } diff --git a/apps/openmw/mwlua/worldbindings.cpp b/apps/openmw/mwlua/worldbindings.cpp index c5ff7c89ca..4c09500788 100644 --- a/apps/openmw/mwlua/worldbindings.cpp +++ b/apps/openmw/mwlua/worldbindings.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -181,6 +182,10 @@ namespace MWLua [lua = context.mLua](const ESM::Weapon& weapon) -> const ESM::Weapon* { checkGameInitialized(lua); return MWBase::Environment::get().getESMStore()->insert(weapon); + }, + [lua = context.mLua](const ESM::Light& light) -> const ESM::Light* { + checkGameInitialized(lua); + return MWBase::Environment::get().getESMStore()->insert(light); }); api["_runStandardActivationAction"] = [context](const GObject& object, const GObject& actor) { diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 3ced01d28f..45e6e9d10e 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -540,6 +540,7 @@ void MWState::StateManager::loadGame(const Character* character, const std::file case ESM::REC_ENAB: case ESM::REC_LEVC: case ESM::REC_LEVI: + case ESM::REC_LIGH: case ESM::REC_CREA: case ESM::REC_CONT: case ESM::REC_RAND: diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 581a119147..a8b2beb003 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -689,7 +689,7 @@ namespace MWWorld + get().getDynamicSize() + get().getDynamicSize() + get().getDynamicSize() + get().getDynamicSize() + get().getDynamicSize() + get().getDynamicSize() - + get().getDynamicSize(); + + get().getDynamicSize() + get().getDynamicSize(); } void ESMStore::write(ESM::ESMWriter& writer, Loading::Listener& progress) const @@ -715,6 +715,7 @@ namespace MWWorld get().write(writer, progress); get().write(writer, progress); get().write(writer, progress); + get().write(writer, progress); } bool ESMStore::readRecord(ESM::ESMReader& reader, uint32_t type_id) @@ -734,6 +735,7 @@ namespace MWWorld case ESM::REC_WEAP: case ESM::REC_LEVI: case ESM::REC_LEVC: + case ESM::REC_LIGH: mStoreImp->mRecNameToStore[type]->read(reader); return true; case ESM::REC_NPC_: diff --git a/files/lua_api/openmw/types.lua b/files/lua_api/openmw/types.lua index 6c0f4544f5..0bfafb17c8 100644 --- a/files/lua_api/openmw/types.lua +++ b/files/lua_api/openmw/types.lua @@ -1589,6 +1589,13 @@ -- @param openmw.core#GameObject object -- @return #boolean +--- +-- Creates a @{#LightRecord} without adding it to the world database. +-- Use @{openmw_world#(world).createRecord} to add the record to the world. +-- @function [parent=#Light] createRecordDraft +-- @param #LightRecord light A Lua table with the fields of a LightRecord, with an optional field `template` that accepts a @{#LightRecord} as a base. +-- @return #LightRecord A strongly typed Light record. + --- -- Returns the read-only @{#LightRecord} of a Light -- @function [parent=#Light] record @@ -1608,7 +1615,15 @@ -- @field #number duration -- @field #number radius -- @field #number color --- @field #boolean isCarriable +-- @field #boolean isCarriable True if the light can be carried by actors and appears up in their inventory. +-- @field #boolean isDynamic If true, the light will apply to actors and other moving objects +-- @field #boolean isFire True if the light acts like a fire. +-- @field #boolean isFlicker +-- @field #boolean isFlickerSlow +-- @field #boolean isNegative If true, the light will reduce light instead of increasing it. +-- @field #boolean isOffByDefault If true, the light will not emit any light or sound while placed in the world. It will still work in the inventory. +-- @field #boolean isPulse +-- @field #boolean isPulseSlow diff --git a/files/lua_api/openmw/world.lua b/files/lua_api/openmw/world.lua index 404b744eb8..4e67fd4c7c 100644 --- a/files/lua_api/openmw/world.lua +++ b/files/lua_api/openmw/world.lua @@ -172,7 +172,8 @@ -- * @{openmw.types#MiscellaneousRecord}, -- * @{openmw.types#ClothingRecord}, -- * @{openmw.types#WeaponRecord}, --- * @{openmw.types#ActivatorRecord} +-- * @{openmw.types#ActivatorRecord}, +-- * @{openmw.types#LightRecord} -- @function [parent=#world] createRecord -- @param #any record A record to be registered in the database. Must be one of the supported types. -- @return #any A new record added to the database. The type is the same as the input's. diff --git a/scripts/data/integration_tests/test_lua_api/test.lua b/scripts/data/integration_tests/test_lua_api/test.lua index 863cdd0f57..22b8c4c0c1 100644 --- a/scripts/data/integration_tests/test_lua_api/test.lua +++ b/scripts/data/integration_tests/test_lua_api/test.lua @@ -126,7 +126,30 @@ local function testRecordStores() testRecordStore(types.NPC.races,"races") testRecordStore(types.Player.birthSigns,"birthSigns") end - +local function testRecordCreation() + local newLight = { + isCarriable = true, + isDynamic = true, + isFire =false, + isFlicker = false, + isFlickerSlow = false, + isNegative = false, + isOffByDefault = false, + isPulse = false, + weight = 1, + value = 10, + duration = 12, + radius = 30, + color = 5, + name = "TestLight", + model = "meshes\\marker_door.dae" + } + local draft = types.Light.createRecordDraft(newLight) + local record = world.createRecord(draft) + for key, value in pairs(newLight) do + testing.expectEqual(record[key],value) + end +end local function initPlayer() player:teleport('', util.vector3(4096, 4096, 867.237), util.transform.identity) coroutine.yield() @@ -165,6 +188,7 @@ tests = { {'teleport', testTeleport}, {'getGMST', testGetGMST}, {'recordStores', testRecordStores}, + {'recordCreation', testRecordCreation}, {'mwscript', testMWScript}, }