From 1acd1bd913c8dc68483c8ce82c0a55678fd13219 Mon Sep 17 00:00:00 2001 From: slothlife Date: Thu, 20 Mar 2014 01:25:52 -0500 Subject: [PATCH 1/4] Feature #1173: Saved Game: include weather state Removed some unused state in and changed Ogre::String to std::string in WeatherManager. --- apps/openmw/mwstate/statemanagerimp.cpp | 1 + apps/openmw/mwworld/weather.cpp | 77 ++++++++++++++++++++++--- apps/openmw/mwworld/weather.hpp | 37 +++++++----- apps/openmw/mwworld/worldimp.cpp | 4 +- components/esm/defs.hpp | 1 + 5 files changed, 93 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index d6309c1c9..c0860b784 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -266,6 +266,7 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl case ESM::REC_GLOB: case ESM::REC_PLAY: case ESM::REC_CSTA: + case ESM::REC_WTHR: MWBase::Environment::get().getWorld()->readRecord (reader, n.val, contentFileMap); break; diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 5f38ee286..b74ca6393 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -1,5 +1,8 @@ #include "weather.hpp" +#include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" @@ -15,6 +18,15 @@ using namespace Ogre; using namespace MWWorld; using namespace MWSound; +#define HOUR "HOUR" +#define WINDSPEED "WNSP" +#define CURRENTWEATHER "CWTH" +#define NEXTWEATHER "NWTH" +#define CURRENTREGION "CREG" +#define FIRSTUPDATE "FUPD" +#define REMAININGTRANSITIONTIME "RTTM" +#define TIMEPASSED "TMPS" + namespace { float lerp (float x, float y, float factor) @@ -91,8 +103,7 @@ WeatherManager::WeatherManager(MWRender::RenderingManager* rendering,MWWorld::Fa mHour(14), mCurrentWeather("clear"), mNextWeather(""), mFirstUpdate(true), mWeatherUpdateTime(0), mThunderFlash(0), mThunderChance(0), mThunderChanceNeeded(50), mThunderSoundDelay(0), mRemainingTransitionTime(0), - mMonth(0), mDay(0), mTimePassed(0), mFallback(fallback), mWindSpeed(0.f), - mRendering(rendering) + mTimePassed(0), mFallback(fallback), mWindSpeed(0.f), mRendering(rendering) { //Globals mThunderSoundID0 = mFallback->getFallbackString("Weather_Thunderstorm_Thunder_Sound_ID_0"); @@ -530,7 +541,7 @@ void WeatherManager::stopSounds(bool stopAll) } } -Ogre::String WeatherManager::nextWeather(const ESM::Region* region) const +std::string WeatherManager::nextWeather(const ESM::Region* region) const { std::vector probability; @@ -599,12 +610,6 @@ void WeatherManager::setHour(const float hour) mHour = hour; } -void WeatherManager::setDate(const int day, const int month) -{ - mDay = day; - mMonth = month; -} - unsigned int WeatherManager::getWeatherID() const { // Source: http://www.uesp.net/wiki/Tes3Mod:GetCurrentWeather @@ -691,6 +696,60 @@ bool WeatherManager::isDark() const return exterior && (mHour < mSunriseTime || mHour > mNightStart - 1); } +void WeatherManager::write(ESM::ESMWriter& writer) +{ + writer.startRecord(ESM::REC_WTHR); + writer.writeHNT(HOUR, mHour); + writer.writeHNT(WINDSPEED, mWindSpeed); + writer.writeHNCString(CURRENTWEATHER, mCurrentWeather.c_str()); + writer.writeHNCString(NEXTWEATHER, mNextWeather.c_str()); + writer.writeHNCString(CURRENTREGION, mCurrentRegion.c_str()); + writer.writeHNT(FIRSTUPDATE, mFirstUpdate); + writer.writeHNT(REMAININGTRANSITIONTIME, mRemainingTransitionTime); + writer.writeHNT(TIMEPASSED, mTimePassed); + writer.endRecord(ESM::REC_WTHR); +} + +bool WeatherManager::readRecord(ESM::ESMReader& reader, int32_t type) +{ + if(ESM::REC_WTHR == type) + { + // store state in locals so that if we fail to load, we don't leave the manager in a half-way state + float newHour = 0.0; + reader.getHNT(newHour, HOUR); + float newWindSpeed = 0.0; + reader.getHNT(newWindSpeed, WINDSPEED); + std::string newCurrentWeather = reader.getHNString(CURRENTWEATHER); + std::string newNextWeather = reader.getHNString(NEXTWEATHER); + std::string newCurrentRegion = reader.getHNString(CURRENTREGION); + bool newFirstUpdate = false; + reader.getHNT(newFirstUpdate, FIRSTUPDATE); + float newRemainingTransitionTime = 0.0; + reader.getHNT(newRemainingTransitionTime, REMAININGTRANSITIONTIME); + double newTimePassed = 0.0; + reader.getHNT(newTimePassed, TIMEPASSED); + + // reset other temporary state + mRegionOverrides.clear(); + stopSounds(true); // TODO: inconsistent state if this throws... + mRegionMods.clear(); + + // swap in new values, now that we can't fail + mHour = newHour; + mWindSpeed = newWindSpeed; + mCurrentWeather.swap(newCurrentWeather); + mNextWeather.swap(newNextWeather); + mCurrentRegion.swap(newCurrentRegion); + mFirstUpdate = newFirstUpdate; + mRemainingTransitionTime = newRemainingTransitionTime; + mTimePassed = newTimePassed; + + return true; + } + + return false; +} + void WeatherManager::switchToNextWeather(bool instantly) { MWBase::World* world = MWBase::Environment::get().getWorld(); diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index fa2d9bd8e..3167a64c2 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -1,12 +1,16 @@ #ifndef GAME_MWWORLD_WEATHER_H #define GAME_MWWORLD_WEATHER_H -#include +#include +#include + #include namespace ESM { struct Region; + class ESMWriter; + class ESMReader; } namespace MWRender @@ -21,8 +25,8 @@ namespace MWWorld /// Defines the actual weather that results from weather setting (see below), time of day and weather transition struct WeatherResult { - Ogre::String mCloudTexture; - Ogre::String mNextCloudTexture; + std::string mCloudTexture; + std::string mNextCloudTexture; float mCloudBlendFactor; Ogre::ColourValue mFogColor; @@ -48,14 +52,14 @@ namespace MWWorld bool mNight; // use night skybox float mNightFade; // fading factor for night skybox - Ogre::String mAmbientLoopSoundID; + std::string mAmbientLoopSoundID; }; /// Defines a single weather setting (according to INI) struct Weather { - Ogre::String mCloudTexture; + std::string mCloudTexture; // Sky (atmosphere) colors Ogre::ColourValue mSkySunriseColor, @@ -105,10 +109,10 @@ namespace MWWorld // Sound effect // This is used for Blight, Ashstorm and Blizzard (Bloodmoon) - Ogre::String mAmbientLoopSoundID; + std::string mAmbientLoopSoundID; // Rain sound effect - Ogre::String mRainLoopSoundID; + std::string mRainLoopSoundID; /// \todo disease chance }; @@ -142,8 +146,6 @@ namespace MWWorld float getWindSpeed() const; - void setDate(const int day, const int month); - void advanceTime(double hours) { mTimePassed += hours*3600; @@ -156,22 +158,25 @@ namespace MWWorld /// @see World::isDark bool isDark() const; + void write(ESM::ESMWriter& writer); + + bool readRecord(ESM::ESMReader& reader, int32_t type); + private: float mHour; - int mDay, mMonth; float mWindSpeed; MWWorld::Fallback* mFallback; void setFallbackWeather(Weather& weather,const std::string& name); MWRender::RenderingManager* mRendering; - std::map mWeatherSettings; + std::map mWeatherSettings; std::map mRegionOverrides; std::vector mSoundsPlaying; - Ogre::String mCurrentWeather; - Ogre::String mNextWeather; + std::string mCurrentWeather; + std::string mNextWeather; std::string mCurrentRegion; @@ -186,13 +191,13 @@ namespace MWWorld double mTimePassed; // time passed since last update void transition(const float factor); - void setResult(const Ogre::String& weatherType); + void setResult(const std::string& weatherType); float calculateHourFade (const std::string& moonName) const; float calculateAngleFade (const std::string& moonName, float angle) const; - void setWeather(const Ogre::String& weatherType, bool instant=false); - Ogre::String nextWeather(const ESM::Region* region) const; + void setWeather(const std::string& weatherType, bool instant=false); + std::string nextWeather(const ESM::Region* region) const; WeatherResult mResult; typedef std::map > RegionModMap; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 62bdd38ea..81afc394a 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -279,6 +279,7 @@ namespace MWWorld mGlobalVariables.write (writer); mCells.write (writer); mPlayer->write (writer); + mWeatherManager->write (writer); } void World::readRecord (ESM::ESMReader& reader, int32_t type, @@ -287,6 +288,7 @@ namespace MWWorld if (!mStore.readRecord (reader, type) && !mGlobalVariables.readRecord (reader, type) && !mPlayer->readRecord (reader, type) && + !mWeatherManager->readRecord (reader, type) && !mCells.readRecord (reader, type, contentFileMap)) { throw std::runtime_error ("unknown record in saved game"); @@ -680,8 +682,6 @@ namespace MWWorld mGlobalVariables["month"].setInteger (month); mRendering->skySetDate (day, month); - - mWeatherManager->setDate (day, month); } void World::setMonth (int month) diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 57842796f..4d5b36c74 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -92,6 +92,7 @@ enum RecNameInts REC_CSTA = 0x41545343, REC_GMAP = 0x50414d47, REC_DIAS = 0x53414944, + REC_WTHR = 0x52485457, // format 1 REC_FILT = 0x544C4946 From 6eab9c5179725ae234a504ba10c67ab35de9ed48 Mon Sep 17 00:00:00 2001 From: slothlife Date: Fri, 21 Mar 2014 01:19:40 -0500 Subject: [PATCH 2/4] Move weather state save/load to a new class --- apps/openmw/mwworld/weather.cpp | 77 +++++++++++++-------------------- components/CMakeLists.txt | 2 +- components/esm/weatherstate.cpp | 59 +++++++++++++++++++++++++ components/esm/weatherstate.hpp | 27 ++++++++++++ 4 files changed, 118 insertions(+), 47 deletions(-) create mode 100644 components/esm/weatherstate.cpp create mode 100644 components/esm/weatherstate.hpp diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index b74ca6393..335702c66 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -1,7 +1,6 @@ #include "weather.hpp" -#include -#include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -18,15 +17,6 @@ using namespace Ogre; using namespace MWWorld; using namespace MWSound; -#define HOUR "HOUR" -#define WINDSPEED "WNSP" -#define CURRENTWEATHER "CWTH" -#define NEXTWEATHER "NWTH" -#define CURRENTREGION "CREG" -#define FIRSTUPDATE "FUPD" -#define REMAININGTRANSITIONTIME "RTTM" -#define TIMEPASSED "TMPS" - namespace { float lerp (float x, float y, float factor) @@ -698,15 +688,18 @@ bool WeatherManager::isDark() const void WeatherManager::write(ESM::ESMWriter& writer) { + ESM::WeatherState state; + state.mHour = mHour; + state.mWindSpeed = mWindSpeed; + state.mCurrentWeather = mCurrentWeather; + state.mNextWeather = mNextWeather; + state.mCurrentRegion = mCurrentRegion; + state.mFirstUpdate = mFirstUpdate; + state.mRemainingTransitionTime = mRemainingTransitionTime; + state.mTimePassed = mTimePassed; + writer.startRecord(ESM::REC_WTHR); - writer.writeHNT(HOUR, mHour); - writer.writeHNT(WINDSPEED, mWindSpeed); - writer.writeHNCString(CURRENTWEATHER, mCurrentWeather.c_str()); - writer.writeHNCString(NEXTWEATHER, mNextWeather.c_str()); - writer.writeHNCString(CURRENTREGION, mCurrentRegion.c_str()); - writer.writeHNT(FIRSTUPDATE, mFirstUpdate); - writer.writeHNT(REMAININGTRANSITIONTIME, mRemainingTransitionTime); - writer.writeHNT(TIMEPASSED, mTimePassed); + state.save(writer); writer.endRecord(ESM::REC_WTHR); } @@ -714,35 +707,27 @@ bool WeatherManager::readRecord(ESM::ESMReader& reader, int32_t type) { if(ESM::REC_WTHR == type) { - // store state in locals so that if we fail to load, we don't leave the manager in a half-way state - float newHour = 0.0; - reader.getHNT(newHour, HOUR); - float newWindSpeed = 0.0; - reader.getHNT(newWindSpeed, WINDSPEED); - std::string newCurrentWeather = reader.getHNString(CURRENTWEATHER); - std::string newNextWeather = reader.getHNString(NEXTWEATHER); - std::string newCurrentRegion = reader.getHNString(CURRENTREGION); - bool newFirstUpdate = false; - reader.getHNT(newFirstUpdate, FIRSTUPDATE); - float newRemainingTransitionTime = 0.0; - reader.getHNT(newRemainingTransitionTime, REMAININGTRANSITIONTIME); - double newTimePassed = 0.0; - reader.getHNT(newTimePassed, TIMEPASSED); - - // reset other temporary state + // load first so that if it fails, we haven't accidentally reset the state below + ESM::WeatherState state; + state.load(reader); + + // reset other temporary state, now that we loaded successfully + stopSounds(true); // let's hope this never throws mRegionOverrides.clear(); - stopSounds(true); // TODO: inconsistent state if this throws... mRegionMods.clear(); - - // swap in new values, now that we can't fail - mHour = newHour; - mWindSpeed = newWindSpeed; - mCurrentWeather.swap(newCurrentWeather); - mNextWeather.swap(newNextWeather); - mCurrentRegion.swap(newCurrentRegion); - mFirstUpdate = newFirstUpdate; - mRemainingTransitionTime = newRemainingTransitionTime; - mTimePassed = newTimePassed; + mThunderFlash = 0.0; + mThunderChance = 0.0; + mThunderChanceNeeded = 50.0; + + // swap in the loaded values now that we can't fail + mHour = state.mHour; + mWindSpeed = state.mWindSpeed; + mCurrentWeather.swap(state.mCurrentWeather); + mNextWeather.swap(state.mNextWeather); + mCurrentRegion.swap(state.mCurrentRegion); + mFirstUpdate = state.mFirstUpdate; + mRemainingTransitionTime = state.mRemainingTransitionTime; + mTimePassed = state.mTimePassed; return true; } diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index db4ecad0b..c4d13170d 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 + npcstats creaturestats weatherstate ) add_component_dir (misc diff --git a/components/esm/weatherstate.cpp b/components/esm/weatherstate.cpp new file mode 100644 index 000000000..48cf55a60 --- /dev/null +++ b/components/esm/weatherstate.cpp @@ -0,0 +1,59 @@ +#include "weatherstate.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +namespace +{ + const char* hourRecord = "HOUR"; + const char* windSpeedRecord = "WNSP"; + const char* currentWeatherRecord = "CWTH"; + const char* nextWeatherRecord = "NWTH"; + const char* currentRegionRecord = "CREG"; + const char* firstUpdateRecord = "FUPD"; + const char* remainingTransitionTimeRecord = "RTTM"; + const char* timePassedRecord = "TMPS"; +} + +namespace ESM +{ + void WeatherState::load(ESMReader& esm) + { + // store values locally so that a failed load can't leave the state half set + float newHour = 0.0; + esm.getHNT(newHour, hourRecord); + float newWindSpeed = 0.0; + esm.getHNT(newWindSpeed, windSpeedRecord); + std::string newCurrentWeather = esm.getHNString(currentWeatherRecord); + std::string newNextWeather = esm.getHNString(nextWeatherRecord); + std::string newCurrentRegion = esm.getHNString(currentRegionRecord); + bool newFirstUpdate = false; + esm.getHNT(newFirstUpdate, firstUpdateRecord); + float newRemainingTransitionTime = 0.0; + esm.getHNT(newRemainingTransitionTime, remainingTransitionTimeRecord); + double newTimePassed = 0.0; + esm.getHNT(newTimePassed, timePassedRecord); + + // swap values now that it is safe to do so + mHour = newHour; + mWindSpeed = newWindSpeed; + mCurrentWeather.swap(newCurrentWeather); + mNextWeather.swap(newNextWeather); + mCurrentRegion.swap(newCurrentRegion); + mFirstUpdate = newFirstUpdate; + mRemainingTransitionTime = newRemainingTransitionTime; + mTimePassed = newTimePassed; + } + + void WeatherState::save(ESMWriter& esm) const + { + esm.writeHNT(hourRecord, mHour); + esm.writeHNT(windSpeedRecord, mWindSpeed); + esm.writeHNCString(currentWeatherRecord, mCurrentWeather.c_str()); + esm.writeHNCString(nextWeatherRecord, mNextWeather.c_str()); + esm.writeHNCString(currentRegionRecord, mCurrentRegion.c_str()); + esm.writeHNT(firstUpdateRecord, mFirstUpdate); + esm.writeHNT(remainingTransitionTimeRecord, mRemainingTransitionTime); + esm.writeHNT(timePassedRecord, mTimePassed); + } +} diff --git a/components/esm/weatherstate.hpp b/components/esm/weatherstate.hpp new file mode 100644 index 000000000..545751225 --- /dev/null +++ b/components/esm/weatherstate.hpp @@ -0,0 +1,27 @@ +#ifndef OPENMW_ESM_WEATHER_H +#define OPENMW_ESM_WEATHER_H + +#include + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + struct WeatherState + { + float mHour; + float mWindSpeed; + std::string mCurrentWeather; + std::string mNextWeather; + std::string mCurrentRegion; + bool mFirstUpdate; + float mRemainingTransitionTime; + double mTimePassed; + + void load(ESMReader& esm); + void save(ESMWriter& esm) const; + }; +} + +#endif From 00eac7d53adc272424fa029a4eaaed262396750e Mon Sep 17 00:00:00 2001 From: slothlife Date: Fri, 21 Mar 2014 01:22:54 -0500 Subject: [PATCH 3/4] Fixed header include guard to match filename --- components/esm/weatherstate.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/esm/weatherstate.hpp b/components/esm/weatherstate.hpp index 545751225..d0cd59c16 100644 --- a/components/esm/weatherstate.hpp +++ b/components/esm/weatherstate.hpp @@ -1,5 +1,5 @@ -#ifndef OPENMW_ESM_WEATHER_H -#define OPENMW_ESM_WEATHER_H +#ifndef OPENMW_ESM_WEATHERSTATE_H +#define OPENMW_ESM_WEATHERSTATE_H #include From 6dce3e51864e9fc8799690c132b5e7d4e868ddf2 Mon Sep 17 00:00:00 2001 From: slothlife Date: Fri, 21 Mar 2014 01:33:11 -0500 Subject: [PATCH 4/4] Fixed include to use C99 header, not C++11 header --- apps/openmw/mwworld/weather.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 3167a64c2..cad3a4492 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -1,7 +1,7 @@ #ifndef GAME_MWWORLD_WEATHER_H #define GAME_MWWORLD_WEATHER_H -#include +#include #include #include