Merge branch 'new_cont_crea' into 'master'

Add record creation for containers, creatures

See merge request OpenMW/openmw!4869
This commit is contained in:
Daisyhasacat 2025-09-20 09:10:08 +00:00
commit 937703cb22
5 changed files with 227 additions and 1 deletions

View File

@ -18,6 +18,53 @@ 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<ESM::Container>(rec["template"]);
else
cont.blank();
cont.mId = {};
// Basic fields
if (rec["name"] != sol::nil)
cont.mName = rec["name"];
if (rec["model"] != sol::nil)
cont.mModel = Misc::ResourceHelpers::meshPathForESM3(rec["model"].get<std::string_view>());
if (rec["mwscript"] != sol::nil)
cont.mScript = ESM::RefId::deserializeText(rec["mwscript"].get<std::string_view>());
if (rec["weight"] != sol::nil)
cont.mWeight = rec["weight"].get<float>();
// 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 +82,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);

View File

@ -10,6 +10,138 @@
#include <components/misc/resourcehelpers.hpp>
#include <components/resource/resourcesystem.hpp>
namespace
{
ESM::Creature tableToCreature(const sol::table& rec)
{
ESM::Creature crea;
// Start from template if provided
if (rec["template"] != sol::nil)
crea = LuaUtil::cast<ESM::Creature>(rec["template"]);
else
crea.blank();
crea.mId = {};
// Basic fields
if (rec["name"] != sol::nil)
crea.mName = rec["name"];
if (rec["model"] != sol::nil)
crea.mModel = Misc::ResourceHelpers::meshPathForESM3(rec["model"].get<std::string_view>());
if (rec["mwscript"] != sol::nil)
crea.mScript = ESM::RefId::deserializeText(rec["mwscript"].get<std::string_view>());
if (rec["baseCreature"] != sol::nil)
crea.mOriginal = ESM::RefId::deserializeText(rec["baseCreature"].get<std::string_view>());
if (rec["soulValue"] != sol::nil)
crea.mData.mSoul = rec["soulValue"].get<int>();
if (rec["type"] != sol::nil)
crea.mData.mType
= rec["type"].get<int>();
if (rec["baseGold"] != sol::nil)
crea.mData.mGold = rec["baseGold"].get<int>();
if (rec["combatSkill"] != sol::nil)
crea.mData.mCombat = rec["combatSkill"].get<int>();
if (rec["magicSkill"] != sol::nil)
crea.mData.mMagic = rec["magicSkill"].get<int>();
if (rec["stealthSkill"] != sol::nil)
crea.mData.mStealth = rec["stealthSkill"].get<int>();
if (rec["attack"] != sol::nil)
{
const sol::table atk = rec["attack"];
for (int i = 0; i < 3; ++i)
{
sol::object v = atk[i + 1];
if (v != sol::nil)
crea.mData.mAttack[i] = v.as<int>();
}
}
if (rec["canFly"] != sol::nil)
{
bool canFly = rec["canFly"];
if (canFly)
crea.mFlags |= ESM::Creature::Flies;
else
crea.mFlags &= ~ESM::Creature::Flies;
}
if (rec["canSwim"] != sol::nil)
{
bool canSwim = rec["canSwim"];
if (canSwim)
crea.mFlags |= ESM::Creature::Swims;
else
crea.mFlags &= ~ESM::Creature::Swims;
}
if (rec["canUseWeapons"] != sol::nil)
{
bool canUseWeapons = rec["canUseWeapons"];
if (canUseWeapons)
crea.mFlags |= ESM::Creature::Weapon;
else
crea.mFlags &= ~ESM::Creature::Weapon;
}
if (rec["canWalk"] != sol::nil)
{
bool canWalk = rec["canWalk"];
if (canWalk)
crea.mFlags |= ESM::Creature::Walks;
else
crea.mFlags &= ~ESM::Creature::Walks;
}
if (rec["isBiped"] != sol::nil)
{
bool isBiped = rec["isBiped"];
if (isBiped)
crea.mFlags |= ESM::Creature::Bipedal;
else
crea.mFlags &= ~ESM::Creature::Bipedal;
}
if (rec["isEssential"] != sol::nil)
{
bool isEssential = rec["isEssential"];
if (isEssential)
crea.mFlags |= ESM::Creature::Essential;
else
crea.mFlags &= ~ESM::Creature::Essential;
}
if (rec["isRespawning"] != sol::nil)
{
bool isRespawning = rec["isRespawning"];
if (isRespawning)
crea.mFlags |= ESM::Creature::Respawn;
else
crea.mFlags &= ~ESM::Creature::Respawn;
}
if (rec["bloodType"] != sol::nil)
crea.mBloodType = rec["bloodType"].get<int>();
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<bool>())
flags |= mask;
}
crea.mAiData.mServices = flags;
}
return crea;
}
}
namespace sol
{
template <>
@ -30,6 +162,7 @@ namespace MWLua
{ "Undead", ESM::Creature::Undead },
{ "Humanoid", ESM::Creature::Humanoid },
}));
creature["createRecordDraft"] = tableToCreature;
addRecordFunctionBinding<ESM::Creature>(creature, context);

View File

@ -5,6 +5,8 @@
#include <components/esm3/loadarmo.hpp>
#include <components/esm3/loadbook.hpp>
#include <components/esm3/loadclot.hpp>
#include <components/esm3/loadcrea.hpp>
#include <components/esm3/loadcont.hpp>
#include <components/esm3/loadligh.hpp>
#include <components/esm3/loadmisc.hpp>
#include <components/esm3/loadnpc.hpp>
@ -197,6 +199,22 @@ namespace MWLua
copy.mId = {};
return MWBase::Environment::get().getESMStore()->insert(copy);
},
[lua = context.mLua](const ESM::Creature& crea) -> const ESM::Creature* {
checkGameInitialized(lua);
if (crea.mId.empty())
return MWBase::Environment::get().getESMStore()->insert(crea);
ESM::Creature copy = crea;
copy.mId = {};
return MWBase::Environment::get().getESMStore()->insert(copy);
},
[lua = context.mLua](const ESM::Container& cont) -> const ESM::Container* {
checkGameInitialized(lua);
if (cont.mId.empty())
return MWBase::Environment::get().getESMStore()->insert(cont);
ESM::Container copy = cont;
copy.mId = {};
return MWBase::Environment::get().getESMStore()->insert(copy);
},
[lua = context.mLua](const ESM::Weapon& weapon) -> const ESM::Weapon* {
checkGameInitialized(lua);
return MWBase::Environment::get().getESMStore()->insert(weapon);

View File

@ -806,6 +806,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.
@ -2194,6 +2206,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):moveInto(playerCell)
---
-- Container content (same as `Container.content`, added for consistency with `Actor.inventory`).
-- @function [parent=#Container] inventory

View File

@ -177,7 +177,10 @@
-- * @{openmw.types#ClothingRecord},
-- * @{openmw.types#WeaponRecord},
-- * @{openmw.types#ActivatorRecord},
-- * @{openmw.types#LightRecord}
-- * @{openmw.types#LightRecord},
-- * @{openmw.types#NpcRecord},
-- * @{openmw.types#ContainerRecord},
-- * @{openmw.types#CreatureRecord}
-- @function [parent=#world] createRecord
-- @param #any record A record to be registered in the database. Must be one of the supported types. The id field is not used, one will be generated for you.
-- @return #any A new record added to the database. The type is the same as the input's.