Savegame: store most of CreatureStats

pull/98/head
scrawl 11 years ago
parent 117b812fb1
commit 6cc691115b

@ -357,9 +357,6 @@ namespace MWClass
data->mInventoryStore.fill(ref->mBase->mInventory, getId(ptr), "",
MWBase::Environment::get().getWorld()->getStore());
// Relates to NPC gold reset delay
data->mNpcStats.setTradeTime(MWWorld::TimeStamp(0.0, 0));
data->mNpcStats.setGoldPool(gold);
// store

@ -1326,9 +1326,6 @@ namespace MWGui
void WindowManager::updatePlayer()
{
unsetSelectedSpell();
unsetSelectedWeapon();
mInventoryWindow->updatePlayer();
}
@ -1425,6 +1422,14 @@ namespace MWGui
mQuickKeysMenu->write(writer);
progress.increaseProgress();
if (!mSelectedSpell.empty())
{
writer.startRecord(ESM::REC_ASPL);
writer.writeHNString("ID__", mSelectedSpell);
writer.endRecord(ESM::REC_ASPL);
progress.increaseProgress();
}
}
void WindowManager::readRecord(ESM::ESMReader &reader, int32_t type)
@ -1433,12 +1438,18 @@ namespace MWGui
mMap->readRecord(reader, type);
else if (type == ESM::REC_KEYS)
mQuickKeysMenu->readRecord(reader, type);
else if (type == ESM::REC_ASPL)
{
reader.getSubNameIs("ID__");
mSelectedSpell = reader.getHString();
}
}
int WindowManager::countSavedGameRecords() const
{
return 1 // Global map
+ 1; // QuickKeysMenu
+ 1 // QuickKeysMenu
+ (!mSelectedSpell.empty() ? 1 : 0);
}
bool WindowManager::isSavingAllowed() const

