diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fc0f0e068..fb0cb5d54e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -95,6 +95,7 @@ Feature #7194: Ori to show texture paths Feature #7214: Searching in the in-game console Feature #7284: Searching in the console with regex and toggleable case-sensitivity + Feature #7468: Factions API for Lua Feature #7477: NegativeLight Magic Effect flag Feature #7499: OpenMW-CS: Generate record filters by drag & dropping cell content to the filters field Feature #7546: Start the game on Fredas diff --git a/CMakeLists.txt b/CMakeLists.txt index adfb7ca7f5..380903f25c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,7 +71,7 @@ message(STATUS "Configuring OpenMW...") set(OPENMW_VERSION_MAJOR 0) set(OPENMW_VERSION_MINOR 49) set(OPENMW_VERSION_RELEASE 0) -set(OPENMW_LUA_API_REVISION 46) +set(OPENMW_LUA_API_REVISION 47) set(OPENMW_VERSION_COMMITHASH "") set(OPENMW_VERSION_TAGHASH "") diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index a8c233ff56..96d54949b1 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -63,7 +63,7 @@ add_openmw_dir (mwlua context globalscripts localscripts playerscripts luabindings objectbindings cellbindings mwscriptbindings camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings postprocessingbindings stats debugbindings types/types types/door types/item types/actor types/container types/lockable types/weapon types/npc types/creature types/player types/activator types/book types/lockpick types/probe types/apparatus types/potion types/ingredient types/misc types/repair types/armor types/light types/static types/clothing types/levelledlist types/terminal - worker magicbindings + worker magicbindings factionbindings ) add_openmw_dir (mwsound diff --git a/apps/openmw/mwlua/factionbindings.cpp b/apps/openmw/mwlua/factionbindings.cpp new file mode 100644 index 0000000000..a509a45487 --- /dev/null +++ b/apps/openmw/mwlua/factionbindings.cpp @@ -0,0 +1,135 @@ +#include "factionbindings.hpp" + +#include +#include + +#include "../mwbase/environment.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/esmstore.hpp" + +#include "../mwmechanics/npcstats.hpp" + +#include "luamanagerimp.hpp" + +namespace +{ + struct FactionRank : ESM::RankData + { + std::string mRankName; + ESM::RefId mFactionId; + size_t mRankIndex; + + FactionRank(const ESM::RefId& factionId, const ESM::RankData& data, std::string_view rankName, size_t rankIndex) + : ESM::RankData(data) + , mRankName(rankName) + , mFactionId(factionId) + { + } + }; +} + +namespace sol +{ + template <> + struct is_automagical : std::false_type + { + }; + template <> + struct is_automagical> : std::false_type + { + }; + template <> + struct is_automagical> : std::false_type + { + }; +} + +namespace MWLua +{ + using FactionStore = MWWorld::Store; + + void initCoreFactionBindings(const Context& context) + { + sol::state_view& lua = context.mLua->sol(); + sol::usertype factionStoreT = lua.new_usertype("ESM3_FactionStore"); + factionStoreT[sol::meta_function::to_string] = [](const FactionStore& store) { + return "ESM3_FactionStore{" + std::to_string(store.getSize()) + " factions}"; + }; + factionStoreT[sol::meta_function::length] = [](const FactionStore& store) { return store.getSize(); }; + factionStoreT[sol::meta_function::index] = sol::overload( + [](const FactionStore& store, size_t index) -> const ESM::Faction* { + if (index == 0 || index > store.getSize()) + return nullptr; + return store.at(index - 1); + }, + [](const FactionStore& store, std::string_view factionId) -> const ESM::Faction* { + return store.search(ESM::RefId::deserializeText(factionId)); + }); + factionStoreT[sol::meta_function::pairs] = lua["ipairsForArray"].template get(); + factionStoreT[sol::meta_function::ipairs] = lua["ipairsForArray"].template get(); + // Faction record + auto factionT = lua.new_usertype("ESM3_Faction"); + factionT[sol::meta_function::to_string] + = [](const ESM::Faction& rec) -> std::string { return "ESM3_Faction[" + rec.mId.toDebugString() + "]"; }; + factionT["id"] = sol::readonly_property([](const ESM::Faction& rec) { return rec.mId.serializeText(); }); + factionT["name"] + = sol::readonly_property([](const ESM::Faction& rec) -> std::string_view { return rec.mName; }); + factionT["hidden"] + = sol::readonly_property([](const ESM::Faction& rec) -> bool { return rec.mData.mIsHidden; }); + factionT["ranks"] = sol::readonly_property([&lua](const ESM::Faction& rec) { + sol::table res(lua, sol::create); + for (size_t i = 0; i < rec.mRanks.size() && i < rec.mData.mRankData.size(); i++) + { + if (rec.mRanks[i].empty()) + break; + + res.add(FactionRank(rec.mId, rec.mData.mRankData[i], rec.mRanks[i], i)); + } + + return res; + }); + factionT["reactions"] = sol::readonly_property([&lua](const ESM::Faction& rec) { + sol::table res(lua, sol::create); + for (const auto& [factionId, reaction] : rec.mReactions) + res[factionId.serializeText()] = reaction; + return res; + }); + factionT["attributes"] = sol::readonly_property([&lua](const ESM::Faction& rec) { + sol::table res(lua, sol::create); + for (auto attributeIndex : rec.mData.mAttribute) + { + ESM::RefId id = ESM::Attribute::indexToRefId(attributeIndex); + if (!id.empty()) + res.add(id.serializeText()); + } + + return res; + }); + factionT["skills"] = sol::readonly_property([&lua](const ESM::Faction& rec) { + sol::table res(lua, sol::create); + for (auto skillIndex : rec.mData.mSkills) + { + ESM::RefId id = ESM::Skill::indexToRefId(skillIndex); + if (!id.empty()) + res.add(id.serializeText()); + } + + return res; + }); + auto rankT = lua.new_usertype("ESM3_FactionRank"); + rankT[sol::meta_function::to_string] = [](const FactionRank& rec) -> std::string { + return "ESM3_FactionRank[" + rec.mFactionId.toDebugString() + ", " + std::to_string(rec.mRankIndex + 1) + + "]"; + }; + rankT["name"] = sol::readonly_property([](const FactionRank& rec) { return rec.mRankName; }); + rankT["primarySkillValue"] = sol::readonly_property([](const FactionRank& rec) { return rec.mPrimarySkill; }); + rankT["favouredSkillValue"] = sol::readonly_property([](const FactionRank& rec) { return rec.mFavouredSkill; }); + rankT["factionReaction"] = sol::readonly_property([](const FactionRank& rec) { return rec.mFactReaction; }); + rankT["attributeValues"] = sol::readonly_property([&lua](const FactionRank& rec) { + sol::table res(lua, sol::create); + res.add(rec.mAttribute1); + res.add(rec.mAttribute2); + return res; + }); + } +} diff --git a/apps/openmw/mwlua/factionbindings.hpp b/apps/openmw/mwlua/factionbindings.hpp new file mode 100644 index 0000000000..fe37133dbe --- /dev/null +++ b/apps/openmw/mwlua/factionbindings.hpp @@ -0,0 +1,13 @@ +#ifndef MWLUA_FACTIONBINDINGS_H +#define MWLUA_FACTIONBINDINGS_H + +#include + +#include "context.hpp" + +namespace MWLua +{ + void initCoreFactionBindings(const Context& context); +} + +#endif // MWLUA_FACTIONBINDINGS_H diff --git a/apps/openmw/mwlua/luabindings.cpp b/apps/openmw/mwlua/luabindings.cpp index 7f40ae0ad4..ef13adb936 100644 --- a/apps/openmw/mwlua/luabindings.cpp +++ b/apps/openmw/mwlua/luabindings.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,7 @@ #include "camerabindings.hpp" #include "cellbindings.hpp" #include "debugbindings.hpp" +#include "factionbindings.hpp" #include "inputbindings.hpp" #include "magicbindings.hpp" #include "nearbybindings.hpp" @@ -153,6 +155,10 @@ namespace MWLua addTimeBindings(api, context, false); api["magic"] = initCoreMagicBindings(context); api["stats"] = initCoreStatsBindings(context); + + initCoreFactionBindings(context); + api["factions"] = &MWBase::Environment::get().getWorld()->getStore().get(); + api["l10n"] = LuaUtil::initL10nLoader(lua->sol(), MWBase::Environment::get().getL10nManager()); const MWWorld::Store* gmstStore = &MWBase::Environment::get().getESMStore()->get(); diff --git a/apps/openmw/mwlua/types/npc.cpp b/apps/openmw/mwlua/types/npc.cpp index e612f228e0..a3bb82098d 100644 --- a/apps/openmw/mwlua/types/npc.cpp +++ b/apps/openmw/mwlua/types/npc.cpp @@ -1,6 +1,7 @@ #include "actor.hpp" #include "types.hpp" +#include #include #include @@ -11,6 +12,7 @@ #include #include +#include "../localscripts.hpp" #include "../stats.hpp" namespace sol @@ -21,6 +23,32 @@ namespace sol }; } +namespace +{ + size_t getValidRanksCount(const ESM::Faction* faction) + { + if (!faction) + return 0; + + for (size_t i = 0; i < faction->mRanks.size(); i++) + { + if (faction->mRanks[i].empty()) + return i; + } + + return faction->mRanks.size(); + } + + ESM::RefId parseFactionId(std::string_view faction) + { + ESM::RefId id = ESM::RefId::deserializeText(faction); + const MWWorld::ESMStore* store = MWBase::Environment::get().getESMStore(); + if (!store->get().search(id)) + throw std::runtime_error("Faction '" + std::string(faction) + "' does not exist"); + return id; + } +} + namespace MWLua { void addNpcBindings(sol::table npc, const Context& context) @@ -29,7 +57,9 @@ namespace MWLua addRecordFunctionBinding(npc, context); - sol::usertype record = context.mLua->sol().new_usertype("ESM3_NPC"); + sol::state_view& lua = context.mLua->sol(); + + sol::usertype record = lua.new_usertype("ESM3_NPC"); record[sol::meta_function::to_string] = [](const ESM::NPC& rec) { return "ESM3_NPC[" + rec.mId.toDebugString() + "]"; }; record["id"] @@ -68,5 +98,212 @@ namespace MWLua throw std::runtime_error("NPC expected"); return MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(o.ptr()); }; + + npc["getFactionRank"] = [](const Object& actor, std::string_view faction) { + const MWWorld::Ptr ptr = actor.ptr(); + ESM::RefId factionId = parseFactionId(faction); + + const MWMechanics::NpcStats& npcStats = ptr.getClass().getNpcStats(ptr); + + int factionRank = npcStats.getFactionRank(factionId); + if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr()) + { + if (npcStats.isInFaction(factionId)) + return factionRank + 1; + else + return 0; + } + else + { + ESM::RefId primaryFactionId = ptr.getClass().getPrimaryFaction(ptr); + if (factionId == primaryFactionId && factionRank == -1) + return ptr.getClass().getPrimaryFactionRank(ptr); + else if (primaryFactionId == factionId) + return factionRank + 1; + else + return 0; + } + }; + + npc["setFactionRank"] = [](Object& actor, std::string_view faction, int value) { + if (dynamic_cast(&actor) && !dynamic_cast(&actor)) + throw std::runtime_error("Local scripts can modify only self"); + + const MWWorld::Ptr ptr = actor.ptr(); + ESM::RefId factionId = parseFactionId(faction); + + const ESM::Faction* factionPtr + = MWBase::Environment::get().getESMStore()->get().find(factionId); + + auto ranksCount = static_cast(getValidRanksCount(factionPtr)); + if (value <= 0 || value > ranksCount) + throw std::runtime_error("Requested rank does not exist"); + + auto targetRank = std::clamp(value, 1, ranksCount) - 1; + + if (ptr != MWBase::Environment::get().getWorld()->getPlayerPtr()) + { + ESM::RefId primaryFactionId = ptr.getClass().getPrimaryFaction(ptr); + if (factionId != primaryFactionId) + throw std::runtime_error("Only players can modify ranks in non-primary factions"); + } + + MWMechanics::NpcStats& npcStats = ptr.getClass().getNpcStats(ptr); + if (!npcStats.isInFaction(factionId)) + throw std::runtime_error("Target actor is not a member of faction " + factionId.toDebugString()); + + npcStats.setFactionRank(factionId, targetRank); + }; + + npc["modifyFactionRank"] = [](Object& actor, std::string_view faction, int value) { + if (dynamic_cast(&actor) && !dynamic_cast(&actor)) + throw std::runtime_error("Local scripts can modify only self"); + + if (value == 0) + return; + + const MWWorld::Ptr ptr = actor.ptr(); + ESM::RefId factionId = parseFactionId(faction); + + const ESM::Faction* factionPtr + = MWBase::Environment::get().getESMStore()->get().search(factionId); + if (!factionPtr) + return; + + auto ranksCount = static_cast(getValidRanksCount(factionPtr)); + + MWMechanics::NpcStats& npcStats = ptr.getClass().getNpcStats(ptr); + + if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr()) + { + int currentRank = npcStats.getFactionRank(factionId); + if (currentRank >= 0) + npcStats.setFactionRank(factionId, std::clamp(currentRank + value, 0, ranksCount - 1)); + else + throw std::runtime_error("Target actor is not a member of faction " + factionId.toDebugString()); + + return; + } + + ESM::RefId primaryFactionId = ptr.getClass().getPrimaryFaction(ptr); + if (factionId != primaryFactionId) + throw std::runtime_error("Only players can modify ranks in non-primary factions"); + + // If we already changed rank for this NPC, modify current rank in the NPC stats. + // Otherwise take rank from base NPC record, adjust it and put it to NPC data. + int currentRank = npcStats.getFactionRank(factionId); + if (currentRank < 0) + { + currentRank = ptr.getClass().getPrimaryFactionRank(ptr); + npcStats.joinFaction(factionId); + } + + npcStats.setFactionRank(factionId, std::clamp(currentRank + value, 0, ranksCount - 1)); + }; + + npc["joinFaction"] = [](Object& actor, std::string_view faction) { + if (dynamic_cast(&actor) && !dynamic_cast(&actor)) + throw std::runtime_error("Local scripts can modify only self"); + + const MWWorld::Ptr ptr = actor.ptr(); + ESM::RefId factionId = parseFactionId(faction); + + if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr()) + { + MWMechanics::NpcStats& npcStats = ptr.getClass().getNpcStats(ptr); + int currentRank = npcStats.getFactionRank(factionId); + if (currentRank < 0) + npcStats.joinFaction(factionId); + return; + } + + throw std::runtime_error("Only player can join factions"); + }; + + npc["leaveFaction"] = [](Object& actor, std::string_view faction) { + if (dynamic_cast(&actor) && !dynamic_cast(&actor)) + throw std::runtime_error("Local scripts can modify only self"); + + const MWWorld::Ptr ptr = actor.ptr(); + ESM::RefId factionId = parseFactionId(faction); + + if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr()) + { + ptr.getClass().getNpcStats(ptr).setFactionRank(factionId, -1); + return; + } + + throw std::runtime_error("Only player can leave factions"); + }; + + npc["getFactionReputation"] = [](const Object& actor, std::string_view faction) { + const MWWorld::Ptr ptr = actor.ptr(); + ESM::RefId factionId = parseFactionId(faction); + + return ptr.getClass().getNpcStats(ptr).getFactionReputation(factionId); + }; + + npc["setFactionReputation"] = [](Object& actor, std::string_view faction, int value) { + if (dynamic_cast(&actor) && !dynamic_cast(&actor)) + throw std::runtime_error("Local scripts can modify only self"); + + const MWWorld::Ptr ptr = actor.ptr(); + ESM::RefId factionId = parseFactionId(faction); + + ptr.getClass().getNpcStats(ptr).setFactionReputation(factionId, value); + }; + + npc["modifyFactionReputation"] = [](Object& actor, std::string_view faction, int value) { + if (dynamic_cast(&actor) && !dynamic_cast(&actor)) + throw std::runtime_error("Local scripts can modify only self"); + + const MWWorld::Ptr ptr = actor.ptr(); + ESM::RefId factionId = parseFactionId(faction); + + MWMechanics::NpcStats& npcStats = ptr.getClass().getNpcStats(ptr); + int existingReputation = npcStats.getFactionReputation(factionId); + npcStats.setFactionReputation(factionId, existingReputation + value); + }; + + npc["expell"] = [](Object& actor, std::string_view faction) { + if (dynamic_cast(&actor) && !dynamic_cast(&actor)) + throw std::runtime_error("Local scripts can modify only self"); + + const MWWorld::Ptr ptr = actor.ptr(); + ESM::RefId factionId = parseFactionId(faction); + ptr.getClass().getNpcStats(ptr).expell(factionId, false); + }; + npc["clearExpelled"] = [](Object& actor, std::string_view faction) { + if (dynamic_cast(&actor) && !dynamic_cast(&actor)) + throw std::runtime_error("Local scripts can modify only self"); + + const MWWorld::Ptr ptr = actor.ptr(); + ESM::RefId factionId = parseFactionId(faction); + ptr.getClass().getNpcStats(ptr).clearExpelled(factionId); + }; + npc["isExpelled"] = [](const Object& actor, std::string_view faction) { + const MWWorld::Ptr ptr = actor.ptr(); + ESM::RefId factionId = parseFactionId(faction); + return ptr.getClass().getNpcStats(ptr).getExpelled(factionId); + }; + npc["getFactions"] = [&lua](const Object& actor) { + const MWWorld::Ptr ptr = actor.ptr(); + MWMechanics::NpcStats& npcStats = ptr.getClass().getNpcStats(ptr); + sol::table res(lua, sol::create); + if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr()) + { + for (const auto& [factionId, _] : npcStats.getFactionRanks()) + res.add(factionId.serializeText()); + return res; + } + + ESM::RefId primaryFactionId = ptr.getClass().getPrimaryFaction(ptr); + if (primaryFactionId.empty()) + return res; + + res.add(primaryFactionId.serializeText()); + + return res; + }; } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 36ff9db7ac..b960e0d38f 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1364,7 +1364,7 @@ namespace MWMechanics const std::map& playerRanks = player.getClass().getNpcStats(player).getFactionRanks(); if (playerRanks.find(factionID) != playerRanks.end()) { - player.getClass().getNpcStats(player).expell(factionID); + player.getClass().getNpcStats(player).expell(factionID, true); } } else if (!factionId.empty()) @@ -1372,7 +1372,7 @@ namespace MWMechanics const std::map& playerRanks = player.getClass().getNpcStats(player).getFactionRanks(); if (playerRanks.find(factionId) != playerRanks.end()) { - player.getClass().getNpcStats(player).expell(factionId); + player.getClass().getNpcStats(player).expell(factionId, true); } } diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 1ee463e622..b9df650fc3 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -112,14 +112,17 @@ bool MWMechanics::NpcStats::getExpelled(const ESM::RefId& factionID) const return mExpelled.find(factionID) != mExpelled.end(); } -void MWMechanics::NpcStats::expell(const ESM::RefId& factionID) +void MWMechanics::NpcStats::expell(const ESM::RefId& factionID, bool printMessage) { if (mExpelled.find(factionID) == mExpelled.end()) { + mExpelled.insert(factionID); + if (!printMessage) + return; + std::string message = "#{sExpelledMessage}"; message += MWBase::Environment::get().getESMStore()->get().find(factionID)->mName; MWBase::Environment::get().getWindowManager()->messageBox(message); - mExpelled.insert(factionID); } } diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index cae94414c9..b6a655e84f 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -74,7 +74,7 @@ namespace MWMechanics const std::set& getExpelled() const { return mExpelled; } bool getExpelled(const ESM::RefId& factionID) const; - void expell(const ESM::RefId& factionID); + void expell(const ESM::RefId& factionID, bool printMessage); void clearExpelled(const ESM::RefId& factionID); bool isInFaction(const ESM::RefId& faction) const; diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 0363f21fa6..745286e109 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -938,7 +938,7 @@ namespace MWScript MWWorld::Ptr player = MWMechanics::getPlayer(); if (!factionID.empty()) { - player.getClass().getNpcStats(player).expell(factionID); + player.getClass().getNpcStats(player).expell(factionID, true); } } }; diff --git a/components/esm3/loadfact.hpp b/components/esm3/loadfact.hpp index 0ba71dc06f..4cbf6c1d23 100644 --- a/components/esm3/loadfact.hpp +++ b/components/esm3/loadfact.hpp @@ -68,7 +68,7 @@ namespace ESM std::map mReactions; // Name of faction ranks (may be empty for NPC factions) - std::string mRanks[10]; + std::array mRanks; void load(ESMReader& esm, bool& isDeleted); void save(ESMWriter& esm, bool isDeleted = false) const; diff --git a/files/lua_api/openmw/core.lua b/files/lua_api/openmw/core.lua index 3bd86de5be..af09274981 100644 --- a/files/lua_api/openmw/core.lua +++ b/files/lua_api/openmw/core.lua @@ -10,6 +10,10 @@ -- The revision of OpenMW Lua API. It is an integer that is incremented every time the API is changed. See the actual value at the top of the page. -- @field [parent=#core] #number API_REVISION +--- +-- A read-only list of all @{#FactionRecord}s in the world database. +-- @field [parent=#core] #list<#FactionRecord> factions + --- -- Terminates the game and quits to the OS. Should be used only for testing purposes. -- @function [parent=#core] quit @@ -868,7 +872,6 @@ -- print(sound.fileName) -- end - --- @{#Stats}: stats -- @field [parent=#core] #Stats stats @@ -920,4 +923,23 @@ -- @field #string failureSound VFS path to the failure sound -- @field #string hitSound VFS path to the hit sound +--- +-- Faction data record +-- @type FactionRecord +-- @field #string id Faction id +-- @field #string name Faction name +-- @field #list<#FactionRank> ranks A read-only list containing data for all ranks in the faction, in order. +-- @field #map<#string, #number> reactions A read-only map containing reactions of other factions to this faction. +-- @field #list<#string> attributes A read-only list containing IDs of attributes to advance ranks in the faction. +-- @field #list<#string> skills A read-only list containing IDs of skills to advance ranks in the faction. + +--- +-- Faction rank data record +-- @type FactionRank +-- @field #string name Faction name Rank display name +-- @field #list<#number> attributeValues Attributes values required to get this rank. +-- @field #number primarySkillValue Primary skill value required to get this rank. +-- @field #number favouredSkillValue Secondary skill value required to get this rank. +-- @field #number factionReaction Reaction of faction members if player is in this faction. + return nil diff --git a/files/lua_api/openmw/types.lua b/files/lua_api/openmw/types.lua index b491203052..03efe885b5 100644 --- a/files/lua_api/openmw/types.lua +++ b/files/lua_api/openmw/types.lua @@ -720,6 +720,134 @@ -- @param openmw.core#GameObject object -- @return #boolean +--- +-- Get all factions in which NPC has a membership. +-- Note: this function does not take in account an expelling state. +-- @function [parent=#NPC] getFactions +-- @param openmw.core#GameObject actor NPC object +-- @return #list<#string> factionIds List of faction IDs. +-- @usage local NPC = require('openmw.types').NPC; +-- for _, factionId in pairs(types.NPC.getFactions(actor)) do +-- print(factionId); +-- end + +--- +-- Get rank of given NPC in given faction. +-- Throws an exception if there is no such faction. +-- Note: this function does not take in account an expelling state. +-- @function [parent=#NPC] getFactionRank +-- @param openmw.core#GameObject actor NPC object +-- @param #string faction Faction ID +-- @return #number rank Rank index (from 1), 0 if NPC is not in faction. +-- @usage local NPC = require('openmw.types').NPC; +-- print(NPC.getFactionRank(player, "mages guild"); + +--- +-- Set rank of given NPC in given faction. +-- Throws an exception if there is no such faction, target rank does not exist or actor is not a member of given faction. +-- For NPCs faction also should be an NPC's primary faction. +-- @function [parent=#NPC] setFactionRank +-- @param openmw.core#GameObject actor NPC object +-- @param #string faction Faction ID +-- @param #number value Rank index (from 1). +-- @usage local NPC = require('openmw.types').NPC; +-- NPC.setFactionRank(player, "mages guild", 6); + +--- +-- Adjust rank of given NPC in given faction. +-- Throws an exception if there is no such faction or actor is not a member of given faction. +-- For NPCs faction also should be an NPC's primary faction. +-- Notes: +-- +-- * If rank should become <= 0 after modification, function set rank to lowest available rank. +-- * If rank should become > 0 after modification, but target rank does not exist, function set rank to the highest valid rank. +-- @function [parent=#NPC] modifyFactionRank +-- @param openmw.core#GameObject actor NPC object +-- @param #string faction Faction ID +-- @param #number value Rank index (from 1) modifier. If rank reaches 0 for player character, he leaves the faction. +-- @usage local NPC = require('openmw.types').NPC; +-- NPC.modifyFactionRank(player, "mages guild", 1); + +--- +-- Add given actor to given faction. +-- Throws an exception if there is no such faction or target actor is not player. +-- Function does nothing if valid target actor is already a member of target faction. +-- @function [parent=#NPC] joinFaction +-- @param openmw.core#GameObject actor NPC object +-- @param #string faction Faction ID +-- @usage local NPC = require('openmw.types').NPC; +-- NPC.joinFaction(player, "mages guild"); + +--- +-- Remove given actor from given faction. +-- Function removes rank data and expelling state, but keeps a reputation in target faction. +-- Throws an exception if there is no such faction or target actor is not player. +-- Function does nothing if valid target actor is already not member of target faction. +-- @function [parent=#NPC] leaveFaction +-- @param openmw.core#GameObject actor NPC object +-- @param #string faction Faction ID +-- @usage local NPC = require('openmw.types').NPC; +-- NPC.leaveFaction(player, "mages guild"); + +--- +-- Get reputation of given actor in given faction. +-- Throws an exception if there is no such faction. +-- @function [parent=#NPC] getFactionReputation +-- @param openmw.core#GameObject actor NPC object +-- @param #string faction Faction ID +-- @return #number reputation Reputation level, 0 if NPC is not in faction. +-- @usage local NPC = require('openmw.types').NPC; +-- print(NPC.getFactionReputation(player, "mages guild")); + +--- +-- Set reputation of given actor in given faction. +-- Throws an exception if there is no such faction. +-- @function [parent=#NPC] setFactionReputation +-- @param openmw.core#GameObject actor NPC object +-- @param #string faction Faction ID +-- @param #number value Reputation value +-- @usage local NPC = require('openmw.types').NPC; +-- NPC.setFactionReputation(player, "mages guild", 100); + +--- +-- Adjust reputation of given actor in given faction. +-- Throws an exception if there is no such faction. +-- @function [parent=#NPC] modifyFactionReputation +-- @param openmw.core#GameObject actor NPC object +-- @param #string faction Faction ID +-- @param #number value Reputation modifier value +-- @usage local NPC = require('openmw.types').NPC; +-- NPC.modifyFactionReputation(player, "mages guild", 5); + +--- +-- Expell NPC from given faction. +-- Throws an exception if there is no such faction. +-- Note: expelled NPC still keeps his rank and reputation in faction, he just get an additonal flag for given faction. +-- @function [parent=#NPC] expell +-- @param openmw.core#GameObject actor NPC object +-- @param #string faction Faction ID +-- @usage local NPC = require('openmw.types').NPC; +-- NPC.expell(player, "mages guild"); + +--- +-- Clear expelling of NPC from given faction. +-- Throws an exception if there is no such faction. +-- @function [parent=#NPC] clearExpelled +-- @param openmw.core#GameObject actor NPC object +-- @param #string faction Faction ID +-- @usage local NPC = require('openmw.types').NPC; +-- NPC.clearExpell(player, "mages guild"); + +--- +-- Check if NPC is expelled from given faction. +-- Throws an exception if there is no such faction. +-- @function [parent=#NPC] isExpelled +-- @param openmw.core#GameObject actor NPC object +-- @param #string faction Faction ID +-- @return #bool isExpelled True if NPC is expelled from the faction. +-- @usage local NPC = require('openmw.types').NPC; +-- local result = NPC.isExpelled(player, "mages guild"); + --- -- Returns the current disposition of the provided NPC. This is their derived disposition, after modifiers such as personality and faction relations are taken into account. -- @function [parent=#NPC] getDisposition