1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-01-29 09:15:35 +00:00

Implement Lua API for factions (feature 7468)

This commit is contained in:
Andrei Kortunov 2023-09-06 20:28:35 +04:00
parent 9d3eb1a901
commit 6ee86dea82
14 changed files with 509 additions and 11 deletions

View file

@ -94,6 +94,7 @@
Feature #7194: Ori to show texture paths Feature #7194: Ori to show texture paths
Feature #7214: Searching in the in-game console Feature #7214: Searching in the in-game console
Feature #7284: Searching in the console with regex and toggleable case-sensitivity 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 #7477: NegativeLight Magic Effect flag
Feature #7499: OpenMW-CS: Generate record filters by drag & dropping cell content to the filters field Feature #7499: OpenMW-CS: Generate record filters by drag & dropping cell content to the filters field
Feature #7546: Start the game on Fredas Feature #7546: Start the game on Fredas

View file

@ -71,7 +71,7 @@ message(STATUS "Configuring OpenMW...")
set(OPENMW_VERSION_MAJOR 0) set(OPENMW_VERSION_MAJOR 0)
set(OPENMW_VERSION_MINOR 49) set(OPENMW_VERSION_MINOR 49)
set(OPENMW_VERSION_RELEASE 0) set(OPENMW_VERSION_RELEASE 0)
set(OPENMW_LUA_API_REVISION 46) set(OPENMW_LUA_API_REVISION 47)
set(OPENMW_VERSION_COMMITHASH "") set(OPENMW_VERSION_COMMITHASH "")
set(OPENMW_VERSION_TAGHASH "") set(OPENMW_VERSION_TAGHASH "")

View file