@ -17,8 +17,10 @@ namespace MWMechanics
mAttacked (false), mHostile (false),
mAttackingOrSpell(false),
mIsWerewolf(false),
mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false), mHitRecovery(false), mBlock(false),
mMovementFlags(0), mDrawState (DrawState_Nothing), mAttackStrength(0.f)
mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mKnockdownOneFrame(false),
mKnockdownOverOneFrame(false), mHitRecovery(false), mBlock(false),
mMovementFlags(0), mDrawState (DrawState_Nothing), mAttackStrength(0.f),
mTradeTime(0,0), mGoldPool(0)
{
for (int i=0; i<4; ++i)
mAiSettings[i] = 0;
@ -348,20 +350,6 @@ namespace MWMechanics
return mLastHitObject;
}
bool CreatureStats::canUsePower(const std::string &power) const
{
std::map<std::string, MWWorld::TimeStamp>::const_iterator it = mUsedPowers.find(power);
if (it == mUsedPowers.end() || it->second + 24 <= MWBase::Environment::get().getWorld()->getTimeStamp())
return true;
else
return false;
}
void CreatureStats::usePower(const std::string &power)
{
mUsedPowers[power] = MWBase::Environment::get().getWorld()->getTimeStamp();
}
void CreatureStats::addToFallHeight(float height)
{
mFallHeight += height;
@ -481,20 +469,75 @@ namespace MWMechanics
void CreatureStats::writeState (ESM::CreatureStats& state) const
{
for (int i=0; i<8; ++i)
for (int i=0; i<ESM::Attribute::Length; ++i)
mAttributes[i].writeState (state.mAttributes[i]);
for (int i=0; i<3; ++i)
mDynamic[i].writeState (state.mDynamic[i]);
state.mTradeTime = mTradeTime.toEsm();
state.mGoldPool = mGoldPool;
state.mDead = mDead;
state.mDied = mDied;
state.mFriendlyHits = mFriendlyHits;
state.mTalkedTo = mTalkedTo;
state.mAlarmed = mAlarmed;
state.mAttacked = mAttacked;
state.mHostile = mHostile;
state.mAttackingOrSpell = mAttackingOrSpell;
// TODO: rewrite. does this really need 3 separate bools?
state.mKnockdown = mKnockdown;
state.mKnockdownOneFrame = mKnockdownOneFrame;
state.mKnockdownOverOneFrame = mKnockdownOverOneFrame;
state.mHitRecovery = mHitRecovery;
state.mBlock = mBlock;
state.mMovementFlags = mMovementFlags;
state.mAttackStrength = mAttackStrength;
state.mFallHeight = mFallHeight;
state.mLastHitObject = mLastHitObject;
state.mRecalcDynamicStats = mRecalcDynamicStats;
state.mDrawState = mDrawState;
state.mLevel = mLevel;
mSpells.writeState(state.mSpells);
}
void CreatureStats::readState (const ESM::CreatureStats& state)
{
for (int i=0; i<8; ++i)
for (int i=0; i<ESM::Attribute::Length; ++i)
mAttributes[i].readState (state.mAttributes[i]);
for (int i=0; i<3; ++i)
mDynamic[i].readState (state.mDynamic[i]);
mTradeTime = MWWorld::TimeStamp(state.mTradeTime);
mGoldPool = state.mGoldPool;
mFallHeight = state.mFallHeight;
mDead = state.mDead;
mDied = state.mDied;
mFriendlyHits = state.mFriendlyHits;
mTalkedTo = state.mTalkedTo;
mAlarmed = state.mAlarmed;
mAttacked = state.mAttacked;
mHostile = state.mHostile;
mAttackingOrSpell = state.mAttackingOrSpell;
// TODO: rewrite. does this really need 3 separate bools?
mKnockdown = state.mKnockdown;
mKnockdownOneFrame = state.mKnockdownOneFrame;
mKnockdownOverOneFrame = state.mKnockdownOverOneFrame;
mHitRecovery = state.mHitRecovery;
mBlock = state.mBlock;
mMovementFlags = state.mMovementFlags;
mAttackStrength = state.mAttackStrength;
mFallHeight = state.mFallHeight;
mLastHitObject = state.mLastHitObject;
mRecalcDynamicStats = state.mRecalcDynamicStats;
mDrawState = DrawState_(state.mDrawState);
mLevel = state.mLevel;
mSpells.readState(state.mSpells);
}
// Relates to NPC gold reset delay

@ -55,15 +55,15 @@ namespace MWMechanics
// Do we need to recalculate stats derived from attributes or other factors?
bool mRecalcDynamicStats;
std::map<std::string, MWWorld::TimeStamp> mUsedPowers;
MWWorld::TimeStamp mTradeTime; // Relates to NPC gold reset delay
int mGoldPool; // the pool of merchant gold not in inventory
protected:
// These two are only set by NpcStats, but they are declared in CreatureStats to prevent using virtual methods.
bool mIsWerewolf;
AttributeValue mWerewolfAttributes[8];
int mLevel;
public:
@ -84,9 +84,6 @@ namespace MWMechanics
/// @return total fall height
float land();
bool canUsePower (const std::string& power) const;
void usePower (const std::string& power);
const AttributeValue & getAttribute(int index) const;
const DynamicStat<float> & getHealth() const;

@ -6,9 +6,9 @@ namespace MWMechanics
/// \note The _ suffix is required to avoid a collision with a Windoze macro. Die, Microsoft! Die!
enum DrawState_
{
DrawState_Weapon = 0,
DrawState_Spell = 1,
DrawState_Nothing = 2
DrawState_Nothing = 0,
DrawState_Weapon = 1,
DrawState_Spell = 2
};
}

@ -342,7 +342,9 @@ namespace MWMechanics
MWWorld::ContainerStoreIterator enchantItem = inv.getSelectedEnchantItem();
if (enchantItem != inv.end())
winMgr->setSelectedEnchantItem(*enchantItem);
else if (winMgr->getSelectedSpell() == "")
else if (!winMgr->getSelectedSpell().empty())
winMgr->setSelectedSpell(winMgr->getSelectedSpell(), int(MWMechanics::getSpellSuccessChance(winMgr->getSelectedSpell(), mWatched)));
else
winMgr->unsetSelectedSpell();
}

