Fixes #1006: Many NPCs have 0 skill

Added calculation of skill values for NPC with mNpdtType
set to NPC_WITH_AUTOCALCULATED_STATS (their NPDT is 12).

Signed-off-by: Lukasz Gromanowski <lgromanowski@gmail.com>
actorid
Lukasz Gromanowski 11 years ago
parent 8d63f8eea2
commit 594cc693b2

@ -989,8 +989,7 @@ void Record<ESM::NPC>::print()
std::cout << " Faction: " << mData.mFaction << std::endl;
std::cout << " Flags: " << npcFlags(mData.mFlags) << std::endl;
// Seriously?
if (mData.mNpdt52.mGold == -10)
if (mData.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
{
std::cout << " Level: " << mData.mNpdt12.mLevel << std::endl;
std::cout << " Reputation: " << (int)mData.mNpdt12.mReputation << std::endl;
@ -1022,7 +1021,7 @@ void Record<ESM::NPC>::print()
std::cout << " Luck: " << (int)mData.mNpdt52.mLuck << std::endl;
std::cout << " Skills:" << std::endl;
for (int i = 0; i != 27; i++)
for (int i = 0; i != ESM::Skill::Length; i++)
std::cout << " " << skillLabel(i) << ": "
<< (int)((unsigned char)mData.mNpdt52.mSkills[i]) << std::endl;

@ -63,7 +63,6 @@ namespace
bool male = (npc->mFlags & ESM::NPC::Female) == 0;
int level = creatureStats.getLevel();
for (int i=0; i<ESM::Attribute::Length; ++i)
{
const ESM::Race::MaleFemale& attribute = race->mData.mAttributeValues[i];
@ -85,7 +84,7 @@ namespace
}
// skill bonus
for (int attribute=0; attribute<ESM::Attribute::Length; ++attribute)
for (int attribute=0; attribute < ESM::Attribute::Length; ++attribute)
{
float modifierSum = 0;
@ -131,6 +130,89 @@ namespace
creatureStats.setHealth(static_cast<int> (0.5 * (strength + endurance)) + multiplier * (creatureStats.getLevel() - 1));
}
/**
* @brief autoCalculateSkills
*
* Skills are calculated with following formulae ( http://www.uesp.net/wiki/Morrowind:NPCs#Skills ):
*
* Skills: (Level - 1) × (Majority Multiplier + Specialization Multiplier)
*
* The Majority Multiplier is 1.0 for a Major or Minor Skill, or 0.1 for a Miscellaneous Skill.
*
* The Specialization Multiplier is 0.5 for a Skill in the same Specialization as the class,
* zero for other Skills.
*
* and by adding class, race, specialization bonus.
*/
void autoCalculateSkills(const ESM::NPC* npc, MWMechanics::NpcStats& npcStats)
{
const ESM::Class *class_ =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Class>().find(npc->mClass);
unsigned int level = npcStats.getLevel();
const ESM::Race *race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(npc->mRace);
for (int i = 0; i < 2; ++i)
{
int bonus = (i==0) ? 10 : 25;
for (int i2 = 0; i2 < 5; ++i2)
{
int index = class_->mData.mSkills[i2][i];
if (index >= 0 && index < ESM::Skill::Length)
{
npcStats.getSkill(index).setBase (npcStats.getSkill(index).getBase() + bonus);
}
}
}
for (int skillIndex = 0; skillIndex < ESM::Skill::Length; ++skillIndex)
{
float majorMultiplier = 0.1f;
float specMultiplier = 0.0f;
int raceBonus = 0;
int specBonus = 0;
for (int raceSkillIndex = 0; raceSkillIndex < 7; ++raceSkillIndex)
{
if (race->mData.mBonus[raceSkillIndex].mSkill == skillIndex)
{
raceBonus = race->mData.mBonus[raceSkillIndex].mBonus;
break;
}
}
for (int k = 0; k < 5; ++k)
{
// is this a minor or major skill?
if ((class_->mData.mSkills[k][0] == skillIndex) || (class_->mData.mSkills[k][1] == skillIndex))
{
majorMultiplier = 1.0f;
break;
}
}
// is this skill in the same Specialization as the class?
const ESM::Skill* skill = MWBase::Environment::get().getWorld()->getStore().get<ESM::Skill>().find(skillIndex);
if (skill->mData.mSpecialization == class_->mData.mSpecialization)
{
specMultiplier = 0.5f;
specBonus = 5;
}
npcStats.getSkill(skillIndex).setBase(
std::min(
npcStats.getSkill(skillIndex).getBase()
+ 5
+ raceBonus
+ specBonus
+ static_cast<int>((level-1) * (majorMultiplier + specMultiplier)), 100.0f));
}
}
}
namespace MWClass
@ -173,7 +255,7 @@ namespace MWClass
{
std::string faction = ref->mBase->mFaction;
Misc::StringUtils::toLower(faction);
if(ref->mBase->mNpdt52.mGold != -10)
if(ref->mBase->mNpdtType != ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
{
data->mNpcStats.getFactionRanks()[faction] = (int)ref->mBase->mNpdt52.mRank;
}
@ -185,11 +267,11 @@ namespace MWClass
// creature stats
int gold=0;
if(ref->mBase->mNpdt52.mGold != -10)
if(ref->mBase->mNpdtType != ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
{
gold = ref->mBase->mNpdt52.mGold;
for (int i=0; i<27; ++i)
for (unsigned int i=0; i< ESM::Skill::Length; ++i)
data->mNpcStats.getSkill (i).setBase (ref->mBase->mNpdt52.mSkills[i]);
data->mNpcStats.getAttribute(0).set (ref->mBase->mNpdt52.mStrength);
@ -220,6 +302,7 @@ namespace MWClass
data->mNpcStats.setReputation(ref->mBase->mNpdt12.mReputation);
autoCalculateAttributes(ref->mBase, data->mNpcStats);
autoCalculateSkills(ref->mBase, data->mNpcStats);
}
data->mNpcStats.getAiSequence().fill(ref->mBase->mAiPackage);

@ -86,7 +86,7 @@ void MWMechanics::NpcStats::setMovementFlag (Flag flag, bool state)
const MWMechanics::Stat<float>& MWMechanics::NpcStats::getSkill (int index) const
{
if (index<0 || index>=27)
if (index<0 || index>=ESM::Skill::Length)
throw std::runtime_error ("skill index out of range");
return (!mIsWerewolf ? mSkill[index] : mWerewolfSkill[index]);
@ -94,7 +94,7 @@ const MWMechanics::Stat<float>& MWMechanics::NpcStats::getSkill (int index) cons
MWMechanics::Stat<float>& MWMechanics::NpcStats::getSkill (int index)
{
if (index<0 || index>=27)
if (index<0 || index>=ESM::Skill::Length)
throw std::runtime_error ("skill index out of range");
return (!mIsWerewolf ? mSkill[index] : mWerewolfSkill[index]);

@ -10,7 +10,7 @@ namespace ESM
void NPC::load(ESMReader &esm)
{
mNpdt52.mGold = -10;
//mNpdt52.mGold = -10;
mPersistent = esm.getRecordFlags() & 0x0400;
@ -29,12 +29,12 @@ void NPC::load(ESMReader &esm)
esm.getSubHeader();
if (esm.getSubSize() == 52)
{
mNpdtType = 52;
mNpdtType = NPC_DEFAULT;
esm.getExact(&mNpdt52, 52);
}
else if (esm.getSubSize() == 12)
{
mNpdtType = 12;
mNpdtType = NPC_WITH_AUTOCALCULATED_STATS;
esm.getExact(&mNpdt12, 12);
}
else
@ -76,9 +76,9 @@ void NPC::save(ESMWriter &esm) const
esm.writeHNCString("KNAM", mHair);
esm.writeHNOCString("SCRI", mScript);
if (mNpdtType == 52)
if (mNpdtType == NPC_DEFAULT)
esm.writeHNT("NPDT", mNpdt52, 52);
else if (mNpdtType == 12)
else if (mNpdtType == NPC_WITH_AUTOCALCULATED_STATS)
esm.writeHNT("NPDT", mNpdt12, 12);
esm.writeHNT("FLAG", mFlags);
@ -114,7 +114,7 @@ void NPC::save(ESMWriter &esm) const
mNpdt52.mLevel = 0;
mNpdt52.mStrength = mNpdt52.mIntelligence = mNpdt52.mWillpower = mNpdt52.mAgility =
mNpdt52.mSpeed = mNpdt52.mEndurance = mNpdt52.mPersonality = mNpdt52.mLuck = 0;
for (int i=0; i<27; ++i) mNpdt52.mSkills[i] = 0;
for (int i=0; i< Skill::Length; ++i) mNpdt52.mSkills[i] = 0;
mNpdt52.mReputation = 0;
mNpdt52.mHealth = mNpdt52.mMana = mNpdt52.mFatigue = 0;
mNpdt52.mDisposition = 0;

@ -8,6 +8,7 @@
#include "loadcont.hpp"
#include "aipackage.hpp"
#include "spelllist.hpp"
#include "loadskil.hpp"
namespace ESM {
@ -58,6 +59,12 @@ struct NPC
Metal = 0x0800 // Metal blood effect (golden?)
};
enum NpcType
{
NPC_WITH_AUTOCALCULATED_STATS = 12,
NPC_DEFAULT = 52
};
#pragma pack(push)
#pragma pack(1)
@ -73,7 +80,7 @@ struct NPC
mPersonality,
mLuck;
char mSkills[27];
char mSkills[Skill::Length];
char mReputation;
short mHealth, mMana, mFatigue;
char mDisposition, mFactionID, mRank;

Loading…
Cancel
Save