@ -63,7 +63,7 @@ add_openmw_dir (mwlua
context globalscripts localscripts playerscripts luabindings objectbindings cellbindings mwscriptbindings context globalscripts localscripts playerscripts luabindings objectbindings cellbindings mwscriptbindings
camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings postprocessingbindings stats debugbindings 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 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 add_openmw_dir (mwsound

View file

@ -0,0 +1,135 @@
#include "factionbindings.hpp"
#include <components/esm3/loadfact.hpp>
#include <components/lua/luastate.hpp>
#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<ESM::Faction> : std::false_type
{
};
template <>
struct is_automagical<MWWorld::Store<ESM::Faction>> : std::false_type
{
};
template <>
struct is_automagical<MWWorld::Store<FactionRank>> : std::false_type
{
};
}
namespace MWLua
{
using FactionStore = MWWorld::Store<ESM::Faction>;
void initCoreFactionBindings(const Context& context)
{
sol::state_view& lua = context.mLua->sol();
sol::usertype<FactionStore> factionStoreT = lua.new_usertype<FactionStore>("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<sol::function>();
factionStoreT[sol::meta_function::ipairs] = lua["ipairsForArray"].template get<sol::function>();
// Faction record
auto factionT = lua.new_usertype<ESM::Faction>("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<FactionRank>("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;
});
}
}

View file

@ -0,0 +1,13 @@
#ifndef MWLUA_FACTIONBINDINGS_H
#define MWLUA_FACTIONBINDINGS_H
#include <sol/forward.hpp>
#include "context.hpp"
namespace MWLua
{
void initCoreFactionBindings(const Context& context);
}
#endif // MWLUA_FACTIONBINDINGS_H

View file

@ -8,6 +8,7 @@
#include <components/esm3/loadarmo.hpp> #include <components/esm3/loadarmo.hpp>
#include <components/esm3/loadbook.hpp> #include <components/esm3/loadbook.hpp>
#include <components/esm3/loadclot.hpp> #include <components/esm3/loadclot.hpp>
#include <components/esm3/loadfact.hpp>
#include <components/esm3/loadmisc.hpp> #include <components/esm3/loadmisc.hpp>
#include <components/esm3/loadskil.hpp> #include <components/esm3/loadskil.hpp>
#include <components/esm3/loadweap.hpp> #include <components/esm3/loadweap.hpp>
@ -36,6 +37,7 @@
#include "camerabindings.hpp" #include "camerabindings.hpp"
#include "cellbindings.hpp" #include "cellbindings.hpp"
#include "debugbindings.hpp" #include "debugbindings.hpp"
#include "factionbindings.hpp"
#include "inputbindings.hpp" #include "inputbindings.hpp"
#include "magicbindings.hpp" #include "magicbindings.hpp"
#include "nearbybindings.hpp" #include "nearbybindings.hpp"
@ -153,6 +155,10 @@ namespace MWLua
addTimeBindings(api, context, false); addTimeBindings(api, context, false);
api["magic"] = initCoreMagicBindings(context); api["magic"] = initCoreMagicBindings(context);
api["stats"] = initCoreStatsBindings(context); api["stats"] = initCoreStatsBindings(context);
initCoreFactionBindings(context);
api["factions"] = &MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>();
api["l10n"] = LuaUtil::initL10nLoader(lua->sol(), MWBase::Environment::get().getL10nManager()); api["l10n"] = LuaUtil::initL10nLoader(lua->sol(), MWBase::Environment::get().getL10nManager());
const MWWorld::Store<ESM::GameSetting>* gmstStore const MWWorld::Store<ESM::GameSetting>* gmstStore
= &MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>(); = &MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>();

View file

@ -1,6 +1,7 @@
#include "actor.hpp" #include "actor.hpp"
#include "types.hpp" #include "types.hpp"
#include <components/esm3/loadfact.hpp>
#include <components/esm3/loadnpc.hpp> #include <components/esm3/loadnpc.hpp>
#include <components/lua/luastate.hpp> #include <components/lua/luastate.hpp>
@ -11,6 +12,7 @@
#include <apps/openmw/mwworld/class.hpp> #include <apps/openmw/mwworld/class.hpp>
#include <apps/openmw/mwworld/esmstore.hpp> #include <apps/openmw/mwworld/esmstore.hpp>
#include "../localscripts.hpp"
#include "../stats.hpp" #include "../stats.hpp"
namespace sol 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<ESM::Faction>().search(id))
throw std::runtime_error("Faction '" + std::string(faction) + "' does not exist");
return id;
}
}
namespace MWLua namespace MWLua
{ {
void addNpcBindings(sol::table npc, const Context& context) void addNpcBindings(sol::table npc, const Context& context)
@ -29,7 +57,9 @@ namespace MWLua
addRecordFunctionBinding<ESM::NPC>(npc, context); addRecordFunctionBinding<ESM::NPC>(npc, context);
sol::usertype<ESM::NPC> record = context.mLua->sol().new_usertype<ESM::NPC>("ESM3_NPC"); sol::state_view& lua = context.mLua->sol();
sol::usertype<ESM::NPC> record = lua.new_usertype<ESM::NPC>("ESM3_NPC");
record[sol::meta_function::to_string] record[sol::meta_function::to_string]
= [](const ESM::NPC& rec) { return "ESM3_NPC[" + rec.mId.toDebugString() + "]"; }; = [](const ESM::NPC& rec) { return "ESM3_NPC[" + rec.mId.toDebugString() + "]"; };
record["id"] record["id"]
@ -68,5 +98,182 @@ namespace MWLua
throw std::runtime_error("NPC expected"); throw std::runtime_error("NPC expected");
return MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(o.ptr()); 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<LObject*>(&actor) && !dynamic_cast<SelfObject*>(&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<ESM::Faction>().find(factionId);
auto ranksCount = static_cast<int>(getValidRanksCount(factionPtr));
if (ptr != MWBase::Environment::get().getWorld()->getPlayerPtr())
{
ESM::RefId primaryFactionId = ptr.getClass().getPrimaryFaction(ptr);
if (value <= 0 || factionId != primaryFactionId)
return;
}
MWMechanics::NpcStats& npcStats = ptr.getClass().getNpcStats(ptr);
if (!npcStats.isInFaction(factionId) && value > 0)
npcStats.joinFaction(factionId);
npcStats.setFactionRank(factionId, std::min(value - 1, ranksCount - 1));
};
npc["modifyFactionRank"] = [](Object& actor, std::string_view faction, int value) {
if (dynamic_cast<LObject*>(&actor) && !dynamic_cast<SelfObject*>(&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<ESM::Faction>().search(factionId);
if (!factionPtr)
return;
auto ranksCount = static_cast<int>(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, currentRank + value);
}
else if (value > 0)
{
npcStats.joinFaction(factionId);
npcStats.setFactionRank(factionId, std::min(value - 1, ranksCount - 1));
}
return;
}
ESM::RefId primaryFactionId = ptr.getClass().getPrimaryFaction(ptr);
if (factionId != primaryFactionId)
return;
// 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)
{
int rank = ptr.getClass().getPrimaryFactionRank(ptr);
npcStats.joinFaction(factionId);
npcStats.setFactionRank(factionId, std::clamp(0, rank + value, ranksCount - 1));
return;
}
else
npcStats.setFactionRank(factionId, std::clamp(0, currentRank + value, ranksCount - 1));
};
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<LObject*>(&actor) && !dynamic_cast<SelfObject*>(&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<LObject*>(&actor) && !dynamic_cast<SelfObject*>(&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<LObject*>(&actor) && !dynamic_cast<SelfObject*>(&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<LObject*>(&actor) && !dynamic_cast<SelfObject*>(&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;
};
} }
} }

View file

@ -1364,7 +1364,7 @@ namespace MWMechanics
const std::map<ESM::RefId, int>& playerRanks = player.getClass().getNpcStats(player).getFactionRanks(); const std::map<ESM::RefId, int>& playerRanks = player.getClass().getNpcStats(player).getFactionRanks();
if (playerRanks.find(factionID) != playerRanks.end()) if (playerRanks.find(factionID) != playerRanks.end())
{ {
player.getClass().getNpcStats(player).expell(factionID); player.getClass().getNpcStats(player).expell(factionID, true);
} }
} }
else if (!factionId.empty()) else if (!factionId.empty())
@ -1372,7 +1372,7 @@ namespace MWMechanics
const std::map<ESM::RefId, int>& playerRanks = player.getClass().getNpcStats(player).getFactionRanks(); const std::map<ESM::RefId, int>& playerRanks = player.getClass().getNpcStats(player).getFactionRanks();
if (playerRanks.find(factionId) != playerRanks.end()) if (playerRanks.find(factionId) != playerRanks.end())
{ {
player.getClass().getNpcStats(player).expell(factionId); player.getClass().getNpcStats(player).expell(factionId, true);
} }
} }