@ -446,11 +446,16 @@ void MWMechanics::NpcStats::writeState (ESM::NpcStats& state) const
state.mDisposition = mDisposition;
for (int i=0; i<27; ++i)
for (int i=0; i<ESM::Skill::Length; ++i)
{
mSkill[i].writeState (state.mSkills[i].mRegular);
mWerewolfSkill[i].writeState (state.mSkills[i].mWerewolf);
}
for (int i=0; i<ESM::Attribute::Length; ++i)
{
mWerewolfAttributes[i].writeState (state.mWerewolfAttributes[i]);
}
state.mIsWerewolf = mIsWerewolf;
state.mCrimeId = mCrimeId;
@ -467,10 +472,9 @@ void MWMechanics::NpcStats::writeState (ESM::NpcStats& state) const
state.mReputation = mReputation;
state.mWerewolfKills = mWerewolfKills;
state.mProfit = mProfit;
state.mAttackStrength = mAttackStrength;
state.mLevelProgress = mLevelProgress;
for (int i=0; i<8; ++i)
for (int i=0; i<ESM::Attribute::Length; ++i)
state.mSkillIncrease[i] = mSkillIncreases[i];
std::copy (mUsedIds.begin(), mUsedIds.end(), std::back_inserter (state.mUsedIds));
@ -500,21 +504,26 @@ void MWMechanics::NpcStats::readState (const ESM::NpcStats& state)
mDisposition = state.mDisposition;
for (int i=0; i<27; ++i)
for (int i=0; i<ESM::Skill::Length; ++i)
{
mSkill[i].readState (state.mSkills[i].mRegular);
mWerewolfSkill[i].readState (state.mSkills[i].mWerewolf);
}
for (int i=0; i<ESM::Attribute::Length; ++i)
{
mWerewolfAttributes[i].readState (state.mWerewolfAttributes[i]);
}
mIsWerewolf = state.mIsWerewolf;
mCrimeId = state.mCrimeId;
mBounty = state.mBounty;
mReputation = state.mReputation;
mWerewolfKills = state.mWerewolfKills;
mProfit = state.mProfit;
mAttackStrength = state.mAttackStrength;
mLevelProgress = state.mLevelProgress;
for (int i=0; i<8; ++i)
for (int i=0; i<ESM::Attribute::Length; ++i)
mSkillIncreases[i] = state.mSkillIncrease[i];
for (std::vector<std::string>::const_iterator iter (state.mUsedIds.begin());

@ -31,8 +31,8 @@ namespace MWMechanics
std::map<std::string, int> mFactionRank;
int mDisposition;
SkillValue mSkill[27];
SkillValue mWerewolfSkill[27];
SkillValue mSkill[ESM::Skill::Length];
SkillValue mWerewolfSkill[ESM::Skill::Length];
int mBounty;
std::set<std::string> mExpelled;
std::map<std::string, int> mFactionReputation;
@ -40,7 +40,6 @@ namespace MWMechanics
int mCrimeId;
int mWerewolfKills;
int mProfit;
float mAttackStrength;
int mLevelProgress; // 0-10

@ -4,6 +4,7 @@
#include <cstdlib>
#include <components/esm/loadspel.hpp>
#include <components/esm/spellstate.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -30,10 +31,19 @@ namespace MWMechanics
{
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId);
std::vector<float> random;
random.resize(spell->mEffects.mList.size());
for (unsigned int i=0; i<random.size();++i)
random[i] = static_cast<float> (std::rand()) / RAND_MAX;
std::map<const int, float> random;
// Determine the random magnitudes (unless this is a castable spell, in which case
// they will be determined when the spell is cast)
if (spell->mData.mType != ESM::Spell::ST_Power && spell->mData.mType != ESM::Spell::ST_Spell)
{
for (unsigned int i=0; i<spell->mEffects.mList.size();++i)
{
if (spell->mEffects.mList[i].mMagnMin != spell->mEffects.mList[i].mMagnMax)
random[i] = static_cast<float> (std::rand()) / RAND_MAX;
}
}
mSpells.insert (std::make_pair (Misc::StringUtils::lowerCase(spellId), random));
}
}
@ -67,7 +77,11 @@ namespace MWMechanics
int i=0;
for (std::vector<ESM::ENAMstruct>::const_iterator it = spell->mEffects.mList.begin(); it != spell->mEffects.mList.end(); ++it)
{
effects.add (*it, it->mMagnMin + (it->mMagnMax - it->mMagnMin) * iter->second[i]);
float random = 1.f;
if (iter->second.find(i) != iter->second.end())
random = iter->second.at(i);
effects.add (*it, it->mMagnMin + (it->mMagnMax - it->mMagnMin) * random);
++i;
}
}
@ -192,9 +206,60 @@ namespace MWMechanics
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = list.mList.begin();
effectIt != list.mList.end(); ++effectIt, ++i)
{
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * it->second[i];
float random = 1.f;
if (it->second.find(i) != it->second.end())
random = it->second.at(i);
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random;
visitor.visit(MWMechanics::EffectKey(*effectIt), spell->mName, "", magnitude);
}
}
}
bool Spells::canUsePower(const std::string &power) const
{
std::map<std::string, MWWorld::TimeStamp>::const_iterator it = mUsedPowers.find(power);
if (it == mUsedPowers.end() || it->second + 24 <= MWBase::Environment::get().getWorld()->getTimeStamp())
return true;
else
return false;
}
void Spells::usePower(const std::string &power)
{
mUsedPowers[power] = MWBase::Environment::get().getWorld()->getTimeStamp();
}
void Spells::readState(const ESM::SpellState &state)
{
mSpells = state.mSpells;
mSelectedSpell = state.mSelectedSpell;
// Discard spells that are no longer available due to changed content files
for (TContainer::iterator iter = mSpells.begin(); iter!=mSpells.end();)
{
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(iter->first);
if (!spell)
{
if (iter->first == mSelectedSpell)
mSelectedSpell = "";
mSpells.erase(iter++);
}
else
++iter;
}
// No need to discard spells here (doesn't really matter if non existent ids are kept)
for (std::map<std::string, ESM::TimeStamp>::const_iterator it = state.mUsedPowers.begin(); it != state.mUsedPowers.end(); ++it)
mUsedPowers[it->first] = MWWorld::TimeStamp(it->second);
}
void Spells::writeState(ESM::SpellState &state) const
{
state.mSpells = mSpells;
state.mSelectedSpell = mSelectedSpell;
for (std::map<std::string, MWWorld::TimeStamp>::const_iterator it = mUsedPowers.begin(); it != mUsedPowers.end(); ++it)
state.mUsedPowers[it->first] = it->second.toEsm();
}
}

