2012-07-06 13:50:26 +00:00
|
|
|
#include "npcstats.hpp"
|
|
|
|
|
2014-02-19 17:40:29 +00:00
|
|
|
#include <iomanip>
|
|
|
|
|
2012-07-06 19:07:04 +00:00
|
|
|
#include <components/esm/loadclas.hpp>
|
|
|
|
#include <components/esm/loadgmst.hpp>
|
2012-11-12 12:23:25 +00:00
|
|
|
#include <components/esm/loadfact.hpp>
|
2014-02-16 14:06:34 +00:00
|
|
|
#include <components/esm/npcstats.hpp>
|
2012-07-06 19:07:04 +00:00
|
|
|
|
2012-10-01 15:17:04 +00:00
|
|
|
#include "../mwworld/esmstore.hpp"
|
2012-07-06 19:07:04 +00:00
|
|
|
|
|
|
|
#include "../mwbase/environment.hpp"
|
|
|
|
#include "../mwbase/world.hpp"
|
2012-09-15 15:12:42 +00:00
|
|
|
#include "../mwbase/windowmanager.hpp"
|
2012-07-06 19:07:04 +00:00
|
|
|
|
2012-07-06 13:50:26 +00:00
|
|
|
MWMechanics::NpcStats::NpcStats()
|
2015-05-01 00:24:27 +00:00
|
|
|
: mDisposition (0)
|
2013-03-31 11:13:46 +00:00
|
|
|
, mReputation(0)
|
2014-04-05 14:26:14 +00:00
|
|
|
, mCrimeId(-1)
|
2015-05-01 00:24:27 +00:00
|
|
|
, mBounty(0)
|
2013-03-31 11:13:46 +00:00
|
|
|
, mWerewolfKills (0)
|
2015-05-01 00:24:27 +00:00
|
|
|
, mLevelProgress(0)
|
2017-04-16 18:15:25 +00:00
|
|
|
, mTimeToStartDrowning(-1.0) // set breath to special value, it will be replaced during actor update
|
2015-06-21 15:27:52 +00:00
|
|
|
, mIsWerewolf(false)
|
2012-09-15 15:12:42 +00:00
|
|
|
{
|
2014-01-15 06:47:21 +00:00
|
|
|
mSkillIncreases.resize (ESM::Attribute::Length, 0);
|
2016-06-26 01:22:58 +00:00
|
|
|
mSpecIncreases.resize(3, 0);
|
2012-09-15 15:12:42 +00:00
|
|
|
}
|
2012-07-06 13:50:26 +00:00
|
|
|
|
2012-11-09 19:18:38 +00:00
|
|
|
int MWMechanics::NpcStats::getBaseDisposition() const
|
2012-11-05 10:07:43 +00:00
|
|
|
{
|
|
|
|
return mDisposition;
|
|
|
|
}
|
|
|
|
|
2012-11-09 19:18:38 +00:00
|
|
|
void MWMechanics::NpcStats::setBaseDisposition(int disposition)
|
2012-11-05 10:07:43 +00:00
|
|
|
{
|
|
|
|
mDisposition = disposition;
|
|
|
|
}
|
|
|
|
|
2014-01-03 00:59:15 +00:00
|
|
|
const MWMechanics::SkillValue& MWMechanics::NpcStats::getSkill (int index) const
|
2012-07-06 16:23:48 +00:00
|
|
|
{
|
2013-12-08 20:47:43 +00:00
|
|
|
if (index<0 || index>=ESM::Skill::Length)
|
2012-07-06 16:23:48 +00:00
|
|
|
throw std::runtime_error ("skill index out of range");
|
|
|
|
|
2015-06-21 15:27:52 +00:00
|
|
|
return mSkill[index];
|
2012-07-06 16:23:48 +00:00
|
|
|
}
|
|
|
|
|
2014-01-03 00:59:15 +00:00
|
|
|
MWMechanics::SkillValue& MWMechanics::NpcStats::getSkill (int index)
|
2012-07-06 16:23:48 +00:00
|
|
|
{
|
2013-12-08 20:47:43 +00:00
|
|
|
if (index<0 || index>=ESM::Skill::Length)
|
2012-07-06 16:23:48 +00:00
|
|
|
throw std::runtime_error ("skill index out of range");
|
|
|
|
|
2015-06-21 15:27:52 +00:00
|
|
|
return mSkill[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
void MWMechanics::NpcStats::setSkill(int index, const MWMechanics::SkillValue &value)
|
|
|
|
{
|
|
|
|
if (index<0 || index>=ESM::Skill::Length)
|
|
|
|
throw std::runtime_error ("skill index out of range");
|
|
|
|
|
|
|
|
mSkill[index] = value;
|
2012-07-06 16:23:48 +00:00
|
|
|
}
|
|
|
|
|
2013-08-09 08:14:08 +00:00
|
|
|
const std::map<std::string, int>& MWMechanics::NpcStats::getFactionRanks() const
|
|
|
|
{
|
|
|
|
return mFactionRank;
|
|
|
|
}
|
|
|
|
|
2019-05-14 06:12:40 +00:00
|
|
|
int MWMechanics::NpcStats::getFactionRank(const std::string &faction) const
|
|
|
|
{
|
|
|
|
const std::string lower = Misc::StringUtils::lowerCase(faction);
|
|
|
|
std::map<std::string, int>::const_iterator it = mFactionRank.find(lower);
|
|
|
|
if (it != mFactionRank.end())
|
|
|
|
return it->second;
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-10-05 14:47:55 +00:00
|
|
|
void MWMechanics::NpcStats::raiseRank(const std::string &faction)
|
2012-07-06 16:23:48 +00:00
|
|
|
{
|
2014-10-05 14:47:55 +00:00
|
|
|
const std::string lower = Misc::StringUtils::lowerCase(faction);
|
|
|
|
std::map<std::string, int>::iterator it = mFactionRank.find(lower);
|
|
|
|
if (it != mFactionRank.end())
|
|
|
|
{
|
|
|
|
// Does the next rank exist?
|
2016-10-05 16:12:06 +00:00
|
|
|
const ESM::Faction* factionPtr = MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(lower);
|
|
|
|
if (it->second+1 < 10 && !factionPtr->mRanks[it->second+1].empty())
|
2014-10-05 14:47:55 +00:00
|
|
|
it->second += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MWMechanics::NpcStats::lowerRank(const std::string &faction)
|
|
|
|
{
|
|
|
|
const std::string lower = Misc::StringUtils::lowerCase(faction);
|
|
|
|
std::map<std::string, int>::iterator it = mFactionRank.find(lower);
|
|
|
|
if (it != mFactionRank.end())
|
|
|
|
{
|
2019-05-12 17:57:54 +00:00
|
|
|
it->second = it->second-1;
|
|
|
|
if (it->second < 0)
|
2019-05-13 07:22:03 +00:00
|
|
|
{
|
2019-05-12 17:57:54 +00:00
|
|
|
mFactionRank.erase(it);
|
2019-05-13 07:22:03 +00:00
|
|
|
mExpelled.erase(lower);
|
|
|
|
}
|
2014-10-05 14:47:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MWMechanics::NpcStats::joinFaction(const std::string& faction)
|
|
|
|
{
|
|
|
|
const std::string lower = Misc::StringUtils::lowerCase(faction);
|
|
|
|
std::map<std::string, int>::iterator it = mFactionRank.find(lower);
|
|
|
|
if (it == mFactionRank.end())
|
|
|
|
mFactionRank[lower] = 0;
|
2012-07-06 16:23:48 +00:00
|
|
|
}
|
|
|
|
|
2014-01-08 17:59:00 +00:00
|
|
|
bool MWMechanics::NpcStats::getExpelled(const std::string& factionID) const
|
2012-11-10 11:28:40 +00:00
|
|
|
{
|
2014-01-08 17:59:00 +00:00
|
|
|
return mExpelled.find(Misc::StringUtils::lowerCase(factionID)) != mExpelled.end();
|
2012-11-10 11:28:40 +00:00
|
|
|
}
|
|
|
|
|
2014-01-08 17:59:00 +00:00
|
|
|
void MWMechanics::NpcStats::expell(const std::string& factionID)
|
2012-07-06 16:23:48 +00:00
|
|
|
{
|
2014-01-08 17:59:00 +00:00
|
|
|
std::string lower = Misc::StringUtils::lowerCase(factionID);
|
|
|
|
if (mExpelled.find(lower) == mExpelled.end())
|
|
|
|
{
|
|
|
|
std::string message = "#{sExpelledMessage}";
|
|
|
|
message += MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(factionID)->mName;
|
|
|
|
MWBase::Environment::get().getWindowManager()->messageBox(message);
|
|
|
|
mExpelled.insert(lower);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MWMechanics::NpcStats::clearExpelled(const std::string& factionID)
|
|
|
|
{
|
|
|
|
mExpelled.erase(Misc::StringUtils::lowerCase(factionID));
|
2012-07-06 16:23:48 +00:00
|
|
|
}
|
2012-07-06 19:07:04 +00:00
|
|
|
|
2015-01-27 16:32:21 +00:00
|
|
|
bool MWMechanics::NpcStats::isInFaction (const std::string& faction) const
|
2012-11-10 08:35:50 +00:00
|
|
|
{
|
2015-01-27 16:32:21 +00:00
|
|
|
return (mFactionRank.find(Misc::StringUtils::lowerCase(faction)) != mFactionRank.end());
|
2012-11-10 08:35:50 +00:00
|
|
|
}
|
|
|
|
|
2015-02-08 20:04:01 +00:00
|
|
|
int MWMechanics::NpcStats::getFactionReputation (const std::string& faction) const
|
|
|
|
{
|
|
|
|
std::map<std::string, int>::const_iterator iter = mFactionReputation.find (Misc::StringUtils::lowerCase(faction));
|
|
|
|
|
|
|
|
if (iter==mFactionReputation.end())
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return iter->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MWMechanics::NpcStats::setFactionReputation (const std::string& faction, int value)
|
|
|
|
{
|
|
|
|
mFactionReputation[Misc::StringUtils::lowerCase(faction)] = value;
|
|
|
|
}
|
|
|
|
|
2015-02-04 22:15:12 +00:00
|
|
|
float MWMechanics::NpcStats::getSkillProgressRequirement (int skillIndex, const ESM::Class& class_) const
|
2012-07-06 19:07:04 +00:00
|
|
|
{
|
2015-03-08 04:42:07 +00:00
|
|
|
float progressRequirement = static_cast<float>(1 + getSkill(skillIndex).getBase());
|
2014-09-25 10:25:57 +00:00
|
|
|
|
2012-11-05 17:19:22 +00:00
|
|
|
const MWWorld::Store<ESM::GameSetting> &gmst =
|
|
|
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
|
|
|
|
2018-08-29 15:38:12 +00:00
|
|
|
float typeFactor = gmst.find ("fMiscSkillBonus")->mValue.getFloat();
|
2012-07-06 19:07:04 +00:00
|
|
|
|
|
|
|
for (int i=0; i<5; ++i)
|
2019-01-24 13:33:18 +00:00
|
|
|
{
|
2012-09-17 07:37:50 +00:00
|
|
|
if (class_.mData.mSkills[i][0]==skillIndex)
|
2012-07-06 19:07:04 +00:00
|
|
|
{
|
2018-08-29 15:38:12 +00:00
|
|
|
typeFactor = gmst.find ("fMinorSkillBonus")->mValue.getFloat();
|
2012-07-06 19:07:04 +00:00
|
|
|
break;
|
|
|
|
}
|
2019-01-24 13:33:18 +00:00
|
|
|
else if (class_.mData.mSkills[i][1]==skillIndex)
|
2012-07-06 19:07:04 +00:00
|
|
|
{
|
2018-08-29 15:38:12 +00:00
|
|
|
typeFactor = gmst.find ("fMajorSkillBonus")->mValue.getFloat();
|
2012-07-06 19:07:04 +00:00
|
|
|
break;
|
|
|
|
}
|
2019-01-24 13:33:18 +00:00
|
|
|
}
|
2012-07-06 19:07:04 +00:00
|
|
|
|
2015-02-04 22:15:12 +00:00
|
|
|
progressRequirement *= typeFactor;
|
|
|
|
|
2012-07-06 19:07:04 +00:00
|
|
|
if (typeFactor<=0)
|
|
|
|
throw std::runtime_error ("invalid skill type factor");
|
|
|
|
|
|
|
|
float specialisationFactor = 1;
|
|
|
|
|
2015-02-04 22:15:12 +00:00
|
|
|
const ESM::Skill *skill =
|
|
|
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::Skill>().find (skillIndex);
|
2012-09-17 07:37:50 +00:00
|
|
|
if (skill->mData.mSpecialization==class_.mData.mSpecialization)
|
2012-07-06 19:07:04 +00:00
|
|
|
{
|
2018-08-29 15:38:12 +00:00
|
|
|
specialisationFactor = gmst.find ("fSpecialSkillBonus")->mValue.getFloat();
|
2012-07-06 19:07:04 +00:00
|
|
|
|
|
|
|
if (specialisationFactor<=0)
|
|
|
|
throw std::runtime_error ("invalid skill specialisation factor");
|
|
|
|
}
|
2015-02-04 22:15:12 +00:00
|
|
|
progressRequirement *= specialisationFactor;
|
|
|
|
|
|
|
|
return progressRequirement;
|
2012-07-06 19:07:04 +00:00
|
|
|
}
|
2012-07-09 19:15:52 +00:00
|
|
|
|
2014-09-25 10:25:57 +00:00
|
|
|
void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_, int usageType, float extraFactor)
|
2012-07-09 19:15:52 +00:00
|
|
|
{
|
2015-02-04 22:15:12 +00:00
|
|
|
const ESM::Skill *skill =
|
|
|
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::Skill>().find (skillIndex);
|
|
|
|
float skillGain = 1;
|
|
|
|
if (usageType>=4)
|
|
|
|
throw std::runtime_error ("skill usage type out of range");
|
|
|
|
if (usageType>=0)
|
|
|
|
{
|
|
|
|
skillGain = skill->mData.mUseValue[usageType];
|
|
|
|
if (skillGain<0)
|
|
|
|
throw std::runtime_error ("invalid skill gain factor");
|
|
|
|
}
|
|
|
|
skillGain *= extraFactor;
|
|
|
|
|
2014-01-13 06:05:52 +00:00
|
|
|
MWMechanics::SkillValue& value = getSkill (skillIndex);
|
2012-07-09 19:15:52 +00:00
|
|
|
|
2015-02-04 22:15:12 +00:00
|
|
|
value.setProgress(value.getProgress() + skillGain);
|
2012-07-09 19:15:52 +00:00
|
|
|
|
2015-02-04 22:15:12 +00:00
|
|
|
if (int(value.getProgress())>=int(getSkillProgressRequirement(skillIndex, class_)))
|
2012-09-15 15:12:42 +00:00
|
|
|
{
|
2015-01-31 23:26:06 +00:00
|
|
|
// skill levelled up
|
2012-09-15 17:06:56 +00:00
|
|
|
increaseSkill(skillIndex, class_, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-24 10:43:18 +00:00
|
|
|
void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &class_, bool preserveProgress, bool readBook)
|
2012-09-15 17:06:56 +00:00
|
|
|
{
|
2018-12-23 11:18:33 +00:00
|
|
|
float base = getSkill (skillIndex).getBase();
|
2012-09-15 17:06:56 +00:00
|
|
|
|
2018-12-23 11:18:33 +00:00
|
|
|
if (base >= 100.f)
|
2012-09-15 17:10:48 +00:00
|
|
|
return;
|
|
|
|
|
2014-01-03 02:46:30 +00:00
|
|
|
base += 1;
|
2012-07-09 19:15:52 +00:00
|
|
|
|
2014-01-26 20:50:40 +00:00
|
|
|
const MWWorld::Store<ESM::GameSetting> &gmst =
|
|
|
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
|
|
|
|
|
|
|
// is this a minor or major skill?
|
2018-08-29 15:38:12 +00:00
|
|
|
int increase = gmst.find("iLevelupMiscMultAttriubte")->mValue.getInteger(); // Note: GMST has a typo
|
2014-01-26 20:50:40 +00:00
|
|
|
for (int k=0; k<5; ++k)
|
|
|
|
{
|
|
|
|
if (class_.mData.mSkills[k][0] == skillIndex)
|
2012-09-15 15:12:42 +00:00
|
|
|
{
|
2018-08-29 15:38:12 +00:00
|
|
|
mLevelProgress += gmst.find("iLevelUpMinorMult")->mValue.getInteger();
|
2019-01-24 13:33:18 +00:00
|
|
|
increase = gmst.find("iLevelUpMinorMultAttribute")->mValue.getInteger();
|
|
|
|
break;
|
2012-09-15 15:12:42 +00:00
|
|
|
}
|
2019-01-24 13:33:18 +00:00
|
|
|
else if (class_.mData.mSkills[k][1] == skillIndex)
|
2014-01-26 20:50:40 +00:00
|
|
|
{
|
2018-08-29 15:38:12 +00:00
|
|
|
mLevelProgress += gmst.find("iLevelUpMajorMult")->mValue.getInteger();
|
2019-01-24 13:33:18 +00:00
|
|
|
increase = gmst.find("iLevelUpMajorMultAttribute")->mValue.getInteger();
|
|
|
|
break;
|
2014-01-26 20:50:40 +00:00
|
|
|
}
|
|
|
|
}
|
2012-09-15 17:06:56 +00:00
|
|
|
|
2012-11-05 17:19:22 +00:00
|
|
|
const ESM::Skill* skill =
|
|
|
|
MWBase::Environment::get().getWorld ()->getStore ().get<ESM::Skill>().find(skillIndex);
|
2014-01-26 20:50:40 +00:00
|
|
|
mSkillIncreases[skill->mData.mAttribute] += increase;
|
2012-09-15 17:06:56 +00:00
|
|
|
|
2018-08-29 15:38:12 +00:00
|
|
|
mSpecIncreases[skill->mData.mSpecialization] += gmst.find("iLevelupSpecialization")->mValue.getInteger();
|
2016-06-26 01:22:58 +00:00
|
|
|
|
2012-09-15 17:06:56 +00:00
|
|
|
// Play sound & skill progress notification
|
|
|
|
/// \todo check if character is the player, if levelling is ever implemented for NPCs
|
2017-07-10 11:48:00 +00:00
|
|
|
MWBase::Environment::get().getWindowManager()->playSound("skillraise");
|
2012-09-15 17:06:56 +00:00
|
|
|
|
2019-02-22 21:14:07 +00:00
|
|
|
std::string message = MWBase::Environment::get().getWindowManager ()->getGameSettingString ("sNotifyMessage39", "");
|
2020-06-06 12:24:33 +00:00
|
|
|
message = Misc::StringUtils::format(message, ("#{" + ESM::Skill::sSkillNameIds[skillIndex] + "}"), static_cast<int>(base));
|
2018-03-24 10:43:18 +00:00
|
|
|
|
|
|
|
if (readBook)
|
2019-02-22 21:14:07 +00:00
|
|
|
message = "#{sBookSkillMessage}\n" + message;
|
2018-03-24 10:43:18 +00:00
|
|
|
|
2019-02-22 21:14:07 +00:00
|
|
|
MWBase::Environment::get().getWindowManager ()->messageBox(message, MWGui::ShowInDialogueMode_Never);
|
2012-09-15 17:06:56 +00:00
|
|
|
|
2018-08-29 15:38:12 +00:00
|
|
|
if (mLevelProgress >= gmst.find("iLevelUpTotal")->mValue.getInteger())
|
2012-09-15 17:06:56 +00:00
|
|
|
{
|
|
|
|
// levelup is possible now
|
2015-01-10 22:21:39 +00:00
|
|
|
MWBase::Environment::get().getWindowManager ()->messageBox ("#{sLevelUpMsg}", MWGui::ShowInDialogueMode_Never);
|
2012-09-15 17:06:56 +00:00
|
|
|
}
|
2012-09-15 15:12:42 +00:00
|
|
|
|
2015-02-04 22:15:12 +00:00
|
|
|
getSkill(skillIndex).setBase (base);
|
2014-01-03 02:46:30 +00:00
|
|
|
if (!preserveProgress)
|
|
|
|
getSkill(skillIndex).setProgress(0);
|
2012-07-09 19:15:52 +00:00
|
|
|
}
|
2012-09-15 15:12:42 +00:00
|
|
|
|
|
|
|
int MWMechanics::NpcStats::getLevelProgress () const
|
|
|
|
{
|
|
|
|
return mLevelProgress;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MWMechanics::NpcStats::levelUp()
|
|
|
|
{
|
2014-01-26 20:08:11 +00:00
|
|
|
const MWWorld::Store<ESM::GameSetting> &gmst =
|
|
|
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
|
|
|
|
2018-08-29 15:38:12 +00:00
|
|
|
mLevelProgress -= gmst.find("iLevelUpTotal")->mValue.getInteger();
|
2015-02-05 00:08:26 +00:00
|
|
|
mLevelProgress = std::max(0, mLevelProgress); // might be necessary when levelup was invoked via console
|
2015-02-05 00:03:10 +00:00
|
|
|
|
|
|
|
for (int i=0; i<ESM::Attribute::Length; ++i)
|
|
|
|
mSkillIncreases[i] = 0;
|
|
|
|
|
2018-12-23 11:18:33 +00:00
|
|
|
const float endurance = getAttribute(ESM::Attribute::Endurance).getBase();
|
2014-01-26 20:08:11 +00:00
|
|
|
|
|
|
|
// "When you gain a level, in addition to increasing three primary attributes, your Health
|
|
|
|
// will automatically increase by 10% of your Endurance attribute. If you increased Endurance this level,
|
|
|
|
// the Health increase is calculated from the increased Endurance"
|
2018-09-22 03:46:47 +00:00
|
|
|
// Note: we should add bonus Health points to current level too.
|
|
|
|
float healthGain = endurance * gmst.find("fLevelUpHealthEndMult")->mValue.getFloat();
|
|
|
|
MWMechanics::DynamicStat<float> health(getHealth());
|
|
|
|
health.setBase(getHealth().getBase() + healthGain);
|
|
|
|
health.setCurrent(std::max(1.f, getHealth().getCurrent() + healthGain));
|
|
|
|
setHealth(health);
|
2014-01-26 20:08:11 +00:00
|
|
|
|
|
|
|
setLevel(getLevel()+1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MWMechanics::NpcStats::updateHealth()
|
|
|
|
{
|
2018-12-23 11:18:33 +00:00
|
|
|
const float endurance = getAttribute(ESM::Attribute::Endurance).getBase();
|
|
|
|
const float strength = getAttribute(ESM::Attribute::Strength).getBase();
|
2014-01-26 20:08:11 +00:00
|
|
|
|
2015-03-08 04:42:07 +00:00
|
|
|
setHealth(floor(0.5f * (strength + endurance)));
|
2012-09-15 15:12:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int MWMechanics::NpcStats::getLevelupAttributeMultiplier(int attribute) const
|
|
|
|
{
|
|
|
|
int num = mSkillIncreases[attribute];
|
2014-01-26 20:50:40 +00:00
|
|
|
|
|
|
|
if (num == 0)
|
2012-09-15 15:12:42 +00:00
|
|
|
return 1;
|
2014-01-26 20:50:40 +00:00
|
|
|
|
|
|
|
num = std::min(10, num);
|
|
|
|
|
|
|
|
// iLevelUp01Mult - iLevelUp10Mult
|
|
|
|
std::stringstream gmst;
|
|
|
|
gmst << "iLevelUp" << std::setfill('0') << std::setw(2) << num << "Mult";
|
|
|
|
|
2018-08-29 15:38:12 +00:00
|
|
|
return MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(gmst.str())->mValue.getInteger();
|
2012-09-15 15:12:42 +00:00
|
|
|
}
|
2012-09-25 08:48:57 +00:00
|
|
|
|
2016-06-26 01:22:58 +00:00
|
|
|
int MWMechanics::NpcStats::getSkillIncreasesForSpecialization(int spec) const
|
|
|
|
{
|
|
|
|
return mSpecIncreases[spec];
|
|
|
|
}
|
|
|
|
|
2012-09-25 08:48:57 +00:00
|
|
|
void MWMechanics::NpcStats::flagAsUsed (const std::string& id)
|
|
|
|
{
|
|
|
|
mUsedIds.insert (id);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MWMechanics::NpcStats::hasBeenUsed (const std::string& id) const
|
|
|
|
{
|
|
|
|
return mUsedIds.find (id)!=mUsedIds.end();
|
|
|
|
}
|
2012-11-09 13:31:38 +00:00
|
|
|
|
|
|
|
int MWMechanics::NpcStats::getBounty() const
|
|
|
|
{
|
2014-12-18 17:08:51 +00:00
|
|
|
return mBounty;
|
2012-11-09 13:31:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MWMechanics::NpcStats::setBounty (int bounty)
|
|
|
|
{
|
2014-12-18 17:08:51 +00:00
|
|
|
mBounty = bounty;
|
2012-11-09 13:31:38 +00:00
|
|
|
}
|
2012-11-10 12:20:41 +00:00
|
|
|
|
2012-11-09 23:29:36 +00:00
|
|
|
int MWMechanics::NpcStats::getReputation() const
|
|
|
|
{
|
|
|
|
return mReputation;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MWMechanics::NpcStats::setReputation(int reputation)
|
|
|
|
{
|
2019-12-11 04:47:46 +00:00
|
|
|
// Reputation is capped in original engine
|
|
|
|
mReputation = std::min(255, std::max(0, reputation));
|
2012-11-09 23:29:36 +00:00
|
|
|
}
|
2012-11-10 14:44:44 +00:00
|
|
|
|
2014-04-05 14:26:14 +00:00
|
|
|
int MWMechanics::NpcStats::getCrimeId() const
|
|
|
|
{
|
|
|
|
return mCrimeId;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MWMechanics::NpcStats::setCrimeId(int id)
|
|
|
|
{
|
|
|
|
mCrimeId = id;
|
|
|
|
}
|
|
|
|
|
2012-11-12 12:23:25 +00:00
|
|
|
bool MWMechanics::NpcStats::hasSkillsForRank (const std::string& factionId, int rank) const
|
|
|
|
{
|
|
|
|
if (rank<0 || rank>=10)
|
|
|
|
throw std::runtime_error ("rank index out of range");
|
|
|
|
|
|
|
|
const ESM::Faction& faction =
|
|
|
|
*MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find (factionId);
|
|
|
|
|
|
|
|
std::vector<int> skills;
|
2012-11-15 19:00:27 +00:00
|
|
|
|
2014-06-15 21:05:38 +00:00
|
|
|
for (int i=0; i<7; ++i)
|
|
|
|
{
|
|
|
|
if (faction.mData.mSkills[i] != -1)
|
2017-09-07 17:06:10 +00:00
|
|
|
skills.push_back (static_cast<int> (getSkill (faction.mData.mSkills[i]).getBase()));
|
2014-06-15 21:05:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (skills.empty())
|
|
|
|
return true;
|
2012-11-15 19:00:27 +00:00
|
|
|
|
2012-11-12 12:23:25 +00:00
|
|
|
std::sort (skills.begin(), skills.end());
|
2012-11-15 19:00:27 +00:00
|
|
|
|
2012-11-12 12:23:25 +00:00
|
|
|
std::vector<int>::const_reverse_iterator iter = skills.rbegin();
|
2012-11-15 19:00:27 +00:00
|
|
|
|
2012-11-12 12:23:25 +00:00
|
|
|
const ESM::RankData& rankData = faction.mData.mRankData[rank];
|
2012-11-15 19:00:27 +00:00
|
|
|
|
2020-06-26 07:47:59 +00:00
|
|
|
if (*iter<rankData.mPrimarySkill)
|
2012-11-12 12:23:25 +00:00
|
|
|
return false;
|
2012-11-15 19:00:27 +00:00
|
|
|
|
2014-06-15 21:05:38 +00:00
|
|
|
if (skills.size() < 2)
|
|
|
|
return true;
|
|
|
|
|
2020-06-26 07:14:38 +00:00
|
|
|
iter++;
|
2020-06-26 07:47:59 +00:00
|
|
|
if (*iter<rankData.mFavouredSkill)
|
2020-06-26 07:14:38 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
if (skills.size() < 3)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
iter++;
|
2020-06-26 07:47:59 +00:00
|
|
|
if (*iter<rankData.mFavouredSkill)
|
2020-06-26 07:14:38 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
2012-11-12 12:23:25 +00:00
|
|
|
}
|
|
|
|
|
2012-11-15 19:00:27 +00:00
|
|
|
bool MWMechanics::NpcStats::isWerewolf() const
|
|
|
|
{
|
2013-08-09 12:14:58 +00:00
|
|
|
return mIsWerewolf;
|
2012-11-15 19:00:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MWMechanics::NpcStats::setWerewolf (bool set)
|
|
|
|
{
|
2015-06-21 15:27:52 +00:00
|
|
|
if (mIsWerewolf == set)
|
|
|
|
return;
|
|
|
|
|
2013-08-09 12:14:58 +00:00
|
|
|
if(set != false)
|
|
|
|
{
|
2014-06-17 14:51:59 +00:00
|
|
|
mWerewolfKills = 0;
|
2013-08-09 12:14:58 +00:00
|
|
|
}
|
|
|
|
mIsWerewolf = set;
|
2012-11-15 19:00:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int MWMechanics::NpcStats::getWerewolfKills() const
|
|
|
|
{
|
|
|
|
return mWerewolfKills;
|
2012-11-28 01:15:34 +00:00
|
|
|
}
|
2013-03-31 11:13:46 +00:00
|
|
|
|
2014-06-17 14:51:59 +00:00
|
|
|
void MWMechanics::NpcStats::addWerewolfKill()
|
|
|
|
{
|
|
|
|
++mWerewolfKills;
|
|
|
|
}
|
|
|
|
|
2013-08-28 00:13:49 +00:00
|
|
|
float MWMechanics::NpcStats::getTimeToStartDrowning() const
|
2013-08-07 13:34:11 +00:00
|
|
|
{
|
|
|
|
return mTimeToStartDrowning;
|
|
|
|
}
|
2014-05-11 19:03:27 +00:00
|
|
|
|
2013-08-07 13:34:11 +00:00
|
|
|
void MWMechanics::NpcStats::setTimeToStartDrowning(float time)
|
|
|
|
{
|
|
|
|
mTimeToStartDrowning=time;
|
|
|
|
}
|
2014-02-16 14:06:34 +00:00
|
|
|
|
2020-03-17 10:15:19 +00:00
|
|
|
void MWMechanics::NpcStats::writeState (ESM::CreatureStats& state) const
|
|
|
|
{
|
|
|
|
CreatureStats::writeState(state);
|
|
|
|
}
|
|
|
|
|
2014-02-16 14:06:34 +00:00
|
|
|
void MWMechanics::NpcStats::writeState (ESM::NpcStats& state) const
|
|
|
|
{
|
|
|
|
for (std::map<std::string, int>::const_iterator iter (mFactionRank.begin());
|
|
|
|
iter!=mFactionRank.end(); ++iter)
|
|
|
|
state.mFactions[iter->first].mRank = iter->second;
|
|
|
|
|
|
|
|
state.mDisposition = mDisposition;
|
|
|
|
|
2014-05-12 19:04:02 +00:00
|
|
|
for (int i=0; i<ESM::Skill::Length; ++i)
|
2015-06-21 16:18:24 +00:00
|
|
|
mSkill[i].writeState (state.mSkills[i]);
|
|
|
|
|
2014-05-12 19:04:02 +00:00
|
|
|
state.mIsWerewolf = mIsWerewolf;
|
2014-02-16 14:06:34 +00:00
|
|
|
|
2014-04-14 22:11:04 +00:00
|
|
|
state.mCrimeId = mCrimeId;
|
|
|
|
|
2014-02-16 14:06:34 +00:00
|
|
|
state.mBounty = mBounty;
|
|
|
|
|
|
|
|
for (std::set<std::string>::const_iterator iter (mExpelled.begin());
|
|
|
|
iter!=mExpelled.end(); ++iter)
|
|
|
|
state.mFactions[*iter].mExpelled = true;
|
|
|
|
|
|
|
|
for (std::map<std::string, int>::const_iterator iter (mFactionReputation.begin());
|
|
|
|
iter!=mFactionReputation.end(); ++iter)
|
|
|
|
state.mFactions[iter->first].mReputation = iter->second;
|
|
|
|
|
|
|
|
state.mReputation = mReputation;
|
|
|
|
state.mWerewolfKills = mWerewolfKills;
|
|
|
|
state.mLevelProgress = mLevelProgress;
|
|
|
|
|
2014-05-12 19:04:02 +00:00
|
|
|
for (int i=0; i<ESM::Attribute::Length; ++i)
|
2014-02-16 14:56:36 +00:00
|
|
|
state.mSkillIncrease[i] = mSkillIncreases[i];
|
|
|
|
|
2016-06-26 01:22:58 +00:00
|
|
|
for (int i=0; i<3; ++i)
|
|
|
|
state.mSpecIncreases[i] = mSpecIncreases[i];
|
|
|
|
|
2014-02-16 14:06:34 +00:00
|
|
|
std::copy (mUsedIds.begin(), mUsedIds.end(), std::back_inserter (state.mUsedIds));
|
|
|
|
|
|
|
|
state.mTimeToStartDrowning = mTimeToStartDrowning;
|
|
|
|
}
|
2020-03-17 10:15:19 +00:00
|
|
|
void MWMechanics::NpcStats::readState (const ESM::CreatureStats& state)
|
|
|
|
{
|
|
|
|
CreatureStats::readState(state);
|
|
|
|
}
|
2014-02-16 14:06:34 +00:00
|
|
|
|
|
|
|
void MWMechanics::NpcStats::readState (const ESM::NpcStats& state)
|
|
|
|
{
|
2014-02-16 15:22:09 +00:00
|
|
|
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
|
|
|
|
|
2014-02-16 14:06:34 +00:00
|
|
|
for (std::map<std::string, ESM::NpcStats::Faction>::const_iterator iter (state.mFactions.begin());
|
|
|
|
iter!=state.mFactions.end(); ++iter)
|
2014-02-16 15:22:09 +00:00
|
|
|
if (store.get<ESM::Faction>().search (iter->first))
|
|
|
|
{
|
|
|
|
if (iter->second.mExpelled)
|
|
|
|
mExpelled.insert (iter->first);
|
2014-02-16 14:06:34 +00:00
|
|
|
|
2014-04-26 09:41:44 +00:00
|
|
|
if (iter->second.mRank >= 0)
|
2014-05-26 10:31:08 +00:00
|
|
|
mFactionRank[iter->first] = iter->second.mRank;
|
2014-02-16 14:06:34 +00:00
|
|
|
|
2014-02-16 15:22:09 +00:00
|
|
|
if (iter->second.mReputation)
|
2015-02-08 20:26:58 +00:00
|
|
|
mFactionReputation[Misc::StringUtils::lowerCase(iter->first)] = iter->second.mReputation;
|
2014-02-16 15:22:09 +00:00
|
|
|
}
|
2014-02-16 14:06:34 +00:00
|
|
|
|
|
|
|
mDisposition = state.mDisposition;
|
|
|
|
|
2014-05-12 19:04:02 +00:00
|
|
|
for (int i=0; i<ESM::Skill::Length; ++i)
|
2015-06-21 16:18:24 +00:00
|
|
|
mSkill[i].readState (state.mSkills[i]);
|
2014-05-12 19:04:02 +00:00
|
|
|
|
|
|
|
mIsWerewolf = state.mIsWerewolf;
|
2014-02-16 14:06:34 +00:00
|
|
|
|
2014-04-14 22:11:04 +00:00
|
|
|
mCrimeId = state.mCrimeId;
|
2014-02-16 14:06:34 +00:00
|
|
|
mBounty = state.mBounty;
|
|
|
|
mReputation = state.mReputation;
|
|
|
|
mWerewolfKills = state.mWerewolfKills;
|
|
|
|
mLevelProgress = state.mLevelProgress;
|
|
|
|
|
2014-05-12 19:04:02 +00:00
|
|
|
for (int i=0; i<ESM::Attribute::Length; ++i)
|
2014-02-16 14:56:36 +00:00
|
|
|
mSkillIncreases[i] = state.mSkillIncrease[i];
|
|
|
|
|
2016-06-26 01:22:58 +00:00
|
|
|
for (int i=0; i<3; ++i)
|
|
|
|
mSpecIncreases[i] = state.mSpecIncreases[i];
|
|
|
|
|
2014-02-16 15:22:09 +00:00
|
|
|
for (std::vector<std::string>::const_iterator iter (state.mUsedIds.begin());
|
|
|
|
iter!=state.mUsedIds.end(); ++iter)
|
|
|
|
if (store.find (*iter))
|
|
|
|
mUsedIds.insert (*iter);
|
2014-02-16 14:06:34 +00:00
|
|
|
|
|
|
|
mTimeToStartDrowning = state.mTimeToStartDrowning;
|
2014-02-19 17:40:29 +00:00
|
|
|
}
|