diff --git a/apps/openmw/mwlua/types/npc.cpp b/apps/openmw/mwlua/types/npc.cpp index ecb6b80dc9..a3bb82098d 100644 --- a/apps/openmw/mwlua/types/npc.cpp +++ b/apps/openmw/mwlua/types/npc.cpp @@ -136,20 +136,23 @@ namespace MWLua = 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 (value <= 0 || factionId != primaryFactionId) - return; + 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()); - if (!npcStats.isInFaction(factionId) && value > 0) - npcStats.joinFaction(factionId); - - npcStats.setFactionRank(factionId, std::min(value - 1, ranksCount - 1)); + npcStats.setFactionRank(factionId, targetRank); }; npc["modifyFactionRank"] = [](Object& actor, std::string_view faction, int value) { @@ -175,35 +178,62 @@ namespace MWLua { 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)); - } + 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) - return; + 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) { - int rank = ptr.getClass().getPrimaryFactionRank(ptr); + currentRank = ptr.getClass().getPrimaryFactionRank(ptr); npcStats.joinFaction(factionId); - npcStats.setFactionRank(factionId, std::clamp(0, rank + value, ranksCount - 1)); + } + 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; } - else - npcStats.setFactionRank(factionId, std::clamp(0, currentRank + value, ranksCount - 1)); + + 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) { diff --git a/files/lua_api/openmw/types.lua b/files/lua_api/openmw/types.lua index 283be0b2fc..03efe885b5 100644 --- a/files/lua_api/openmw/types.lua +++ b/files/lua_api/openmw/types.lua @@ -744,12 +744,8 @@ --- -- 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. +-- 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 @@ -759,12 +755,12 @@ --- -- 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. +-- 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 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. +-- * 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 @@ -772,6 +768,27 @@ -- @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.