@ -7,12 +7,16 @@
#include <components/misc/stringops.hpp>
#include "../mwworld/ptr.hpp"
#include "../mwworld/timestamp.hpp"
#include "magiceffects.hpp"
namespace ESM
{
struct Spell;
struct SpellState;
}
namespace MWMechanics
@ -22,21 +26,29 @@ namespace MWMechanics
/// \brief Spell list
///
/// This class manages known spells as well as abilities, powers and permanent negative effects like
/// diseases.
/// diseases. It also keeps track of used powers (which can only be used every 24h).
class Spells
{
public:
typedef std::map<std::string, std::vector<float> > TContainer; // ID, normalised magnitudes
typedef std::map<std::string, std::map<const int, float> > TContainer; // ID, <effect index, normalised random magnitude>
typedef TContainer::const_iterator TIterator;
private:
TContainer mSpells;
// Note: this is the spell that's about to be cast, *not* the spell selected in the GUI (which may be different)
std::string mSelectedSpell;
std::map<std::string, MWWorld::TimeStamp> mUsedPowers;
public:
bool canUsePower (const std::string& power) const;
void usePower (const std::string& power);
void purgeCommonDisease();
void purgeBlightDisease();
void purgeCorprusDisease();
@ -72,6 +84,9 @@ namespace MWMechanics
bool hasBlightDisease() const;
void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor) const;
void readState (const ESM::SpellState& state);
void writeState (ESM::SpellState& state) const;
};
}

