Implement Lua API for factions (feature 7468)

macos_ci_fix
Andrei Kortunov 8 months ago
parent 9d3eb1a901
commit 6ee86dea82

@ -94,6 +94,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

@ -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 "")

@ -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

@ -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;
});
}
}

@ -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

@ -8,6 +8,7 @@
#include <components/esm3/loadarmo.hpp>
#include <components/esm3/loadbook.hpp>
#include <components/esm3/loadclot.hpp>
#include <components/esm3/loadfact.hpp>
#include <components/esm3/loadmisc.hpp>
#include <components/esm3/loadskil.hpp>
#include <components/esm3/loadweap.hpp>
@ -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<ESM::Faction>();
api["l10n"] = LuaUtil::initL10nLoader(lua->sol(), MWBase::Environment::get().getL10nManager());
const MWWorld::Store<ESM::GameSetting>* gmstStore
= &MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>();

@ -1,6 +1,7 @@
#include "actor.hpp"
#include "types.hpp"
#include <components/esm3/loadfact.hpp>
#include <components/esm3/loadnpc.hpp>
#include <components/lua/luastate.hpp>
@ -11,6 +12,7 @@
#include <apps/openmw/mwworld/class.hpp>
#include <apps/openmw/mwworld/esmstore.hpp>
#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<ESM::Faction>().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<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]
= [](const ESM::NPC& rec) { return "ESM3_NPC[" + rec.mId.toDebugString() + "]"; };
record["id"]
@ -68,5 +98,182 @@ 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<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;
};
}
}

@ -1364,7 +1364,7 @@ namespace MWMechanics
const std::map<ESM::RefId, int>& 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<ESM::RefId, int>& 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);
}
}

@ -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<ESM::Faction>().find(factionID)->mName;
MWBase::Environment::get().getWindowManager()->messageBox(message);
mExpelled.insert(factionID);
}
}

@ -74,7 +74,7 @@ namespace MWMechanics
const std::set<ESM::RefId>& 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;

@ -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);
}
}
};

@ -68,7 +68,7 @@ namespace ESM
std::map<ESM::RefId, int> mReactions;
// 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 save(ESMWriter& esm, bool isDeleted = false) const;

@ -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

@ -720,6 +720,117 @@
-- @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.
-- 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.
-- @function [parent=#NPC] getDisposition

Loading…
Cancel
Save