View file

@ -112,14 +112,17 @@ bool MWMechanics::NpcStats::getExpelled(const ESM::RefId& factionID) const
return mExpelled.find(factionID) != mExpelled.end(); 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()) if (mExpelled.find(factionID) == mExpelled.end())
{ {
mExpelled.insert(factionID);
if (!printMessage)
return;
std::string message = "#{sExpelledMessage}"; std::string message = "#{sExpelledMessage}";
message += MWBase::Environment::get().getESMStore()->get<ESM::Faction>().find(factionID)->mName; message += MWBase::Environment::get().getESMStore()->get<ESM::Faction>().find(factionID)->mName;
MWBase::Environment::get().getWindowManager()->messageBox(message); MWBase::Environment::get().getWindowManager()->messageBox(message);
mExpelled.insert(factionID);
} }
} }

View file

@ -74,7 +74,7 @@ namespace MWMechanics
const std::set<ESM::RefId>& getExpelled() const { return mExpelled; } const std::set<ESM::RefId>& getExpelled() const { return mExpelled; }
bool getExpelled(const ESM::RefId& factionID) const; 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); void clearExpelled(const ESM::RefId& factionID);
bool isInFaction(const ESM::RefId& faction) const; bool isInFaction(const ESM::RefId& faction) const;

View file

@ -938,7 +938,7 @@ namespace MWScript
MWWorld::Ptr player = MWMechanics::getPlayer(); MWWorld::Ptr player = MWMechanics::getPlayer();
if (!factionID.empty()) if (!factionID.empty())
{ {
player.getClass().getNpcStats(player).expell(factionID); player.getClass().getNpcStats(player).expell(factionID, true);
} }
} }
}; };

View file

@ -68,7 +68,7 @@ namespace ESM
std::map<ESM::RefId, int> mReactions; std::map<ESM::RefId, int> mReactions;
// Name of faction ranks (may be empty for NPC factions) // Name of faction ranks (may be empty for NPC factions)
std::string mRanks[10]; std::array<std::string, 10> mRanks;
void load(ESMReader& esm, bool& isDeleted); void load(ESMReader& esm, bool& isDeleted);
void save(ESMWriter& esm, bool isDeleted = false) const; void save(ESMWriter& esm, bool isDeleted = false) const;

View file

@ -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. -- 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 -- @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. -- Terminates the game and quits to the OS. Should be used only for testing purposes.
-- @function [parent=#core] quit -- @function [parent=#core] quit
@ -868,7 +872,6 @@
-- print(sound.fileName) -- print(sound.fileName)
-- end -- end
--- @{#Stats}: stats --- @{#Stats}: stats
-- @field [parent=#core] #Stats stats -- @field [parent=#core] #Stats stats
@ -920,4 +923,23 @@
-- @field #string failureSound VFS path to the failure sound -- @field #string failureSound VFS path to the failure sound
-- @field #string hitSound VFS path to the hit 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 return nil

View file

@ -720,6 +720,117 @@
-- @param openmw.core#GameObject object -- @param openmw.core#GameObject object
-- @return #boolean -- @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.
-- For NPCs faction should be an NPC's primary faction.
-- Notes:
--
-- * "value" <= 0 does nothing for NPCs and make the player character to leave the faction (purge his rank and reputation in faction).
-- * "value" > 0 set rank to given value if rank is valid (name is not empty), and to the highest valid rank otherwise.
-- @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.
-- For NPCs faction should be an NPC's primary faction.
-- Throws an exception if there is no such faction.
-- Notes:
--
-- * If rank should become <= 0 after modification, function does nothing for NPCs and makes the player character to leave the faction (purge his rank and reputation in faction).
-- * If rank should become > 0 after modification, function set rank to given value if rank is valid (name is not empty), and to the highest valid rank otherwise.
-- @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);
---
-- 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. -- 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 -- @function [parent=#NPC] getDisposition