@ -342,8 +342,6 @@ Ogre::TexturePtr LocalMap::createFogOfWarTexture(const std::string &texName)
PF_A8R8G8B8,
TU_DYNAMIC_WRITE_ONLY);
}
else
tex->unload();
return tex;
}
@ -362,6 +360,7 @@ void LocalMap::loadFogOfWar (const std::string& texturePrefix, ESM::FogTexture&
Ogre::TexturePtr tex = createFogOfWarTexture(texName);
tex->unload();
tex->loadImage(image);
// create a buffer to use for dynamic operations

@ -326,6 +326,8 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl
case ESM::REC_GMAP:
case ESM::REC_KEYS:
case ESM::REC_ASPL:
MWBase::Environment::get().getWindowManager()->readRecord(reader, n.val);
break;

@ -199,7 +199,7 @@ void ESMStore::setUp()
if (!mRaces.find (player->mRace) ||
!mClasses.find (player->mClass))
throw std::runtime_error ("Invalid player record (race or class unavilable");
throw std::runtime_error ("Invalid player record (race or class unavailable");
}
return true;

@ -87,6 +87,7 @@ namespace MWWorld
float mMultiplier;
};
// TODO: store in savegame
typedef std::map<std::string, std::vector<EffectParams> > TEffectMagnitudes;
TEffectMagnitudes mPermanentMagicEffectMagnitudes;

@ -1,10 +1,10 @@
#include "timestamp.hpp"
#include <cmath>
#include <stdexcept>
#include <components/esm/defs.hpp>
namespace MWWorld
{
TimeStamp::TimeStamp (float hour, int day)
@ -105,4 +105,18 @@ namespace MWWorld
return hours + 24*days;
}
ESM::TimeStamp TimeStamp::toEsm() const
{
ESM::TimeStamp ret;
ret.mDay = mDay;
ret.mHour = mHour;
return ret;
}
TimeStamp::TimeStamp(const ESM::TimeStamp &esm)
{
mDay = esm.mDay;
mHour = esm.mHour;
}
}

@ -1,6 +1,11 @@
#ifndef GAME_MWWORLD_TIMESTAMP_H
#define GAME_MWWORLD_TIMESTAMP_H
namespace ESM
{
class TimeStamp;
}
namespace MWWorld
{
/// \brief In-game time stamp
@ -14,9 +19,12 @@ namespace MWWorld
public:
explicit TimeStamp (float hour = 0, int day = 0);
///< \oaram hour [0, 23)
///< \param hour [0, 23)
/// \param day >=0
explicit TimeStamp (const ESM::TimeStamp& esm);
ESM::TimeStamp toEsm () const;
float getHour() const;
int getDay() const;

@ -2164,8 +2164,8 @@ namespace MWWorld
// If this is a power, check if it was already used in the last 24h
if (!fail && spell->mData.mType == ESM::Spell::ST_Power)
{
if (stats.canUsePower(spell->mId))
stats.usePower(spell->mId);
if (stats.getSpells().canUsePower(spell->mId))
stats.getSpells().usePower(spell->mId);
else
{
message = "#{sPowerAlreadyUsed}";

@ -45,7 +45,7 @@ add_component_dir (esm
loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat
loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter
savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate dialoguestate statstate
npcstats creaturestats weatherstate quickkeys fogstate
npcstats creaturestats weatherstate quickkeys fogstate spellstate
)
add_component_dir (misc

@ -8,13 +8,143 @@ void ESM::CreatureStats::load (ESMReader &esm)
for (int i=0; i<3; ++i)
mDynamic[i].load (esm);
mGoldPool = 0;
esm.getHNOT (mGoldPool, "GOLD");
mTradeTime.mDay = 0;
mTradeTime.mHour = 0;
esm.getHNOT (mTradeTime, "TIME");
mDead = false;
esm.getHNOT (mDead, "DEAD");
mDied = false;
esm.getHNOT (mDied, "DIED");
mFriendlyHits = 0;
esm.getHNOT (mFriendlyHits, "FRHT");
mTalkedTo = false;
esm.getHNOT (mTalkedTo, "TALK");
mAlarmed = false;
esm.getHNOT (mAlarmed, "ALRM");
mHostile = false;
esm.getHNOT (mHostile, "HOST");
mAttackingOrSpell = false;
esm.getHNOT (mAttackingOrSpell, "ATCK");
mKnockdown = false;
esm.getHNOT (mKnockdown, "KNCK");
mKnockdownOneFrame = false;
esm.getHNOT (mKnockdownOneFrame, "KNC1");
mKnockdownOverOneFrame = false;
esm.getHNOT (mKnockdownOverOneFrame, "KNCO");
mHitRecovery = false;
esm.getHNOT (mHitRecovery, "HITR");
mBlock = false;
esm.getHNOT (mBlock, "BLCK");
mMovementFlags = 0;
esm.getHNOT (mMovementFlags, "MOVE");
mAttackStrength = 0;
esm.getHNOT (mAttackStrength, "ASTR");
mFallHeight = 0;
esm.getHNOT (mFallHeight, "FALL");
mLastHitObject = esm.getHNOString ("LHIT");
mRecalcDynamicStats = false;
esm.getHNOT (mRecalcDynamicStats, "CALC");
mDrawState = 0;
esm.getHNOT (mDrawState, "DRAW");
mLevel = 1;
esm.getHNOT (mLevel, "LEVL");
mSpells.load(esm);
}
void ESM::CreatureStats::save (ESMWriter &esm) const
{
for (int i=0; i<8; ++i)
mAttributes[i].save (esm);
for (int i=0; i<3; ++i)
mDynamic[i].save (esm);
}
if (mGoldPool)
esm.writeHNT ("GOLD", mGoldPool);
esm.writeHNT ("TIME", mTradeTime);
if (mDead)
esm.writeHNT ("DEAD", mDead);
if (mDied)
esm.writeHNT ("DIED", mDied);
if (mFriendlyHits)
esm.writeHNT ("FRHT", mFriendlyHits);
if (mTalkedTo)
esm.writeHNT ("TALK", mTalkedTo);
if (mAlarmed)
esm.writeHNT ("ALRM", mAlarmed);
if (mHostile)
esm.writeHNT ("HOST", mHostile);
if (mAttackingOrSpell)
esm.writeHNT ("ATCK", mAttackingOrSpell);
if (mKnockdown)
esm.writeHNT ("KNCK", mKnockdown);
if (mKnockdownOneFrame)
esm.writeHNT ("KNC1", mKnockdownOneFrame);
if (mKnockdownOverOneFrame)
esm.writeHNT ("KNCO", mKnockdownOverOneFrame);
if (mHitRecovery)
esm.writeHNT ("HITR", mHitRecovery);
if (mBlock)
esm.writeHNT ("BLCK", mBlock);
if (mMovementFlags)
esm.writeHNT ("MOVE", mMovementFlags);
if (mAttackStrength)
esm.writeHNT ("ASTR", mAttackStrength);
if (mFallHeight)
esm.writeHNT ("FALL", mFallHeight);
if (!mLastHitObject.empty())
esm.writeHNString ("LHIT", mLastHitObject);
if (mRecalcDynamicStats)
esm.writeHNT ("CALC", mRecalcDynamicStats);
if (mDrawState)
esm.writeHNT ("DRAW", mDrawState);
if (mLevel != 1)
esm.writeHNT ("LEVL", mLevel);
mSpells.save(esm);
}

@ -7,21 +7,51 @@
#include "statstate.hpp"
#include "defs.hpp"
#include "spellstate.hpp"
namespace ESM
{
class ESMReader;
class ESMWriter;
// format 0, saved games only
struct CreatureStats
{
StatState<int> mAttributes[8];
StatState<float> mDynamic[3];
ESM::TimeStamp mTradeTime;
int mGoldPool;
bool mDead;
bool mDied;
int mFriendlyHits;
bool mTalkedTo;
bool mAlarmed;
bool mAttacked;
bool mHostile;
bool mAttackingOrSpell;
bool mKnockdown;
bool mKnockdownOneFrame;
bool mKnockdownOverOneFrame;
bool mHitRecovery;
bool mBlock;
unsigned int mMovementFlags;
float mAttackStrength;
float mFallHeight;
std::string mLastHitObject;
bool mRecalcDynamicStats;
int mDrawState;
int mLevel;
SpellState mSpells;
void load (ESMReader &esm);
void save (ESMWriter &esm) const;
};
}
#endif
#endif

@ -6,6 +6,12 @@
namespace ESM
{
struct TimeStamp
{
float mHour;
int mDay;
};
// Pixel color value. Standard four-byte rr,gg,bb,aa format.
typedef int32_t Color;
@ -101,6 +107,7 @@ enum RecNameInts
REC_WTHR = 0x52485457,
REC_KEYS = FourCC<'K','E','Y','S'>::value,
REC_DYNA = FourCC<'D','Y','N','A'>::value,
REC_ASPL = FourCC<'A','S','P','L'>::value,
// format 1
REC_FILT = 0x544C4946

@ -100,6 +100,9 @@ namespace ESM
void ESMWriter::startSubRecord(const std::string& name)
{
// Sub-record hierarchies are not properly supported in ESMReader. This should be fixed later.
assert (mRecords.size() <= 1);
writeName(name);
RecordData rec;
rec.name = name;

@ -96,6 +96,7 @@ class ESMWriter
void startRecord(const std::string& name, uint32_t flags = 0);
void startRecord(uint32_t name, uint32_t flags = 0);
/// @note Sub-record hierarchies are not properly supported in ESMReader. This should be fixed later.
void startSubRecord(const std::string& name);
void endRecord(const std::string& name);
void endRecord(uint32_t name);

@ -36,6 +36,18 @@ void ESM::NpcStats::load (ESMReader &esm)
mSkills[i].mWerewolf.load (esm);
}
bool hasWerewolfAttributes = false;
esm.getHNOT (hasWerewolfAttributes, "HWAT");
if (hasWerewolfAttributes)
{
for (int i=0; i<8; ++i)
mWerewolfAttributes[i].load (esm);
}
mIsWerewolf = false;
esm.getHNOT (mIsWerewolf, "WOLF");
mBounty = 0;
esm.getHNOT (mBounty, "BOUN");
@ -48,8 +60,9 @@ void ESM::NpcStats::load (ESMReader &esm)
mProfit = 0;
esm.getHNOT (mProfit, "PROF");
mAttackStrength = 0;
esm.getHNOT (mAttackStrength, "ASTR");
// No longer used. Now part of CreatureStats.
float attackStrength = 0;
esm.getHNOT (attackStrength, "ASTR");
mLevelProgress = 0;
esm.getHNOT (mLevelProgress, "LPRO");
@ -101,6 +114,13 @@ void ESM::NpcStats::save (ESMWriter &esm) const
mSkills[i].mWerewolf.save (esm);
}
esm.writeHNT ("HWAT", true);
for (int i=0; i<8; ++i)
mWerewolfAttributes[i].save (esm);
if (mIsWerewolf)
esm.writeHNT ("WOLF", mIsWerewolf);
if (mBounty)
esm.writeHNT ("BOUN", mBounty);
@ -113,9 +133,6 @@ void ESM::NpcStats::save (ESMWriter &esm) const
if (mProfit)
esm.writeHNT ("PROF", mProfit);
if (mAttackStrength)
esm.writeHNT ("ASTR", mAttackStrength);
if (mLevelProgress)
esm.writeHNT ("LPRO", mLevelProgress);
@ -136,4 +153,4 @@ void ESM::NpcStats::save (ESMWriter &esm) const
if (mCrimeId != -1)
esm.writeHNT ("CRID", mCrimeId);
}
}

