forked from teamnwah/openmw-tes3coop
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>
This commit is contained in:
parent
8d63f8eea2
commit
594cc693b2
5 changed files with 106 additions and 17 deletions
|
@ -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…
Reference in a new issue