You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openmw-tes3mp/apps/openmw/mwmechanics/npcstats.cpp

262 lines
6.6 KiB
C++

#include "npcstats.hpp"
#include <cmath>
#include <stdexcept>
#include <boost/format.hpp>
#include <components/esm/loadskil.hpp>
#include <components/esm/loadclas.hpp>
#include <components/esm/loadgmst.hpp>
#include "../mwworld/esmstore.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
12 years ago
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/soundmanager.hpp"
MWMechanics::NpcStats::NpcStats()
: mMovementFlags (0), mDrawState (DrawState_Nothing), mBounty (0)
, mLevelProgress(0), mDisposition(0)
12 years ago
{
mSkillIncreases.resize (ESM::Attribute::Length);
for (int i=0; i<ESM::Attribute::Length; ++i)
mSkillIncreases[i] = 0;
}
MWMechanics::DrawState_ MWMechanics::NpcStats::getDrawState() const
{
return mDrawState;
}
void MWMechanics::NpcStats::setDrawState (DrawState_ state)
{
mDrawState = state;
}
int MWMechanics::NpcStats::getDisposition() const
{
return mDisposition;
}
void MWMechanics::NpcStats::setDisposition(int disposition)
{
mDisposition = disposition;
}
bool MWMechanics::NpcStats::getMovementFlag (Flag flag) const
{
return mMovementFlags & flag;
}
void MWMechanics::NpcStats::setMovementFlag (Flag flag, bool state)
{
if (state)
mMovementFlags |= flag;
else
mMovementFlags &= ~flag;
}
const MWMechanics::Stat<float>& MWMechanics::NpcStats::getSkill (int index) const
{
if (index<0 || index>=27)
throw std::runtime_error ("skill index out of range");
return mSkill[index];
}
MWMechanics::Stat<float>& MWMechanics::NpcStats::getSkill (int index)
{
if (index<0 || index>=27)
throw std::runtime_error ("skill index out of range");
return mSkill[index];
}
std::map<std::string, int>& MWMechanics::NpcStats::getFactionRanks()
{
return mFactionRank;
}
const std::map<std::string, int>& MWMechanics::NpcStats::getFactionRanks() const
{
return mFactionRank;
}
float MWMechanics::NpcStats::getSkillGain (int skillIndex, const ESM::Class& class_, int usageType,
int level) const
{
if (level<0)
level = static_cast<int> (getSkill (skillIndex).getBase());
const ESM::Skill *skill =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Skill>().find (skillIndex);
float skillFactor = 1;
if (usageType>=4)
throw std::runtime_error ("skill usage type out of range");
if (usageType>0)
{
skillFactor = skill->mData.mUseValue[usageType];
if (skillFactor<=0)
throw std::runtime_error ("invalid skill gain factor");
}
const MWWorld::Store<ESM::GameSetting> &gmst =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
float typeFactor = gmst.find ("fMiscSkillBonus")->getFloat();
for (int i=0; i<5; ++i)
if (class_.mData.mSkills[i][0]==skillIndex)
{
typeFactor = gmst.find ("fMinorSkillBonus")->getFloat();
break;
}
for (int i=0; i<5; ++i)
if (class_.mData.mSkills[i][1]==skillIndex)
{
typeFactor = gmst.find ("fMajorSkillBonus")->getFloat();
break;
}
if (typeFactor<=0)
throw std::runtime_error ("invalid skill type factor");
float specialisationFactor = 1;
if (skill->mData.mSpecialization==class_.mData.mSpecialization)
{
specialisationFactor = gmst.find ("fSpecialSkillBonus")->getFloat();
if (specialisationFactor<=0)
throw std::runtime_error ("invalid skill specialisation factor");
}
return 1.0 / (level +1) * (1.0 / (skillFactor)) * typeFactor * specialisationFactor;
}
void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_, int usageType)
{
float base = getSkill (skillIndex).getBase();
int level = static_cast<int> (base);
base += getSkillGain (skillIndex, class_, usageType);
if (static_cast<int> (base)!=level)
12 years ago
{
// skill leveled up
increaseSkill(skillIndex, class_, false);
}
else
getSkill (skillIndex).setBase (base);
}
void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &class_, bool preserveProgress)
{
float base = getSkill (skillIndex).getBase();
int level = static_cast<int> (base);
12 years ago
if (level >= 100)
return;
if (preserveProgress)
base += 1;
else
base = level+1;
// if this is a major or minor skill of the class, increase level progress
bool levelProgress = false;
for (int i=0; i<2; ++i)
for (int j=0; j<5; ++j)
12 years ago
{
int skill = class_.mData.mSkills[j][i];
if (skill == skillIndex)
levelProgress = true;
12 years ago
}
mLevelProgress += levelProgress;
// check the attribute this skill belongs to
const ESM::Skill* skill =
MWBase::Environment::get().getWorld ()->getStore ().get<ESM::Skill>().find(skillIndex);
++mSkillIncreases[skill->mData.mAttribute];
// Play sound & skill progress notification
/// \todo check if character is the player, if levelling is ever implemented for NPCs
MWBase::Environment::get().getSoundManager ()->playSound ("skillraise", 1, 1);
std::stringstream message;
message << boost::format(MWBase::Environment::get().getWindowManager ()->getGameSettingString ("sNotifyMessage39", ""))
% std::string("#{" + ESM::Skill::sSkillNameIds[skillIndex] + "}")
% base;
MWBase::Environment::get().getWindowManager ()->messageBox(message.str(), std::vector<std::string>());
if (mLevelProgress >= 10)
{
// levelup is possible now
MWBase::Environment::get().getWindowManager ()->messageBox ("#{sLevelUpMsg}", std::vector<std::string>());
}
12 years ago
getSkill (skillIndex).setBase (base);
}
12 years ago
int MWMechanics::NpcStats::getLevelProgress () const
{
return mLevelProgress;
}
void MWMechanics::NpcStats::levelUp()
{
mLevelProgress -= 10;
for (int i=0; i<ESM::Attribute::Length; ++i)
mSkillIncreases[i] = 0;
}
int MWMechanics::NpcStats::getLevelupAttributeMultiplier(int attribute) const
{
// Source: http://www.uesp.net/wiki/Morrowind:Level#How_to_Level_Up
int num = mSkillIncreases[attribute];
if (num <= 1)
return 1;
else if (num <= 4)
return 2;
else if (num <= 7)
return 3;
else if (num <= 9)
return 4;
else
return 5;
}
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();
}
int MWMechanics::NpcStats::getBounty() const
{
return mBounty;
}
void MWMechanics::NpcStats::setBounty (int bounty)
{
mBounty = bounty;
}