diff --git a/apps/openmw/mwlua/types/container.cpp b/apps/openmw/mwlua/types/container.cpp index 8e37ac275b..923ae2cd34 100644 --- a/apps/openmw/mwlua/types/container.cpp +++ b/apps/openmw/mwlua/types/container.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -18,6 +19,51 @@ namespace sol }; } +namespace +{ + ESM::Container tableToContainer(const sol::table& rec) + { + ESM::Container cont; + + // Start from template if provided + if (rec["template"] != sol::nil) + cont = LuaUtil::cast(rec["template"]); + else + cont.blank(); + + // Basic fields + if (rec["name"] != sol::nil) + cont.mName = rec["name"]; + if (rec["model"] != sol::nil) + cont.mModel = Misc::ResourceHelpers::meshPathForESM3(rec["model"].get()); + if (rec["mwscript"] != sol::nil) + cont.mScript = ESM::RefId::deserializeText(rec["mwscript"].get()); + if (rec["weight"] != sol::nil) + cont.mWeight = rec["weight"].get(); + + // Flags + if (rec["isOrganic"] != sol::nil) + { + bool isOrganic = rec["isOrganic"]; + if (isOrganic) + cont.mFlags |= ESM::Container::Organic; + else + cont.mFlags &= ~ESM::Container::Organic; + } + + if (rec["isRespawning"] != sol::nil) + { + bool isRespawning = rec["isRespawning"]; + if (isRespawning) + cont.mFlags |= ESM::Container::Respawn; + else + cont.mFlags &= ~ESM::Container::Respawn; + } + + return cont; + } +} + namespace MWLua { @@ -35,6 +81,7 @@ namespace MWLua const MWWorld::Ptr& ptr = containerPtr(obj); return ptr.getClass().getEncumbrance(ptr); }; + container["createRecordDraft"] = tableToContainer; container["encumbrance"] = container["getEncumbrance"]; // for compatibility; should be removed later container["getCapacity"] = [](const Object& obj) -> float { const MWWorld::Ptr& ptr = containerPtr(obj); diff --git a/apps/openmw/mwlua/types/creature.cpp b/apps/openmw/mwlua/types/creature.cpp index 81dfd612e4..0d51cf8965 100644 --- a/apps/openmw/mwlua/types/creature.cpp +++ b/apps/openmw/mwlua/types/creature.cpp @@ -10,6 +10,89 @@ #include #include +namespace +{ + ESM::Creature tableToCreature(const sol::table& rec) + { + ESM::Creature crea; + auto setCreatureFlag = [&](std::string_view key, int flag) { + if (rec[key] == sol::nil) + return; + + bool value = rec[key]; + if (value) + crea.mFlags |= flag; + else + crea.mFlags &= ~flag; + }; + + // Start from template if provided + if (rec["template"] != sol::nil) + crea = LuaUtil::cast(rec["template"]); + else + crea.blank(); + + // Basic fields + if (rec["name"] != sol::nil) + crea.mName = rec["name"]; + if (rec["model"] != sol::nil) + crea.mModel = Misc::ResourceHelpers::meshPathForESM3(rec["model"].get()); + if (rec["mwscript"] != sol::nil) + crea.mScript = ESM::RefId::deserializeText(rec["mwscript"].get()); + if (rec["baseCreature"] != sol::nil) + crea.mOriginal = ESM::RefId::deserializeText(rec["baseCreature"].get()); + + if (rec["soulValue"] != sol::nil) + crea.mData.mSoul = rec["soulValue"].get(); + if (rec["type"] != sol::nil) + crea.mData.mType = rec["type"].get(); + if (rec["baseGold"] != sol::nil) + crea.mData.mGold = rec["baseGold"].get(); + if (rec["combatSkill"] != sol::nil) + crea.mData.mCombat = rec["combatSkill"].get(); + if (rec["magicSkill"] != sol::nil) + crea.mData.mMagic = rec["magicSkill"].get(); + if (rec["stealthSkill"] != sol::nil) + crea.mData.mStealth = rec["stealthSkill"].get(); + + if (rec["attack"] != sol::nil) + { + const sol::table atk = rec["attack"]; + for (int i = 0; i < 6; ++i) + { + sol::object v = atk[i + 1]; + if (v != sol::nil) + crea.mData.mAttack[i] = v.as(); + } + } + setCreatureFlag("canFly", ESM::Creature::Flies); + setCreatureFlag("canSwim", ESM::Creature::Swims); + setCreatureFlag("canUseWeapons", ESM::Creature::Weapon); + setCreatureFlag("canWalk", ESM::Creature::Walks); + setCreatureFlag("isBiped", ESM::Creature::Bipedal); + setCreatureFlag("isEssential", ESM::Creature::Essential); + setCreatureFlag("isRespawning", ESM::Creature::Respawn); + + if (rec["bloodType"] != sol::nil) + crea.mBloodType = rec["bloodType"].get(); + + if (rec["servicesOffered"] != sol::nil) + { + const sol::table services = rec["servicesOffered"]; + int flags = 0; + for (const auto& [mask, key] : MWLua::ServiceNames) + { + sol::object value = services[key]; + if (value != sol::nil && value.as()) + flags |= mask; + } + crea.mAiData.mServices = flags; + } + + return crea; + } +} + namespace sol { template <> @@ -30,6 +113,7 @@ namespace MWLua { "Undead", ESM::Creature::Undead }, { "Humanoid", ESM::Creature::Humanoid }, })); + creature["createRecordDraft"] = tableToCreature; addRecordFunctionBinding(creature, context); diff --git a/apps/openmw/mwlua/worldbindings.cpp b/apps/openmw/mwlua/worldbindings.cpp index ae7ede69d2..326b8ad381 100644 --- a/apps/openmw/mwlua/worldbindings.cpp +++ b/apps/openmw/mwlua/worldbindings.cpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include #include #include @@ -209,6 +211,14 @@ namespace MWLua copy.mId = {}; return MWBase::Environment::get().getESMStore()->insert(copy); }, + [lua = context.mLua](const ESM::Creature& crea) -> const ESM::Creature* { + checkGameInitialized(lua); + return MWBase::Environment::get().getESMStore()->insert(crea); + }, + [lua = context.mLua](const ESM::Container& cont) -> const ESM::Container* { + checkGameInitialized(lua); + return MWBase::Environment::get().getESMStore()->insert(cont); + }, [lua = context.mLua](const ESM::Weapon& weapon) -> const ESM::Weapon* { checkGameInitialized(lua); return MWBase::Environment::get().getESMStore()->insert(weapon); diff --git a/files/lua_api/openmw/types.lua b/files/lua_api/openmw/types.lua index 765d304802..0d1dbc707a 100644 --- a/files/lua_api/openmw/types.lua +++ b/files/lua_api/openmw/types.lua @@ -819,6 +819,18 @@ -- @extends #Actor -- @field #Actor baseType @{#Actor} +--- +-- Creates a @{#CreatureRecord} without adding it to the world database. +-- Use @{openmw_world#(world).createRecord} to add the record to the world. +-- @function [parent=#Creature] createRecordDraft +-- @param #CreatureRecord creature A Lua table with the fields of a CreatureRecord, with an additional field `template` that accepts a @{#CreatureRecord} as a base. +-- @return #CreatureRecord A strongly typed Creature record. +-- @usage local creatureTemplate = types.Creature.records['mudcrab'] +-- local creatureTable = {name = "Epic Mudcrab", template = creatureTemplate, soulValue = 500, isEssential = true} +-- local recordDraft = types.Creature.createRecordDraft(creatureTable) +-- local newRecord = world.createRecord(recordDraft) +-- world.createObject(newRecord.id):teleport(playerCell, playerPosition) + --- -- A read-only list of all @{#CreatureRecord}s in the world database, may be indexed by recordId. -- Implements [iterables#List](iterables.html#List) of #CreatureRecord. @@ -2220,6 +2232,18 @@ -- @param openmw.core#GameObject object -- @return openmw.core#Inventory +--- +-- Creates a @{#ContainerRecord} without adding it to the world database. +-- Use @{openmw_world#(world).createRecord} to add the record to the world. +-- @function [parent=#Container] createRecordDraft +-- @param #ContainerRecord container A Lua table with the fields of a ContainerRecord, with an additional field `template` that accepts a @{#ContainerRecord} as a base. +-- @return #ContainerRecord A strongly typed Container record. +-- @usage local chestTemplate = types.Container.records['chest_small_01'] +-- local containerTable = {name = "Respawning Treasure Chest", template = chestTemplate, isRespawning = true, weight = 150.0} +-- local recordDraft = types.Container.createRecordDraft(containerTable) +-- local newRecord = world.createRecord(recordDraft) +-- world.createObject(newRecord.id):teleport(playerCell, playerPosition) + --- -- Container content (same as `Container.content`, added for consistency with `Actor.inventory`). -- @function [parent=#Container] inventory diff --git a/files/lua_api/openmw/world.lua b/files/lua_api/openmw/world.lua index 0cc1d065ff..789372eeaf 100644 --- a/files/lua_api/openmw/world.lua +++ b/files/lua_api/openmw/world.lua @@ -174,6 +174,8 @@ -- * @{openmw.types#ArmorRecord}, -- * @{openmw.types#BookRecord}, -- * @{openmw.types#ClothingRecord}, +-- * @{openmw.types#ContainerRecord}, +-- * @{openmw.types#CreatureRecord}, -- * @{openmw.types#LightRecord}, -- * @{openmw.types#MiscellaneousRecord}, -- * @{openmw.types#NpcRecord},