diff --git a/AUTHORS.md b/AUTHORS.md index e394629839..1471466e64 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -225,7 +225,7 @@ Programmers tlmullis tri4ng1e Thoronador - Tobias Tribble (zackogenic) + Tobias Tribble (zackhasacat) Tom Lowe (Vulpen) Tom Mason (wheybags) Torben Leif Carrington (TorbenC) diff --git a/apps/openmw/mwlua/luabindings.cpp b/apps/openmw/mwlua/luabindings.cpp index 0f32cba562..04df6b6b16 100644 --- a/apps/openmw/mwlua/luabindings.cpp +++ b/apps/openmw/mwlua/luabindings.cpp @@ -3,9 +3,14 @@ #include #include +#include #include +#include +#include +#include +#include #include - +#include #include #include #include @@ -186,11 +191,28 @@ namespace MWLua }; // Creates a new record in the world database. - api["createRecord"] = sol::overload([](const ESM::Potion& potion) -> const ESM::Potion* { - return MWBase::Environment::get().getESMStore()->insert(potion); - } - // TODO: add here overloads for other records - ); + api["createRecord"] = sol::overload( + [](const ESM::Activator& activator) -> const ESM::Activator* { + return MWBase::Environment::get().getESMStore()->insert(activator); + }, + [](const ESM::Armor& armor) -> const ESM::Armor* { + return MWBase::Environment::get().getESMStore()->insert(armor); + }, + [](const ESM::Clothing& clothing) -> const ESM::Clothing* { + return MWBase::Environment::get().getESMStore()->insert(clothing); + }, + [](const ESM::Book& book) -> const ESM::Book* { + return MWBase::Environment::get().getESMStore()->insert(book); + }, + [](const ESM::Miscellaneous& misc) -> const ESM::Miscellaneous* { + return MWBase::Environment::get().getESMStore()->insert(misc); + }, + [](const ESM::Potion& potion) -> const ESM::Potion* { + return MWBase::Environment::get().getESMStore()->insert(potion); + }, + [](const ESM::Weapon& weapon) -> const ESM::Weapon* { + return MWBase::Environment::get().getESMStore()->insert(weapon); + }); api["_runStandardActivationAction"] = [context](const GObject& object, const GObject& actor) { context.mLuaManager->addAction( diff --git a/apps/openmw/mwlua/types/activator.cpp b/apps/openmw/mwlua/types/activator.cpp index 72baeffac5..1ea9ec07f7 100644 --- a/apps/openmw/mwlua/types/activator.cpp +++ b/apps/openmw/mwlua/types/activator.cpp @@ -16,6 +16,19 @@ namespace sol { }; } +namespace +{ + // Populates a activator struct from a Lua table. + ESM::Activator tableToActivator(const sol::table& rec) + { + ESM::Activator activator; + activator.mName = rec["name"]; + activator.mModel = rec["model"]; + std::string_view scriptId = rec["mwscript"].get(); + activator.mScript = ESM::RefId::deserializeText(scriptId); + return activator; + } +} namespace MWLua { @@ -23,6 +36,7 @@ namespace MWLua { auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); + activator["createRecordDraft"] = tableToActivator; addRecordFunctionBinding(activator, context); sol::usertype record = context.mLua->sol().new_usertype("ESM3_Activator"); diff --git a/apps/openmw/mwlua/types/armor.cpp b/apps/openmw/mwlua/types/armor.cpp index 0368a89b21..d530ddd205 100644 --- a/apps/openmw/mwlua/types/armor.cpp +++ b/apps/openmw/mwlua/types/armor.cpp @@ -16,6 +16,34 @@ namespace sol { }; } +namespace +{ + // Populates an armor struct from a Lua table. + ESM::Armor tableToArmor(const sol::table& rec) + { + ESM::Armor armor; + armor.mName = rec["name"]; + armor.mModel = rec["model"]; + armor.mIcon = rec["icon"]; + std::string_view enchantId = rec["enchant"].get(); + armor.mEnchant = ESM::RefId::deserializeText(enchantId); + std::string_view scriptId = rec["mwscript"].get(); + armor.mScript = ESM::RefId::deserializeText(scriptId); + + armor.mData.mWeight = rec["weight"]; + armor.mData.mValue = rec["value"]; + int armorType = rec["type"].get(); + if (armorType >= 0 && armorType <= ESM::Armor::RBracer) + armor.mData.mType = armorType; + else + throw std::runtime_error("Invalid Armor Type provided: " + std::to_string(armorType)); + armor.mData.mHealth = rec["health"]; + armor.mData.mArmor = rec["baseArmor"]; + armor.mData.mEnchant = std::round(rec["enchantCapacity"].get() * 10); + + return armor; + } +} namespace MWLua { @@ -39,6 +67,7 @@ namespace MWLua addRecordFunctionBinding(armor, context); + armor["createRecordDraft"] = tableToArmor; sol::usertype record = context.mLua->sol().new_usertype("ESM3_Armor"); record[sol::meta_function::to_string] = [](const ESM::Armor& rec) -> std::string { return "ESM3_Armor[" + rec.mId.toDebugString() + "]"; }; diff --git a/apps/openmw/mwlua/types/book.cpp b/apps/openmw/mwlua/types/book.cpp index 4306fa2ade..2a4068f2fa 100644 --- a/apps/openmw/mwlua/types/book.cpp +++ b/apps/openmw/mwlua/types/book.cpp @@ -1,5 +1,8 @@ #include "types.hpp" +#include +#include + #include #include #include @@ -18,6 +21,43 @@ namespace sol }; } +namespace +{ + // Populates a book struct from a Lua table. + ESM::Book tableToBook(const sol::table& rec) + { + ESM::Book book; + book.mName = rec["name"]; + book.mModel = rec["model"]; + book.mIcon = rec["icon"]; + book.mText = rec["text"]; + std::string_view enchantId = rec["enchant"].get(); + book.mEnchant = ESM::RefId::deserializeText(enchantId); + + book.mData.mEnchant = std::round(rec["enchantCapacity"].get() * 10); + std::string_view scriptId = rec["mwscript"].get(); + book.mScript = ESM::RefId::deserializeText(scriptId); + book.mData.mWeight = rec["weight"]; + book.mData.mValue = rec["value"]; + book.mData.mIsScroll = rec["isScroll"]; + + std::string_view skill = rec["skill"].get(); + + book.mData.mSkillId = -1; + if (skill.length() > 0) + { + for (std::size_t i = 0; i < std::size(ESM::Skill::sSkillNames); ++i) + { + if (Misc::StringUtils::ciEqual(ESM::Skill::sSkillNames[i], skill)) + book.mData.mSkillId = i; + } + if (book.mData.mSkillId == -1) + throw std::runtime_error("Incorrect skill: " + std::string(skill)); + } + return book; + } +} + namespace MWLua { void addBookBindings(sol::table book, const Context& context) @@ -26,6 +66,7 @@ namespace MWLua // TODO: Remove book.SKILL after branching 0.49 sol::table skill(context.mLua->sol(), sol::create); book["SKILL"] = LuaUtil::makeStrictReadOnly(skill); + book["createRecordDraft"] = tableToBook; for (int id = ESM::Skill::Block; id < ESM::Skill::Length; ++id) { std::string skillName = Misc::StringUtils::lowerCase(ESM::Skill::sSkillNames[id]); diff --git a/apps/openmw/mwlua/types/clothing.cpp b/apps/openmw/mwlua/types/clothing.cpp index 947b997f71..18c9580fb9 100644 --- a/apps/openmw/mwlua/types/clothing.cpp +++ b/apps/openmw/mwlua/types/clothing.cpp @@ -16,11 +16,36 @@ namespace sol { }; } - +namespace +{ + // Populates a clothing struct from a Lua table. + ESM::Clothing tableToClothing(const sol::table& rec) + { + ESM::Clothing clothing; + clothing.mName = rec["name"]; + clothing.mModel = rec["model"]; + clothing.mIcon = rec["icon"]; + std::string_view scriptId = rec["mwscript"].get(); + clothing.mScript = ESM::RefId::deserializeText(scriptId); + clothing.mData.mEnchant = std::round(rec["enchantCapacity"].get() * 10); + std::string_view enchantId = rec["enchant"].get(); + clothing.mEnchant = ESM::RefId::deserializeText(enchantId); + clothing.mData.mWeight = rec["weight"]; + clothing.mData.mValue = rec["value"]; + int clothingType = rec["type"].get(); + if (clothingType >= 0 && clothingType <= ESM::Clothing::Amulet) + clothing.mData.mType = clothingType; + else + throw std::runtime_error("Invalid Clothing Type provided: " + std::to_string(clothingType)); + return clothing; + } +} namespace MWLua { void addClothingBindings(sol::table clothing, const Context& context) { + clothing["createRecordDraft"] = tableToClothing; + clothing["TYPE"] = LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs({ { "Amulet", ESM::Clothing::Amulet }, { "Belt", ESM::Clothing::Belt }, diff --git a/apps/openmw/mwlua/types/misc.cpp b/apps/openmw/mwlua/types/misc.cpp index a6d73b5dac..b20107a4e3 100644 --- a/apps/openmw/mwlua/types/misc.cpp +++ b/apps/openmw/mwlua/types/misc.cpp @@ -18,6 +18,23 @@ namespace sol }; } +namespace +{ + // Populates a misc struct from a Lua table. + ESM::Miscellaneous tableToMisc(const sol::table& rec) + { + ESM::Miscellaneous misc; + misc.mName = rec["name"]; + misc.mModel = rec["model"]; + misc.mIcon = rec["icon"]; + std::string_view scriptId = rec["mwscript"].get(); + misc.mScript = ESM::RefId::deserializeText(scriptId); + misc.mData.mWeight = rec["weight"]; + misc.mData.mValue = rec["value"]; + return misc; + } +} + namespace MWLua { void addMiscellaneousBindings(sol::table miscellaneous, const Context& context) @@ -25,6 +42,7 @@ namespace MWLua auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); addRecordFunctionBinding(miscellaneous, context); + miscellaneous["createRecordDraft"] = tableToMisc; miscellaneous["setSoul"] = [](const GObject& object, std::string_view soulId) { ESM::RefId creature = ESM::RefId::deserializeText(soulId); diff --git a/apps/openmw/mwlua/types/npc.cpp b/apps/openmw/mwlua/types/npc.cpp index 3f20a657c2..eb8dd32445 100644 --- a/apps/openmw/mwlua/types/npc.cpp +++ b/apps/openmw/mwlua/types/npc.cpp @@ -43,6 +43,7 @@ namespace MWLua = sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mHair.serializeText(); }); record["head"] = sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mHead.serializeText(); }); + record["isMale"] = sol::readonly_property([](const ESM::NPC& rec) -> bool { return rec.isMale(); }); // This function is game-specific, in future we should replace it with something more universal. npc["isWerewolf"] = [](const Object& o) { diff --git a/apps/openmw/mwlua/types/weapon.cpp b/apps/openmw/mwlua/types/weapon.cpp index aa781c58ea..2457a0752f 100644 --- a/apps/openmw/mwlua/types/weapon.cpp +++ b/apps/openmw/mwlua/types/weapon.cpp @@ -17,6 +17,48 @@ namespace sol }; } #include + +namespace +{ + // Populates a weapon struct from a Lua table. + ESM::Weapon tableToWeapon(const sol::table& rec) + { + ESM::Weapon weapon; + weapon.mName = rec["name"]; + weapon.mModel = rec["model"]; + weapon.mIcon = rec["icon"]; + std::string_view enchantId = rec["enchant"].get(); + weapon.mEnchant = ESM::RefId::deserializeText(enchantId); + std::string_view scriptId = rec["mwscript"].get(); + weapon.mScript = ESM::RefId::deserializeText(scriptId); + weapon.mData.mFlags = 0; + if (rec["isMagical"]) + weapon.mData.mFlags |= ESM::Weapon::Magical; + if (rec["isSilver"]) + weapon.mData.mFlags |= ESM::Weapon::Silver; + int weaponType = rec["type"].get(); + if (weaponType >= 0 && weaponType <= ESM::Weapon::MarksmanThrown) + weapon.mData.mType = weaponType; + else + throw std::runtime_error("Invalid Weapon Type provided: " + std::to_string(weaponType)); + + weapon.mData.mWeight = rec["weight"]; + weapon.mData.mValue = rec["value"]; + weapon.mData.mHealth = rec["health"]; + weapon.mData.mSpeed = rec["speed"]; + weapon.mData.mReach = rec["reach"]; + weapon.mData.mEnchant = std::round(rec["enchantCapacity"].get() * 10); + weapon.mData.mChop[0] = rec["chopMinDamage"]; + weapon.mData.mChop[1] = rec["chopMaxDamage"]; + weapon.mData.mSlash[0] = rec["slashMinDamage"]; + weapon.mData.mSlash[1] = rec["slashMaxDamage"]; + weapon.mData.mThrust[0] = rec["thrustMinDamage"]; + weapon.mData.mThrust[1] = rec["thrustMaxDamage"]; + + return weapon; + } +} + namespace MWLua { void addWeaponBindings(sol::table weapon, const Context& context) @@ -41,6 +83,7 @@ namespace MWLua auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); addRecordFunctionBinding(weapon, context); + weapon["createRecordDraft"] = tableToWeapon; sol::usertype record = context.mLua->sol().new_usertype("ESM3_Weapon"); record[sol::meta_function::to_string] diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index c14a221187..5ceebacf7b 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -461,6 +461,8 @@ void MWState::StateManager::loadGame(const Character* character, const std::file break; case ESM::REC_ALCH: + case ESM::REC_MISC: + case ESM::REC_ACTI: case ESM::REC_ARMO: case ESM::REC_BOOK: case ESM::REC_CLAS: diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index ae539fcab1..3b6001ce72 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -631,6 +631,7 @@ namespace MWWorld + get().getDynamicSize() + get().getDynamicSize() + get().getDynamicSize() + get().getDynamicSize() + get().getDynamicSize() + get().getDynamicSize() + + get().getDynamicSize() + get().getDynamicSize() + get().getDynamicSize() + get().getDynamicSize() + get().getDynamicSize() + get().getDynamicSize() + get().getDynamicSize(); @@ -651,6 +652,8 @@ namespace MWWorld get().write(writer, progress); get().write(writer, progress); get().write(writer, progress); + get().write(writer, progress); + get().write(writer, progress); get().write(writer, progress); get().write(writer, progress); get().write(writer, progress); @@ -665,6 +668,8 @@ namespace MWWorld switch (type) { case ESM::REC_ALCH: + case ESM::REC_MISC: + case ESM::REC_ACTI: case ESM::REC_ARMO: case ESM::REC_BOOK: case ESM::REC_CLAS: diff --git a/files/lua_api/openmw/types.lua b/files/lua_api/openmw/types.lua index 9a06cb53f3..635b76fc72 100644 --- a/files/lua_api/openmw/types.lua +++ b/files/lua_api/openmw/types.lua @@ -570,6 +570,7 @@ -- @field #string mwscript MWScript that is attached to this NPC -- @field #string hair Path to the hair body part model -- @field #string head Path to the head body part model +-- @field #bool isMale The gender setting of the NPC --- @{#Player} functions -- @field [parent=#types] #Player Player @@ -640,6 +641,12 @@ -- @field #number baseArmor The base armor rating of this armor -- @field #number enchantCapacity +--- +-- Creates a @{#ArmorRecord} without adding it to the world database. +-- Use @{openmw_world#(world).createRecord} to add the record to the world. +-- @function [parent=#Armor] createRecordDraft +-- @param #ArmorRecord armor A Lua table with the fields of a ArmorRecord. +-- @return #ArmorRecord A strongly typed Armor record. --- @{#Book} functions @@ -711,7 +718,12 @@ -- @field #boolean isScroll -- @field #number enchantCapacity - +--- +-- Creates a @{#BookRecord} without adding it to the world database. +-- Use @{openmw_world#(world).createRecord} to add the record to the world. +-- @function [parent=#Book] createRecordDraft +-- @param #BookRecord book A Lua table with the fields of a BookRecord. +-- @return #BookRecord A strongly typed Book record. --- @{#Clothing} functions -- @field [parent=#types] #Clothing Clothing @@ -750,6 +762,13 @@ -- @param #any objectOrRecordId -- @return #ClothingRecord +--- +-- Creates a @{#ClothingRecord} without adding it to the world database. +-- Use @{openmw_world#(world).createRecord} to add the record to the world. +-- @function [parent=#Clothing] createRecordDraft +-- @param #ClothingRecord clothing A Lua table with the fields of a ClothingRecord. +-- @return #ClothingRecord A strongly typed clothing record. + --- -- @type ClothingRecord -- @field #string id Record id @@ -864,6 +883,13 @@ -- @param openmw.core#GameObject object -- @return #string +--- +-- Creates a @{#MiscellaneousRecord} without adding it to the world database. +-- Use @{openmw_world#(world).createRecord} to add the record to the world. +-- @function [parent=#Miscellaneous] createRecordDraft +-- @param #MiscellaneousRecord miscellaneous A Lua table with the fields of a MiscellaneousRecord. +-- @return #MiscellaneousRecord A strongly typed Miscellaneous record. + --- -- Sets the soul of a miscellaneous item, intended for soul gem objects; Must be used in a global script. -- @function [parent=#Miscellaneous] setSoul @@ -986,7 +1012,12 @@ -- @field #number thrustMinDamage -- @field #number thrustMaxDamage - +--- +-- Creates a @{#WeaponRecord} without adding it to the world database. +-- Use @{openmw_world#(world).createRecord} to add the record to the world. +-- @function [parent=#Weapon] createRecordDraft +-- @param #WeaponRecord weapon A Lua table with the fields of a WeaponRecord. +-- @return #WeaponRecord A strongly typed Weapon record. --- @{#Apparatus} functions -- @field [parent=#types] #Apparatus Apparatus @@ -1156,6 +1187,13 @@ -- @field #string model VFS path to the model -- @field #string mwscript MWScript on this activator (can be empty) +--- +-- Creates a @{#ActivatorRecord} without adding it to the world database. +-- Use @{openmw_world#(world).createRecord} to add the record to the world. +-- @function [parent=#Activator] createRecordDraft +-- @param #ActivatorRecord activator A Lua table with the fields of a ActivatorRecord. +-- @return #ActivatorRecord A strongly typed Activator record. + --- @{#Container} functions -- @field [parent=#types] #Container Container diff --git a/files/lua_api/openmw/world.lua b/files/lua_api/openmw/world.lua index 732ce1e938..2cc80063fd 100644 --- a/files/lua_api/openmw/world.lua +++ b/files/lua_api/openmw/world.lua @@ -83,10 +83,13 @@ -- Creates a custom record in the world database. -- Eventually meant to support all records, but the current -- set of supported types is limited to: --- * @{openmw.types#PotionRecord} +-- * @{openmw.types#PotionRecord}, +-- * @{openmw.types#ArmorRecord}, +-- * @{openmw.types#BookRecord}, +-- * @{openmw.types#MiscellaneousRecord}, +-- * @{openmw.types#ActivatorRecord} -- @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. return nil -