diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index b548e0844..4c9b683a4 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -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 diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index e3baf84fb..ed6910221 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -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 diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 40ac92251..1482ee24e 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -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::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 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 & getHealth() const; diff --git a/apps/openmw/mwmechanics/drawstate.hpp b/apps/openmw/mwmechanics/drawstate.hpp index 5be00505c..7f59d8d78 100644 --- a/apps/openmw/mwmechanics/drawstate.hpp +++ b/apps/openmw/mwmechanics/drawstate.hpp @@ -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 }; } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index eb51ddfc5..2b1f82b92 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -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(); } diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index b1d2c75b1..24e758e32 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -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::const_iterator iter (state.mUsedIds.begin()); diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 0ae596a54..185a58b94 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -31,8 +31,8 @@ namespace MWMechanics std::map mFactionRank; int mDisposition; - SkillValue mSkill[27]; - SkillValue mWerewolfSkill[27]; + SkillValue mSkill[ESM::Skill::Length]; + SkillValue mWerewolfSkill[ESM::Skill::Length]; int mBounty; std::set mExpelled; std::map mFactionReputation; @@ -40,7 +40,6 @@ namespace MWMechanics int mCrimeId; int mWerewolfKills; int mProfit; - float mAttackStrength; int mLevelProgress; // 0-10 diff --git a/apps/openmw/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp index c2bf794f1..a326b7a6d 100644 --- a/apps/openmw/mwmechanics/spells.cpp +++ b/apps/openmw/mwmechanics/spells.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -30,10 +31,19 @@ namespace MWMechanics { const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().find(spellId); - std::vector random; - random.resize(spell->mEffects.mList.size()); - for (unsigned int i=0; i (std::rand()) / RAND_MAX; + std::map 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; imEffects.mList.size();++i) + { + if (spell->mEffects.mList[i].mMagnMin != spell->mEffects.mList[i].mMagnMax) + random[i] = static_cast (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::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::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::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().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::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::const_iterator it = mUsedPowers.begin(); it != mUsedPowers.end(); ++it) + state.mUsedPowers[it->first] = it->second.toEsm(); + } } diff --git a/apps/openmw/mwmechanics/spells.hpp b/apps/openmw/mwmechanics/spells.hpp index 354b1fd0b..6997a9d7a 100644 --- a/apps/openmw/mwmechanics/spells.hpp +++ b/apps/openmw/mwmechanics/spells.hpp @@ -7,12 +7,16 @@ #include #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 > TContainer; // ID, normalised magnitudes + + typedef std::map > TContainer; // ID, 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 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; }; } diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 4168127df..0d32dd0ef 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -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 diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index c89041710..153da1f30 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -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; diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index a769e7f67..ed140434b 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -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; diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index b4b275b6a..95b956907 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -87,6 +87,7 @@ namespace MWWorld float mMultiplier; }; + // TODO: store in savegame typedef std::map > TEffectMagnitudes; TEffectMagnitudes mPermanentMagicEffectMagnitudes; diff --git a/apps/openmw/mwworld/timestamp.cpp b/apps/openmw/mwworld/timestamp.cpp index 126d5490c..a73ed7ca5 100644 --- a/apps/openmw/mwworld/timestamp.cpp +++ b/apps/openmw/mwworld/timestamp.cpp @@ -1,10 +1,10 @@ - #include "timestamp.hpp" #include - #include +#include + 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; + } } diff --git a/apps/openmw/mwworld/timestamp.hpp b/apps/openmw/mwworld/timestamp.hpp index e2d8b242a..54cd40baf 100644 --- a/apps/openmw/mwworld/timestamp.hpp +++ b/apps/openmw/mwworld/timestamp.hpp @@ -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; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index d5da19dfa..b9a8f8dda 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -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}"; diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 3dd5df295..533288048 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -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 diff --git a/components/esm/creaturestats.cpp b/components/esm/creaturestats.cpp index fe250089a..5bb6de662 100644 --- a/components/esm/creaturestats.cpp +++ b/components/esm/creaturestats.cpp @@ -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); -} \ No newline at end of file + + 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); +} diff --git a/components/esm/creaturestats.hpp b/components/esm/creaturestats.hpp index 540044f38..2cf2c5b4f 100644 --- a/components/esm/creaturestats.hpp +++ b/components/esm/creaturestats.hpp @@ -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 mAttributes[8]; StatState 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 \ No newline at end of file +#endif diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 5a99d0c2e..9ef4dabd2 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -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 diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index 91f123eb7..9d8d943d9 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -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; diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index b385ac067..57faefdf4 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -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); diff --git a/components/esm/npcstats.cpp b/components/esm/npcstats.cpp index 80238ad68..21f573bc7 100644 --- a/components/esm/npcstats.cpp +++ b/components/esm/npcstats.cpp @@ -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); -} \ No newline at end of file +} diff --git a/components/esm/npcstats.hpp b/components/esm/npcstats.hpp index 504cd0163..ce7c75d2a 100644 --- a/components/esm/npcstats.hpp +++ b/components/esm/npcstats.hpp @@ -31,6 +31,9 @@ namespace ESM Faction(); }; + StatState mWerewolfAttributes[8]; + bool mIsWerewolf; + std::map 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 mUsedIds; @@ -52,4 +54,4 @@ namespace ESM }; } -#endif \ No newline at end of file +#endif diff --git a/components/esm/quickkeys.cpp b/components/esm/quickkeys.cpp index ad2b671aa..42cd91c5b 100644 --- a/components/esm/quickkeys.cpp +++ b/components/esm/quickkeys.cpp @@ -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::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); } } diff --git a/components/esm/spellstate.cpp b/components/esm/spellstate.cpp new file mode 100644 index 000000000..2dca2dcec --- /dev/null +++ b/components/esm/spellstate.cpp @@ -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 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& random = it->second; + for (std::map::const_iterator rIt = random.begin(); rIt != random.end(); ++rIt) + { + esm.writeHNT("INDX", rIt->first); + esm.writeHNT("RAND", rIt->second); + } + } + + for (std::map::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); + } + +} diff --git a/components/esm/spellstate.hpp b/components/esm/spellstate.hpp new file mode 100644 index 000000000..cb5c0ff0d --- /dev/null +++ b/components/esm/spellstate.hpp @@ -0,0 +1,29 @@ +#ifndef OPENMW_ESM_SPELLSTATE_H +#define OPENMW_ESM_SPELLSTATE_H + +#include +#include + +#include "defs.hpp" + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + struct SpellState + { + typedef std::map > TContainer; + TContainer mSpells; + + std::map mUsedPowers; + + std::string mSelectedSpell; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; + +} + +#endif