Commit 431f5407 authored by scrawl's avatar scrawl

ESSImport: fix NPC factions

parent 875df46a
......@@ -270,7 +270,7 @@ public:
faction.mExpelled = (it->mFlags & 0x2) != 0;
faction.mRank = it->mRank;
faction.mReputation = it->mReputation;
mContext->mPlayer.mObject.mNpcStats.mFactions[it->mFactionName.toString()] = faction;
mContext->mPlayer.mObject.mNpcStats.mFactions[Misc::StringUtils::lowerCase(it->mFactionName.toString())] = faction;
}
for (int i=0; i<8; ++i)
mContext->mPlayer.mObject.mNpcStats.mSkillIncrease[i] = pcdt.mPNAM.mSkillIncreases[i];
......
......@@ -296,25 +296,6 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
// NPC stats
if (!ref->mBase->mFaction.empty())
{
std::string faction = ref->mBase->mFaction;
if (const ESM::Faction* fact = MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().search(faction))
{
if(ref->mBase->mNpdtType != ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
{
data->mNpcStats.setFactionRank(fact->mId, (int)ref->mBase->mNpdt52.mRank);
}
else
{
data->mNpcStats.setFactionRank(fact->mId, (int)ref->mBase->mNpdt12.mRank);
}
}
else
std::cerr << "Warning: ignoring nonexistent faction '" << faction << "' on NPC '" << ref->mBase->mId << "'" << std::endl;
}
// creature stats
int gold=0;
if(ref->mBase->mNpdtType != ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
......@@ -371,13 +352,13 @@ namespace MWClass
std::cerr << "Warning: ignoring nonexistent race power '" << *iter << "' on NPC '" << ref->mBase->mId << "'" << std::endl;
}
if (data->mNpcStats.getFactionRanks().size())
if (!ref->mBase->mFaction.empty())
{
static const int iAutoRepFacMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("iAutoRepFacMod")->getInt();
static const int iAutoRepLevMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("iAutoRepLevMod")->getInt();
int rank = data->mNpcStats.getFactionRanks().begin()->second;
int rank = ref->mBase->getFactionRank();
data->mNpcStats.setReputation(iAutoRepFacMod * (rank+1) + iAutoRepLevMod * (data->mNpcStats.getLevel()-1));
}
......@@ -1371,4 +1352,16 @@ namespace MWClass
{
return true;
}
std::string Npc::getPrimaryFaction (const MWWorld::Ptr& ptr) const
{
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
return ref->mBase->mFaction;
}
int Npc::getPrimaryFactionRank (const MWWorld::Ptr& ptr) const
{
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
return ref->mBase->getFactionRank();
}
}
......@@ -189,6 +189,9 @@ namespace MWClass
virtual void restock (const MWWorld::Ptr& ptr) const;
virtual int getBaseFightRating (const MWWorld::Ptr& ptr) const;
virtual std::string getPrimaryFaction(const MWWorld::Ptr &ptr) const;
virtual int getPrimaryFactionRank(const MWWorld::Ptr &ptr) const;
};
}
......
......@@ -67,14 +67,11 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const
if (isCreature)
return false;
MWMechanics::NpcStats& stats = mActor.getClass().getNpcStats (mActor);
std::map<std::string, int>::const_iterator iter = stats.getFactionRanks().find ( Misc::StringUtils::lowerCase (info.mFaction));
if (iter==stats.getFactionRanks().end())
if (!Misc::StringUtils::ciEqual(mActor.getClass().getPrimaryFaction(mActor), info.mFaction))
return false;
// check rank
if (iter->second < info.mData.mRank)
if (mActor.getClass().getPrimaryFactionRank(mActor) < info.mData.mRank)
return false;
}
else if (info.mData.mRank != -1)
......@@ -83,13 +80,8 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const
return false;
// Rank requirement, but no faction given. Use the actor's faction, if there is one.
MWMechanics::NpcStats& stats = mActor.getClass().getNpcStats (mActor);
if (!stats.getFactionRanks().size())
return false;
// check rank
if (stats.getFactionRanks().begin()->second < info.mData.mRank)
if (mActor.getClass().getPrimaryFactionRank(mActor) < info.mData.mRank)
return false;
}
......@@ -336,12 +328,10 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con
case SelectWrapper::Function_RankRequirement:
{
if (mActor.getClass().getNpcStats (mActor).getFactionRanks().empty())
std::string faction = mActor.getClass().getPrimaryFaction(mActor);
if (faction.empty())
return 0;
std::string faction =
mActor.getClass().getNpcStats (mActor).getFactionRanks().begin()->first;
int rank = getFactionRank (player, faction);
if (rank>=9)
......@@ -376,15 +366,14 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con
case SelectWrapper::Function_FactionRankDiff:
{
if (mActor.getClass().getNpcStats (mActor).getFactionRanks().empty())
return 0;
const std::pair<std::string, int> faction =
*mActor.getClass().getNpcStats (mActor).getFactionRanks().begin();
std::string faction = mActor.getClass().getPrimaryFaction(mActor);
int rank = getFactionRank (player, faction.first);
if (faction.empty())
return 0;
return rank-faction.second;
int rank = getFactionRank (player, faction);
int npcRank = mActor.getClass().getPrimaryFactionRank(mActor);
return rank-npcRank;
}
case SelectWrapper::Function_WerewolfKills:
......@@ -396,11 +385,10 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con
{
bool low = select.getFunction()==SelectWrapper::Function_RankLow;
if (mActor.getClass().getNpcStats (mActor).getFactionRanks().empty())
return 0;
std::string factionId = mActor.getClass().getPrimaryFaction(mActor);
std::string factionId =
mActor.getClass().getNpcStats (mActor).getFactionRanks().begin()->first;
if (factionId.empty())
return 0;
int value = 0;
......@@ -454,7 +442,7 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
case SelectWrapper::Function_NotFaction:
return !Misc::StringUtils::ciEqual(mActor.get<ESM::NPC>()->mBase->mFaction, select.getName());
return !Misc::StringUtils::ciEqual(mActor.getClass().getPrimaryFaction(mActor), select.getName());
case SelectWrapper::Function_NotClass:
......@@ -494,8 +482,7 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
case SelectWrapper::Function_SameFaction:
return mActor.getClass().getNpcStats (mActor).isSameFaction (
player.getClass().getNpcStats (player));
return player.getClass().getNpcStats (player).isInFaction(mActor.getClass().getPrimaryFaction(mActor));
case SelectWrapper::Function_PcCommonDisease:
......@@ -512,11 +499,10 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
case SelectWrapper::Function_PcExpelled:
{
if (mActor.getClass().getNpcStats (mActor).getFactionRanks().empty())
return false;
std::string faction = mActor.getClass().getPrimaryFaction(mActor);
std::string faction =
mActor.getClass().getNpcStats (mActor).getFactionRanks().begin()->first;
if (faction.empty())
return false;
return player.getClass().getNpcStats(player).getExpelled(faction);
}
......@@ -561,7 +547,7 @@ int MWDialogue::Filter::getFactionRank (const MWWorld::Ptr& actor, const std::st
{
MWMechanics::NpcStats& stats = actor.getClass().getNpcStats (actor);
std::map<std::string, int>::const_iterator iter = stats.getFactionRanks().find (factionId);
std::map<std::string, int>::const_iterator iter = stats.getFactionRanks().find (Misc::StringUtils::lowerCase(factionId));
if (iter==stats.getFactionRanks().end())
return -1;
......
......@@ -572,8 +572,7 @@ namespace MWMechanics
float reaction = 0;
int rank = 0;
std::string npcFaction = "";
if(!npcSkill.getFactionRanks().empty()) npcFaction = npcSkill.getFactionRanks().begin()->first;
std::string npcFaction = ptr.getClass().getPrimaryFaction(ptr);
Misc::StringUtils::toLower(npcFaction);
......@@ -1156,10 +1155,10 @@ namespace MWMechanics
// If committing a crime against a faction member, expell from the faction
if (!victim.isEmpty() && victim.getClass().isNpc())
{
std::string factionID;
if(!victim.getClass().getNpcStats(victim).getFactionRanks().empty())
factionID = victim.getClass().getNpcStats(victim).getFactionRanks().begin()->first;
if (player.getClass().getNpcStats(player).isSameFaction(victim.getClass().getNpcStats(victim)))
std::string factionID = victim.getClass().getPrimaryFaction(victim);
const std::map<std::string, int>& playerRanks = player.getClass().getNpcStats(player).getFactionRanks();
if (playerRanks.find(Misc::StringUtils::lowerCase(factionID)) != playerRanks.end())
{
player.getClass().getNpcStats(player).expell(factionID);
}
......
......@@ -91,12 +91,6 @@ void MWMechanics::NpcStats::lowerRank(const std::string &faction)
}
}
void MWMechanics::NpcStats::setFactionRank(const std::string &faction, int rank)
{
const std::string lower = Misc::StringUtils::lowerCase(faction);
mFactionRank[lower] = rank;
}
void MWMechanics::NpcStats::joinFaction(const std::string& faction)
{
const std::string lower = Misc::StringUtils::lowerCase(faction);
......@@ -127,14 +121,9 @@ void MWMechanics::NpcStats::clearExpelled(const std::string& factionID)
mExpelled.erase(Misc::StringUtils::lowerCase(factionID));
}
bool MWMechanics::NpcStats::isSameFaction (const NpcStats& npcStats) const
bool MWMechanics::NpcStats::isInFaction (const std::string& faction) const
{
for (std::map<std::string, int>::const_iterator iter (mFactionRank.begin()); iter!=mFactionRank.end();
++iter)
if (npcStats.mFactionRank.find (iter->first)!=npcStats.mFactionRank.end())
return true;
return false;
return (mFactionRank.find(Misc::StringUtils::lowerCase(faction)) != mFactionRank.end());
}
float MWMechanics::NpcStats::getSkillGain (int skillIndex, const ESM::Class& class_, int usageType,
......
......@@ -33,9 +33,7 @@ namespace MWMechanics
// ----- used by the player only, maybe should be moved at some point -------
int mBounty;
int mWerewolfKills;
/// NPCs other than the player can only have one faction. But for the sake of consistency
/// we use the same data structure for the PC and the NPCs.
/// \note the faction key must be in lowercase
/// Used for the player only; NPCs have maximum one faction defined in their NPC record
std::map<std::string, int> mFactionRank;
std::set<std::string> mExpelled;
std::map<std::string, int> mFactionReputation;
......@@ -74,17 +72,13 @@ namespace MWMechanics
void lowerRank(const std::string& faction);
/// Join this faction, setting the initial rank to 0.
void joinFaction(const std::string& faction);
/// Warning: this function performs no check whether the rank exists,
/// and should be used in initial actor setup only.
void setFactionRank(const std::string& faction, int rank);
const std::set<std::string>& getExpelled() const { return mExpelled; }
bool getExpelled(const std::string& factionID) const;
void expell(const std::string& factionID);
void clearExpelled(const std::string& factionID);
bool isSameFaction (const NpcStats& npcStats) const;
///< Do *this and \a npcStats share a faction?
bool isInFaction (const std::string& faction) const;
float getSkillGain (int skillIndex, const ESM::Class& class_, int usageType = -1,
int level = -1, float extraFactor=1.f) const;
......
......@@ -196,7 +196,7 @@ namespace MWScript
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();
runtime.push (ptr.getClass().getNpcStats (ptr).isSameFaction (player.getClass().getNpcStats (player)));
player.getClass().getNpcStats (player).isInFaction(ptr.getClass().getPrimaryFaction(ptr));
}
};
......
......@@ -313,17 +313,19 @@ namespace MWScript
std::string InterpreterContext::getNPCRank() const
{
if (getReferenceImp().getClass().getNpcStats(getReferenceImp()).getFactionRanks().empty())
const MWWorld::Ptr& ptr = getReferenceImp();
std::string faction = ptr.getClass().getPrimaryFaction(ptr);
if (faction.empty())
throw std::runtime_error("getNPCRank(): NPC is not in a faction");
const std::map<std::string, int>& ranks = getReferenceImp().getClass().getNpcStats (getReferenceImp()).getFactionRanks();
std::map<std::string, int>::const_iterator it = ranks.begin();
int rank = ptr.getClass().getPrimaryFactionRank(ptr);
if (rank < 0 || rank > 9)
throw std::runtime_error("getNPCRank(): invalid rank");
MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::ESMStore &store = world->getStore();
const ESM::Faction *faction = store.get<ESM::Faction>().find(it->first);
return faction->mRanks[it->second];
const ESM::Faction *fact = store.get<ESM::Faction>().find(faction);
return fact->mRanks[rank];
}
std::string InterpreterContext::getPCName() const
......@@ -352,13 +354,12 @@ namespace MWScript
MWBase::World *world = MWBase::Environment::get().getWorld();
MWWorld::Ptr player = world->getPlayerPtr();
if (getReferenceImp().getClass().getNpcStats(getReferenceImp()).getFactionRanks().empty())
std::string factionId = getReferenceImp().getClass().getPrimaryFaction(getReferenceImp());
if (factionId.empty())
throw std::runtime_error("getPCRank(): NPC is not in a faction");
std::string factionId = getReferenceImp().getClass().getNpcStats (getReferenceImp()).getFactionRanks().begin()->first;
const std::map<std::string, int>& ranks = player.getClass().getNpcStats (player).getFactionRanks();
std::map<std::string, int>::const_iterator it = ranks.find(factionId);
std::map<std::string, int>::const_iterator it = ranks.find(Misc::StringUtils::lowerCase(factionId));
int rank = -1;
if (it != ranks.end())
rank = it->second;
......@@ -382,13 +383,12 @@ namespace MWScript
MWBase::World *world = MWBase::Environment::get().getWorld();
MWWorld::Ptr player = world->getPlayerPtr();
if (getReferenceImp().getClass().getNpcStats(getReferenceImp()).getFactionRanks().empty())
std::string factionId = getReferenceImp().getClass().getPrimaryFaction(getReferenceImp());
if (factionId.empty())
throw std::runtime_error("getPCNextRank(): NPC is not in a faction");
std::string factionId = getReferenceImp().getClass().getNpcStats (getReferenceImp()).getFactionRanks().begin()->first;
const std::map<std::string, int>& ranks = player.getClass().getNpcStats (player).getFactionRanks();
std::map<std::string, int>::const_iterator it = ranks.find(factionId);
std::map<std::string, int>::const_iterator it = ranks.find(Misc::StringUtils::lowerCase(factionId));
int rank = -1;
if (it != ranks.end())
rank = it->second;
......
......@@ -32,13 +32,12 @@ namespace
{
std::string getDialogueActorFaction(MWWorld::Ptr actor)
{
const MWMechanics::NpcStats &stats = actor.getClass().getNpcStats (actor);
if (stats.getFactionRanks().empty())
std::string factionId = actor.getClass().getPrimaryFaction(actor);
if (factionId.empty())
throw std::runtime_error (
"failed to determine dialogue actors faction (because actor is factionless)");
return stats.getFactionRanks().begin()->first;
return factionId;
}
}
......@@ -665,14 +664,7 @@ namespace MWScript
}
else
{
if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty())
{
factionID = "";
}
else
{
factionID = ptr.getClass().getNpcStats(ptr).getFactionRanks().begin()->first;
}
factionID = ptr.getClass().getPrimaryFaction(ptr);
}
::Misc::StringUtils::toLower(factionID);
// Make sure this faction exists
......@@ -779,8 +771,7 @@ namespace MWScript
}
else
{
if (!ptr.getClass().getNpcStats (ptr).getFactionRanks().empty())
factionId = ptr.getClass().getNpcStats (ptr).getFactionRanks().begin()->first;
factionId = getDialogueActorFaction(ptr);
}
if (factionId.empty())
......@@ -815,8 +806,7 @@ namespace MWScript
}
else
{
if (!ptr.getClass().getNpcStats (ptr).getFactionRanks().empty())
factionId = ptr.getClass().getNpcStats (ptr).getFactionRanks().begin()->first;
factionId = getDialogueActorFaction(ptr);
}
if (factionId.empty())
......@@ -850,8 +840,7 @@ namespace MWScript
}
else
{
if (!ptr.getClass().getNpcStats (ptr).getFactionRanks().empty())
factionId = ptr.getClass().getNpcStats (ptr).getFactionRanks().begin()->first;
factionId = getDialogueActorFaction(ptr);
}
if (factionId.empty())
......@@ -941,14 +930,7 @@ namespace MWScript
}
else
{
if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty())
{
factionID = "";
}
else
{
factionID = ptr.getClass().getNpcStats(ptr).getFactionRanks().begin()->first;
}
factionID = ptr.getClass().getPrimaryFaction(ptr);
}
::Misc::StringUtils::toLower(factionID);
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
......@@ -980,14 +962,7 @@ namespace MWScript
}
else
{
if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty())
{
factionID = "";
}
else
{
factionID = ptr.getClass().getNpcStats(ptr).getFactionRanks().begin()->first;
}
factionID = ptr.getClass().getPrimaryFaction(ptr);
}
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
if(factionID!="")
......@@ -1014,14 +989,7 @@ namespace MWScript
}
else
{
if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty())
{
factionID = "";
}
else
{
factionID = ptr.getClass().getNpcStats(ptr).getFactionRanks().begin()->first;
}
factionID = ptr.getClass().getPrimaryFaction(ptr);
}
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
if(factionID!="")
......@@ -1038,13 +1006,10 @@ namespace MWScript
{
MWWorld::Ptr ptr = R()(runtime);
std::string factionID = "";
if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty())
std::string factionID = ptr.getClass().getPrimaryFaction(ptr);
if(factionID.empty())
return;
else
{
factionID = ptr.getClass().getNpcStats(ptr).getFactionRanks().begin()->first;
}
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
// no-op when executed on the player
......@@ -1064,13 +1029,10 @@ namespace MWScript
{
MWWorld::Ptr ptr = R()(runtime);
std::string factionID = "";
if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty())
std::string factionID = ptr.getClass().getPrimaryFaction(ptr);
if(factionID.empty())
return;
else
{
factionID = ptr.getClass().getNpcStats(ptr).getFactionRanks().begin()->first;
}
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
// no-op when executed on the player
......
......@@ -445,4 +445,13 @@ namespace MWWorld
{
throw std::runtime_error("class does not support fight rating");
}
std::string Class::getPrimaryFaction (const MWWorld::Ptr& ptr) const
{
return std::string();
}
int Class::getPrimaryFactionRank (const MWWorld::Ptr& ptr) const
{
return -1;
}
}
......@@ -342,6 +342,9 @@ namespace MWWorld
virtual std::string getSound(const MWWorld::Ptr& ptr) const;
virtual int getBaseFightRating (const MWWorld::Ptr& ptr) const;
virtual std::string getPrimaryFaction (const MWWorld::Ptr& ptr) const;
virtual int getPrimaryFactionRank (const MWWorld::Ptr& ptr) const;
};
}
......
......@@ -144,4 +144,14 @@ void NPC::save(ESMWriter &esm) const
mHair.clear();
mHead.clear();
}
int NPC::getFactionRank() const
{
if (mFaction.empty())
return -1;
else if (mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
return mNpdt12.mRank;
else // NPC_DEFAULT
return mNpdt52.mRank;
}
}
......@@ -110,6 +110,8 @@ struct NPC
NPDTstruct52 mNpdt52;
NPDTstruct12 mNpdt12; //for autocalculated characters
int getFactionRank() const; /// wrapper for mNpdt*, -1 = no rank
int mFlags;
bool mPersistent;
......
......@@ -34,7 +34,7 @@ namespace ESM
StatState<int> mWerewolfAttributes[8];
bool mIsWerewolf;
std::map<std::string, Faction> mFactions;
std::map<std::string, Faction> mFactions; // lower case IDs
int mDisposition;
Skill mSkills[27];
int mBounty;
......@@ -43,7 +43,7 @@ namespace ESM
int mProfit;
int mLevelProgress;
int mSkillIncrease[8];
std::vector<std::string> mUsedIds;
std::vector<std::string> mUsedIds; // lower case IDs
float mTimeToStartDrowning;
int mCrimeId;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment