Savegame: store most of CreatureStats

deque
scrawl 11 years ago
parent 117b812fb1
commit 6cc691115b

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

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

@ -17,8 +17,10 @@ namespace MWMechanics
mAttacked (false), mHostile (false), mAttacked (false), mHostile (false),
mAttackingOrSpell(false), mAttackingOrSpell(false),
mIsWerewolf(false), mIsWerewolf(false),
mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false), mHitRecovery(false), mBlock(false), mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mKnockdownOneFrame(false),
mMovementFlags(0), mDrawState (DrawState_Nothing), mAttackStrength(0.f) 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) for (int i=0; i<4; ++i)
mAiSettings[i] = 0; mAiSettings[i] = 0;
@ -348,20 +350,6 @@ namespace MWMechanics
return mLastHitObject; 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) void CreatureStats::addToFallHeight(float height)
{ {
mFallHeight += height; mFallHeight += height;
@ -481,20 +469,75 @@ namespace MWMechanics
void CreatureStats::writeState (ESM::CreatureStats& state) const 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]); mAttributes[i].writeState (state.mAttributes[i]);
for (int i=0; i<3; ++i) for (int i=0; i<3; ++i)
mDynamic[i].writeState (state.mDynamic[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) 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]); mAttributes[i].readState (state.mAttributes[i]);
for (int i=0; i<3; ++i) for (int i=0; i<3; ++i)
mDynamic[i].readState (state.mDynamic[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 // Relates to NPC gold reset delay

@ -55,15 +55,15 @@ namespace MWMechanics
// Do we need to recalculate stats derived from attributes or other factors? // Do we need to recalculate stats derived from attributes or other factors?
bool mRecalcDynamicStats; bool mRecalcDynamicStats;
std::map<std::string, MWWorld::TimeStamp> mUsedPowers;
MWWorld::TimeStamp mTradeTime; // Relates to NPC gold reset delay MWWorld::TimeStamp mTradeTime; // Relates to NPC gold reset delay
int mGoldPool; // the pool of merchant gold not in inventory int mGoldPool; // the pool of merchant gold not in inventory
protected: protected:
// These two are only set by NpcStats, but they are declared in CreatureStats to prevent using virtual methods.
bool mIsWerewolf; bool mIsWerewolf;
AttributeValue mWerewolfAttributes[8]; AttributeValue mWerewolfAttributes[8];
int mLevel; int mLevel;
public: public:
@ -84,9 +84,6 @@ namespace MWMechanics
/// @return total fall height /// @return total fall height
float land(); float land();
bool canUsePower (const std::string& power) const;
void usePower (const std::string& power);
const AttributeValue & getAttribute(int index) const; const AttributeValue & getAttribute(int index) const;
const DynamicStat<float> & getHealth() 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! /// \note The _ suffix is required to avoid a collision with a Windoze macro. Die, Microsoft! Die!
enum DrawState_ enum DrawState_
{ {
DrawState_Weapon = 0, DrawState_Nothing = 0,
DrawState_Spell = 1, DrawState_Weapon = 1,
DrawState_Nothing = 2 DrawState_Spell = 2
}; };
} }

@ -342,7 +342,9 @@ namespace MWMechanics
MWWorld::ContainerStoreIterator enchantItem = inv.getSelectedEnchantItem(); MWWorld::ContainerStoreIterator enchantItem = inv.getSelectedEnchantItem();
if (enchantItem != inv.end()) if (enchantItem != inv.end())
winMgr->setSelectedEnchantItem(*enchantItem); 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(); winMgr->unsetSelectedSpell();
} }

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

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

@ -4,6 +4,7 @@
#include <cstdlib> #include <cstdlib>
#include <components/esm/loadspel.hpp> #include <components/esm/loadspel.hpp>
#include <components/esm/spellstate.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.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); const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId);
std::vector<float> random; std::map<const int, float> random;
random.resize(spell->mEffects.mList.size());
for (unsigned int i=0; i<random.size();++i) // Determine the random magnitudes (unless this is a castable spell, in which case
random[i] = static_cast<float> (std::rand()) / RAND_MAX; // 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)); mSpells.insert (std::make_pair (Misc::StringUtils::lowerCase(spellId), random));
} }
} }
@ -67,7 +77,11 @@ namespace MWMechanics
int i=0; int i=0;
for (std::vector<ESM::ENAMstruct>::const_iterator it = spell->mEffects.mList.begin(); it != spell->mEffects.mList.end(); ++it) 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; ++i;
} }
} }
@ -192,9 +206,60 @@ namespace MWMechanics
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = list.mList.begin(); for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = list.mList.begin();
effectIt != list.mList.end(); ++effectIt, ++i) 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); 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 <components/misc/stringops.hpp>
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
#include "../mwworld/timestamp.hpp"
#include "magiceffects.hpp" #include "magiceffects.hpp"
namespace ESM namespace ESM
{ {
struct Spell; struct Spell;
struct SpellState;
} }
namespace MWMechanics namespace MWMechanics
@ -22,21 +26,29 @@ namespace MWMechanics
/// \brief Spell list /// \brief Spell list
/// ///
/// This class manages known spells as well as abilities, powers and permanent negative effects like /// 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 class Spells
{ {
public: 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; typedef TContainer::const_iterator TIterator;
private: private:
TContainer mSpells; 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::string mSelectedSpell;
std::map<std::string, MWWorld::TimeStamp> mUsedPowers;
public: public:
bool canUsePower (const std::string& power) const;
void usePower (const std::string& power);
void purgeCommonDisease(); void purgeCommonDisease();
void purgeBlightDisease(); void purgeBlightDisease();
void purgeCorprusDisease(); void purgeCorprusDisease();
@ -72,6 +84,9 @@ namespace MWMechanics
bool hasBlightDisease() const; bool hasBlightDisease() const;
void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor) 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, PF_A8R8G8B8,
TU_DYNAMIC_WRITE_ONLY); TU_DYNAMIC_WRITE_ONLY);
} }
else
tex->unload();
return tex; return tex;
} }
@ -362,6 +360,7 @@ void LocalMap::loadFogOfWar (const std::string& texturePrefix, ESM::FogTexture&
Ogre::TexturePtr tex = createFogOfWarTexture(texName); Ogre::TexturePtr tex = createFogOfWarTexture(texName);
tex->unload();
tex->loadImage(image); tex->loadImage(image);
// create a buffer to use for dynamic operations // 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_GMAP:
case ESM::REC_KEYS: case ESM::REC_KEYS:
case ESM::REC_ASPL:
MWBase::Environment::get().getWindowManager()->readRecord(reader, n.val); MWBase::Environment::get().getWindowManager()->readRecord(reader, n.val);
break; break;

@ -199,7 +199,7 @@ void ESMStore::setUp()
if (!mRaces.find (player->mRace) || if (!mRaces.find (player->mRace) ||
!mClasses.find (player->mClass)) !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; return true;

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

@ -1,10 +1,10 @@
#include "timestamp.hpp" #include "timestamp.hpp"
#include <cmath> #include <cmath>
#include <stdexcept> #include <stdexcept>
#include <components/esm/defs.hpp>
namespace MWWorld namespace MWWorld
{ {
TimeStamp::TimeStamp (float hour, int day) TimeStamp::TimeStamp (float hour, int day)
@ -105,4 +105,18 @@ namespace MWWorld
return hours + 24*days; 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 #ifndef GAME_MWWORLD_TIMESTAMP_H
#define GAME_MWWORLD_TIMESTAMP_H #define GAME_MWWORLD_TIMESTAMP_H
namespace ESM
{
class TimeStamp;
}
namespace MWWorld namespace MWWorld
{ {
/// \brief In-game time stamp /// \brief In-game time stamp
@ -14,9 +19,12 @@ namespace MWWorld
public: public:
explicit TimeStamp (float hour = 0, int day = 0); explicit TimeStamp (float hour = 0, int day = 0);
///< \oaram hour [0, 23) ///< \param hour [0, 23)
/// \param day >=0 /// \param day >=0
explicit TimeStamp (const ESM::TimeStamp& esm);
ESM::TimeStamp toEsm () const;
float getHour() const; float getHour() const;
int getDay() 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 this is a power, check if it was already used in the last 24h
if (!fail && spell->mData.mType == ESM::Spell::ST_Power) if (!fail && spell->mData.mType == ESM::Spell::ST_Power)
{ {
if (stats.canUsePower(spell->mId)) if (stats.getSpells().canUsePower(spell->mId))
stats.usePower(spell->mId); stats.getSpells().usePower(spell->mId);
else else
{ {
message = "#{sPowerAlreadyUsed}"; message = "#{sPowerAlreadyUsed}";

@ -45,7 +45,7 @@ add_component_dir (esm
loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat
loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter 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 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 add_component_dir (misc

@ -8,13 +8,143 @@ void ESM::CreatureStats::load (ESMReader &esm)
for (int i=0; i<3; ++i) for (int i=0; i<3; ++i)
mDynamic[i].load (esm); 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 void ESM::CreatureStats::save (ESMWriter &esm) const
{ {
for (int i=0; i<8; ++i) for (int i=0; i<8; ++i)
mAttributes[i].save (esm); mAttributes[i].save (esm);
for (int i=0; i<3; ++i) for (int i=0; i<3; ++i)
mDynamic[i].save (esm); 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 "statstate.hpp"
#include "defs.hpp"
#include "spellstate.hpp"
namespace ESM namespace ESM
{ {
class ESMReader; class ESMReader;
class ESMWriter; class ESMWriter;
// format 0, saved games only // format 0, saved games only
struct CreatureStats struct CreatureStats
{ {
StatState<int> mAttributes[8]; StatState<int> mAttributes[8];
StatState<float> mDynamic[3]; 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 load (ESMReader &esm);
void save (ESMWriter &esm) const; void save (ESMWriter &esm) const;
}; };
} }
#endif #endif

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

@ -100,6 +100,9 @@ namespace ESM
void ESMWriter::startSubRecord(const std::string& name) 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); writeName(name);
RecordData rec; RecordData rec;
rec.name = name; rec.name = name;

@ -96,6 +96,7 @@ class ESMWriter
void startRecord(const std::string& name, uint32_t flags = 0); void startRecord(const std::string& name, uint32_t flags = 0);
void startRecord(uint32_t 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 startSubRecord(const std::string& name);
void endRecord(const std::string& name); void endRecord(const std::string& name);
void endRecord(uint32_t name); void endRecord(uint32_t name);

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

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

@ -8,11 +8,13 @@ namespace ESM
void QuickKeys::load(ESMReader &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; int keyType;
esm.getHNT(keyType, "TYPE"); esm.getHT(keyType);
std::string id; std::string id;
id = esm.getHNString("ID__"); id = esm.getHNString("ID__");
@ -21,21 +23,18 @@ namespace ESM
key.mId = id; key.mId = id;
mKeys.push_back(key); 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 void QuickKeys::save(ESMWriter &esm) const
{ {
const std::string recKey = "KEY_";
for (std::vector<QuickKey>::const_iterator it = mKeys.begin(); it != mKeys.end(); ++it) for (std::vector<QuickKey>::const_iterator it = mKeys.begin(); it != mKeys.end(); ++it)
{ {
esm.startSubRecord(recKey);
esm.writeHNT("TYPE", it->mType); esm.writeHNT("TYPE", it->mType);
esm.writeHNString("ID__", it->mId); 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