@ -31,6 +31,9 @@ namespace ESM
Faction();
};
StatState<int> mWerewolfAttributes[8];
bool mIsWerewolf;
std::map<std::string, Faction> mFactions;
int mDisposition;
Skill mSkills[27];
@ -38,7 +41,6 @@ namespace ESM
int mReputation;
int mWerewolfKills;
int mProfit;
float mAttackStrength;
int mLevelProgress;
int mSkillIncrease[8];
std::vector<std::string> mUsedIds;
@ -52,4 +54,4 @@ namespace ESM
};
}
#endif
#endif

@ -8,11 +8,13 @@ namespace ESM
void QuickKeys::load(ESMReader &esm)
{
while (esm.isNextSub("KEY_"))
if (esm.isNextSub("KEY_"))
esm.getSubHeader(); // no longer used, because sub-record hierachies do not work properly in esmreader
while (esm.isNextSub("TYPE"))
{
esm.getSubHeader();
int keyType;
esm.getHNT(keyType, "TYPE");
esm.getHT(keyType);
std::string id;
id = esm.getHNString("ID__");
@ -21,21 +23,18 @@ namespace ESM
key.mId = id;
mKeys.push_back(key);
if (esm.isNextSub("KEY_"))
esm.getSubHeader(); // no longer used, because sub-record hierachies do not work properly in esmreader
}
}
void QuickKeys::save(ESMWriter &esm) const
{
const std::string recKey = "KEY_";
for (std::vector<QuickKey>::const_iterator it = mKeys.begin(); it != mKeys.end(); ++it)
{
esm.startSubRecord(recKey);
esm.writeHNT("TYPE", it->mType);
esm.writeHNString("ID__", it->mId);
esm.endRecord(recKey);
}
}

@ -0,0 +1,66 @@
#include "spellstate.hpp"
#include "esmreader.hpp"
#include "esmwriter.hpp"
namespace ESM
{
void SpellState::load(ESMReader &esm)
{
while (esm.isNextSub("SPEL"))
{
std::string id = esm.getHString();
std::map<const int, float> random;
while (esm.isNextSub("INDX"))
{
int index;
esm.getHT(index);
float magnitude;
esm.getHNT(magnitude, "RAND");
random[index] = magnitude;
}
mSpells[id] = random;
}
while (esm.isNextSub("USED"))
{
std::string id = esm.getHString();
TimeStamp time;
esm.getHNT(time, "TIME");
mUsedPowers[id] = time;
}
mSelectedSpell = esm.getHNOString("SLCT");
}
void SpellState::save(ESMWriter &esm) const
{
for (TContainer::const_iterator it = mSpells.begin(); it != mSpells.end(); ++it)
{
esm.writeHNString("SPEL", it->first);
const std::map<const int, float>& random = it->second;
for (std::map<const int, float>::const_iterator rIt = random.begin(); rIt != random.end(); ++rIt)
{
esm.writeHNT("INDX", rIt->first);
esm.writeHNT("RAND", rIt->second);
}
}
for (std::map<std::string, TimeStamp>::const_iterator it = mUsedPowers.begin(); it != mUsedPowers.end(); ++it)
{
esm.writeHNString("USED", it->first);
esm.writeHNT("TIME", it->second);
}
if (!mSelectedSpell.empty())
esm.writeHNString("SLCT", mSelectedSpell);
}
}

@ -0,0 +1,29 @@
#ifndef OPENMW_ESM_SPELLSTATE_H
#define OPENMW_ESM_SPELLSTATE_H
#include <map>
#include <string>
#include "defs.hpp"
namespace ESM
{
class ESMReader;
class ESMWriter;
struct SpellState
{
typedef std::map<std::string, std::map<const int, float> > TContainer;
TContainer mSpells;
std::map<std::string, TimeStamp> mUsedPowers;
std::string mSelectedSpell;
void load (ESMReader &esm);
void save (ESMWriter &esm) const;
};
}
#endif
Loading…
Cancel
Save