diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index a6bd85b1d7..a6b68df86d 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -67,7 +67,7 @@ add_openmw_dir (mwworld actionequip timestamp actionalchemy cellstore actionapply actioneat store esmstore recordcmp fallback actionrepair actionsoulgem livecellref actiondoor contentloader esmloader actiontrap cellreflist cellref physicssystem weather projectilemanager - cellpreloader + cellpreloader datetimemanager ) add_openmw_dir (mwphysics diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 6bf4cbaaeb..627e2f2b72 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -48,6 +48,7 @@ namespace ESM struct EffectList; struct CreatureLevList; struct ItemLevList; + struct TimeStamp; } namespace MWRender @@ -204,24 +205,14 @@ namespace MWBase virtual void advanceTime (double hours, bool incremental = false) = 0; ///< Advance in-game time. - virtual void setHour (double hour) = 0; - ///< Set in-game time hour. - - virtual void setMonth (int month) = 0; - ///< Set in-game time month. - - virtual void setDay (int day) = 0; - ///< Set in-game time day. - - virtual int getDay() const = 0; - virtual int getMonth() const = 0; - virtual int getYear() const = 0; - virtual std::string getMonthName (int month = -1) const = 0; ///< Return name of month (-1: current month) virtual MWWorld::TimeStamp getTimeStamp() const = 0; - ///< Return current in-game time stamp. + ///< Return current in-game time and number of day since new game start. + + virtual ESM::EpochTimeStamp getEpochTimeStamp() const = 0; + ///< Return current in-game date and time. virtual bool toggleSky() = 0; ///< \return Resulting mode diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 64f9122986..18cc187c15 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -150,11 +150,10 @@ namespace MWGui if (hour >= 13) hour -= 12; if (hour == 0) hour = 12; - std::string dateTimeText = - MyGUI::utility::toString(MWBase::Environment::get().getWorld ()->getDay ()) + " " - + month + " (#{sDay} " + MyGUI::utility::toString(MWBase::Environment::get().getWorld ()->getTimeStamp ().getDay()) - + ") " + MyGUI::utility::toString(hour) + " " + (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}"); - + ESM::EpochTimeStamp currentDate = MWBase::Environment::get().getWorld()->getEpochTimeStamp(); + int daysPassed = MWBase::Environment::get().getWorld()->getTimeStamp().getDay(); + std::string formattedHour = pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}"; + std::string dateTimeText = Misc::StringUtils::format("%i %s (#{sDay} %i) %i %s", currentDate.mDay, month, daysPassed, hour, formattedHour); mDateTimeText->setCaptionWithReplacing (dateTimeText); } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index db83f72c16..9974b8f160 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -213,11 +213,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot profile.mPlayerClassId = classId; profile.mPlayerCell = world.getCellName(); - - profile.mInGameTime.mGameHour = world.getTimeStamp().getHour(); - profile.mInGameTime.mDay = world.getDay(); - profile.mInGameTime.mMonth = world.getMonth(); - profile.mInGameTime.mYear = world.getYear(); + profile.mInGameTime = world.getEpochTimeStamp(); profile.mTimePlayed = mTimePlayed; profile.mDescription = description; diff --git a/apps/openmw/mwworld/datetimemanager.cpp b/apps/openmw/mwworld/datetimemanager.cpp new file mode 100644 index 0000000000..0894c974d3 --- /dev/null +++ b/apps/openmw/mwworld/datetimemanager.cpp @@ -0,0 +1,227 @@ +#include "datetimemanager.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "esmstore.hpp" +#include "globals.hpp" +#include "timestamp.hpp" + +namespace +{ + static int getDaysPerMonth(int month) + { + switch (month) + { + case 0: return 31; + case 1: return 28; + case 2: return 31; + case 3: return 30; + case 4: return 31; + case 5: return 30; + case 6: return 31; + case 7: return 31; + case 8: return 30; + case 9: return 31; + case 10: return 30; + case 11: return 31; + } + + throw std::runtime_error ("month out of range"); + } +} + +namespace MWWorld +{ + void DateTimeManager::setup(Globals& globalVariables) + { + mGameHour = globalVariables["gamehour"].getFloat(); + mDaysPassed = globalVariables["dayspassed"].getInteger(); + mDay = globalVariables["day"].getInteger(); + mMonth = globalVariables["month"].getInteger(); + mYear = globalVariables["year"].getInteger(); + mTimeScale = globalVariables["timescale"].getFloat(); + } + + void DateTimeManager::setHour(double hour) + { + if (hour < 0) + hour = 0; + + int days = static_cast(hour / 24); + hour = std::fmod(hour, 24); + mGameHour = static_cast(hour); + + if (days > 0) + setDay(days + mDay); + } + + void DateTimeManager::setDay(int day) + { + if (day < 1) + day = 1; + + int month = mMonth; + while (true) + { + int days = getDaysPerMonth(month); + if (day <= days) + break; + + if (month < 11) + { + ++month; + } + else + { + month = 0; + mYear++; + } + + day -= days; + } + + mDay = day; + mMonth = month; + } + + TimeStamp DateTimeManager::getTimeStamp() const + { + return TimeStamp(mGameHour, mDaysPassed); + } + + float DateTimeManager::getTimeScaleFactor() const + { + return mTimeScale; + } + + ESM::EpochTimeStamp DateTimeManager::getEpochTimeStamp() const + { + ESM::EpochTimeStamp timeStamp; + timeStamp.mGameHour = mGameHour; + timeStamp.mDay = mDay; + timeStamp.mMonth = mMonth; + timeStamp.mYear = mYear; + return timeStamp; + } + + void DateTimeManager::setMonth(int month) + { + if (month < 0) + month = 0; + + int years = month / 12; + month = month % 12; + + int days = getDaysPerMonth(month); + if (mDay > days) + mDay = days; + + mMonth = month; + + if (years > 0) + mYear += years; + } + + void DateTimeManager::advanceTime(double hours, Globals& globalVariables) + { + hours += mGameHour; + setHour(hours); + + int days = static_cast(hours / 24); + if (days > 0) + mDaysPassed += days; + + globalVariables["gamehour"].setFloat(mGameHour); + globalVariables["dayspassed"].setInteger(mDaysPassed); + globalVariables["day"].setInteger(mDay); + globalVariables["month"].setInteger(mMonth); + globalVariables["year"].setInteger(mYear); + } + + std::string DateTimeManager::getMonthName(int month) const + { + if (month == -1) + month = mMonth; + + const int months = 12; + if (month < 0 || month >= months) + return std::string(); + + static const char *monthNames[months] = + { + "sMonthMorningstar", "sMonthSunsdawn", "sMonthFirstseed", "sMonthRainshand", + "sMonthSecondseed", "sMonthMidyear", "sMonthSunsheight", "sMonthLastseed", + "sMonthHeartfire", "sMonthFrostfall", "sMonthSunsdusk", "sMonthEveningstar" + }; + + const ESM::GameSetting *setting = MWBase::Environment::get().getWorld()->getStore().get().find(monthNames[month]); + return setting->mValue.getString(); + } + + bool DateTimeManager::updateGlobalFloat(const std::string& name, float value) + { + if (name=="gamehour") + { + setHour(value); + return true; + } + else if (name=="day") + { + setDay(static_cast(value)); + return true; + } + else if (name=="month") + { + setMonth(static_cast(value)); + return true; + } + else if (name=="year") + { + mYear = static_cast(value); + } + else if (name=="timescale") + { + mTimeScale = value; + } + else if (name=="dayspassed") + { + mDaysPassed = static_cast(value); + } + + return false; + } + + bool DateTimeManager::updateGlobalInt(const std::string& name, int value) + { + if (name=="gamehour") + { + setHour(static_cast(value)); + return true; + } + else if (name=="day") + { + setDay(value); + return true; + } + else if (name=="month") + { + setMonth(value); + return true; + } + else if (name=="year") + { + mYear = value; + } + else if (name=="timescale") + { + mTimeScale = static_cast(value); + } + else if (name=="dayspassed") + { + mDaysPassed = value; + } + + return false; + } +} diff --git a/apps/openmw/mwworld/datetimemanager.hpp b/apps/openmw/mwworld/datetimemanager.hpp new file mode 100644 index 0000000000..b460be746a --- /dev/null +++ b/apps/openmw/mwworld/datetimemanager.hpp @@ -0,0 +1,43 @@ +#ifndef GAME_MWWORLD_DATETIMEMANAGER_H +#define GAME_MWWORLD_DATETIMEMANAGER_H + +#include + +namespace ESM +{ + struct EpochTimeStamp; +} + +namespace MWWorld +{ + class Globals; + class TimeStamp; + + class DateTimeManager + { + int mDaysPassed = 0; + int mDay = 0; + int mMonth = 0; + int mYear = 0; + float mGameHour = 0.f; + float mTimeScale = 0.f; + + void setHour(double hour); + void setDay(int day); + void setMonth(int month); + + public: + std::string getMonthName(int month) const; + TimeStamp getTimeStamp() const; + ESM::EpochTimeStamp getEpochTimeStamp() const; + float getTimeScaleFactor() const; + + void advanceTime(double hours, Globals& globalVariables); + + void setup(Globals& globalVariables); + bool updateGlobalInt(const std::string& name, int value); + bool updateGlobalFloat(const std::string& name, float value); + }; +} + +#endif diff --git a/apps/openmw/mwworld/globals.cpp b/apps/openmw/mwworld/globals.cpp index 69ec5563bc..8a481334e8 100644 --- a/apps/openmw/mwworld/globals.cpp +++ b/apps/openmw/mwworld/globals.cpp @@ -2,10 +2,9 @@ #include -#include - #include #include +#include #include "esmstore.hpp" diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index aee98e7ea9..c51266bab6 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -60,6 +60,7 @@ #include "../mwphysics/object.hpp" #include "../mwphysics/constants.hpp" +#include "datetimemanager.hpp" #include "player.hpp" #include "manualref.hpp" #include "cellstore.hpp" @@ -121,33 +122,11 @@ namespace MWWorld LoadersContainer mLoaders; }; - int World::getDaysPerMonth (int month) const - { - switch (month) - { - case 0: return 31; - case 1: return 28; - case 2: return 31; - case 3: return 30; - case 4: return 31; - case 5: return 30; - case 6: return 31; - case 7: return 31; - case 8: return 30; - case 9: return 31; - case 10: return 30; - case 11: return 31; - } - - throw std::runtime_error ("month out of range"); - } - void World::adjustSky() { if (mSky && (isCellExterior() || isCellQuasiExterior())) { - mRendering->skySetDate (mDay->getInteger(), mMonth->getInteger()); - + updateSkyDate(); mRendering->setSkyEnabled(true); } else @@ -193,6 +172,8 @@ namespace MWWorld if (mEsm[0].getFormat() == 0) ensureNeededRecords(); + mCurrentDate.reset(new DateTimeManager()); + fillGlobalVariables(); mStore.setUp(true); @@ -227,13 +208,7 @@ namespace MWWorld void World::fillGlobalVariables() { mGlobalVariables.fill (mStore); - - mGameHour = &mGlobalVariables["gamehour"]; - mDaysPassed = &mGlobalVariables["dayspassed"]; - mDay = &mGlobalVariables["day"]; - mMonth = &mGlobalVariables["month"]; - mYear = &mGlobalVariables["year"]; - mTimeScale = &mGlobalVariables["timescale"]; + mCurrentDate->setup(mGlobalVariables); } void World::startNewGame (bool bypass) @@ -310,6 +285,7 @@ namespace MWWorld mPhysics->toggleCollisionMode(); MWBase::Environment::get().getWindowManager()->updatePlayer(); + mCurrentDate->setup(mGlobalVariables); } void World::clear() @@ -639,26 +615,20 @@ namespace MWWorld void World::setGlobalInt (const std::string& name, int value) { - if (name=="gamehour") - setHour (value); - else if (name=="day") - setDay (value); - else if (name=="month") - setMonth (value); - else - mGlobalVariables[name].setInteger (value); + bool dateUpdated = mCurrentDate->updateGlobalInt(name, value); + if (dateUpdated) + updateSkyDate(); + + mGlobalVariables[name].setInteger (value); } void World::setGlobalFloat (const std::string& name, float value) { - if (name=="gamehour") - setHour (value); - else if (name=="day") - setDay(static_cast(value)); - else if (name=="month") - setMonth(static_cast(value)); - else - mGlobalVariables[name].setFloat (value); + bool dateUpdated = mCurrentDate->updateGlobalFloat(name, value); + if (dateUpdated) + updateSkyDate(); + + mGlobalVariables[name].setFloat(value); } int World::getGlobalInt (const std::string& name) const @@ -676,6 +646,11 @@ namespace MWWorld return mGlobalVariables.getType (name); } + std::string World::getMonthName (int month) const + { + return mCurrentDate->getMonthName(month); + } + std::string World::getCellName (const MWWorld::CellStore *cell) const { if (!cell) @@ -894,130 +869,29 @@ namespace MWWorld } mWeatherManager->advanceTime (hours, incremental); + mCurrentDate->advanceTime(hours, mGlobalVariables); + updateSkyDate(); if (!incremental) { mRendering->notifyWorldSpaceChanged(); mProjectileManager->clear(); } - - hours += mGameHour->getFloat(); - - setHour (hours); - - int days = static_cast(hours / 24); - - if (days>0) - mDaysPassed->setInteger ( - days + mDaysPassed->getInteger()); } - void World::setHour (double hour) + float World::getTimeScaleFactor() const { - if (hour<0) - hour = 0; - - int days = static_cast(hour / 24); - - hour = std::fmod (hour, 24); - - mGameHour->setFloat(static_cast(hour)); - - if (days>0) - setDay (days + mDay->getInteger()); - } - - void World::setDay (int day) - { - if (day<1) - day = 1; - - int month = mMonth->getInteger(); - - while (true) - { - int days = getDaysPerMonth (month); - if (day<=days) - break; - - if (month<11) - { - ++month; - } - else - { - month = 0; - mYear->setInteger(mYear->getInteger()+1); - } - - day -= days; - } - - mDay->setInteger(day); - mMonth->setInteger(month); - - mRendering->skySetDate(day, month); - } - - void World::setMonth (int month) - { - if (month<0) - month = 0; - - int years = month / 12; - month = month % 12; - - int days = getDaysPerMonth (month); - - if (mDay->getInteger()>days) - mDay->setInteger (days); - - mMonth->setInteger (month); - - if (years>0) - mYear->setInteger (years+mYear->getInteger()); - - mRendering->skySetDate (mDay->getInteger(), month); - } - - int World::getDay() const - { - return mDay->getInteger(); - } - - int World::getMonth() const - { - return mMonth->getInteger(); - } - - int World::getYear() const - { - return mYear->getInteger(); - } - - std::string World::getMonthName (int month) const - { - if (month==-1) - month = getMonth(); - - const int months = 12; - - if (month<0 || month>=months) - return ""; - - static const char *monthNames[months] = - { - "sMonthMorningstar", "sMonthSunsdawn", "sMonthFirstseed", "sMonthRainshand", - "sMonthSecondseed", "sMonthMidyear", "sMonthSunsheight", "sMonthLastseed", - "sMonthHeartfire", "sMonthFrostfall", "sMonthSunsdusk", "sMonthEveningstar" - }; - - return mStore.get().find (monthNames[month])->mValue.getString(); + return mCurrentDate->getTimeScaleFactor(); } TimeStamp World::getTimeStamp() const { - return TimeStamp (mGameHour->getFloat(), mDaysPassed->getInteger()); + return mCurrentDate->getTimeStamp(); + } + + ESM::EpochTimeStamp World::getEpochTimeStamp() const + { + return mCurrentDate->getEpochTimeStamp(); } bool World::toggleSky() @@ -1042,11 +916,6 @@ namespace MWWorld mRendering->skySetMoonColour (red); } - float World::getTimeScaleFactor() const - { - return mTimeScale->getFloat(); - } - void World::changeToInteriorCell (const std::string& cellName, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent) { mPhysics->clearQueuedMovement(); @@ -1089,6 +958,8 @@ namespace MWWorld changeToExteriorCell (position, adjustPlayerPos, changeEvent); else changeToInteriorCell (cellId.mWorldspace, position, adjustPlayerPos, changeEvent); + + mCurrentDate->setup(mGlobalVariables); } void World::markCellAsUnchanged() @@ -3968,4 +3839,10 @@ namespace MWWorld mNavigator->reportStats(frameNumber, stats); mPhysics->reportStats(frameNumber, stats); } + + void World::updateSkyDate() + { + ESM::EpochTimeStamp currentDate = mCurrentDate->getEpochTimeStamp(); + mRendering->skySetDate(currentDate.mDay, currentDate.mMonth); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index fa2f7778ba..aeb6bbae40 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -69,6 +69,7 @@ namespace MWPhysics namespace MWWorld { + class DateTimeManager; class WeatherManager; class Player; class ProjectileManager; @@ -85,13 +86,6 @@ namespace MWWorld LocalScripts mLocalScripts; MWWorld::Globals mGlobalVariables; - ESM::Variant* mGameHour; - ESM::Variant* mDaysPassed; - ESM::Variant* mDay; - ESM::Variant* mMonth; - ESM::Variant* mYear; - ESM::Variant* mTimeScale; - Cells mCells; std::string mCurrentWorldSpace; @@ -102,6 +96,7 @@ namespace MWWorld std::unique_ptr mRendering; std::unique_ptr mWorldScene; std::unique_ptr mWeatherManager; + std::unique_ptr mCurrentDate; std::shared_ptr mProjectileManager; bool mSky; @@ -139,7 +134,6 @@ namespace MWWorld World& operator= (const World&); void updateWeather(float duration, bool paused = false); - int getDaysPerMonth (int month) const; void rotateObjectImp (const Ptr& ptr, const osg::Vec3f& rot, MWBase::RotationFlags flags); @@ -173,6 +167,8 @@ namespace MWWorld void fillGlobalVariables(); + void updateSkyDate(); + /** * @brief loadContentFiles - Loads content files (esm,esp,omwgame,omwaddon) * @param fileCollections- Container which holds content file names and their paths @@ -318,24 +314,14 @@ namespace MWWorld void advanceTime (double hours, bool incremental = false) override; ///< Advance in-game time. - void setHour (double hour) override; - ///< Set in-game time hour. - - void setMonth (int month) override; - ///< Set in-game time month. - - void setDay (int day) override; - ///< Set in-game time day. - - int getDay() const override; - int getMonth() const override; - int getYear() const override; - std::string getMonthName (int month = -1) const override; ///< Return name of month (-1: current month) TimeStamp getTimeStamp() const override; - ///< Return current in-game time stamp. + ///< Return current in-game time and number of day since new game start. + + ESM::EpochTimeStamp getEpochTimeStamp() const override; + ///< Return current in-game date and time. bool toggleSky() override; ///< \return Resulting mode diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 0f0478faab..6c0c33526e 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -14,6 +14,14 @@ struct TimeStamp int mDay; }; +struct EpochTimeStamp +{ + float mGameHour; + int mDay; + int mMonth; + int mYear; +}; + // Pixel color value. Standard four-byte rr,gg,bb,aa format. typedef uint32_t Color; diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index cda4113140..b5bf6e4066 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -2,7 +2,6 @@ #include "esmreader.hpp" #include "esmwriter.hpp" -#include "defs.hpp" unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE; int ESM::SavedGame::sCurrentFormat = 10; diff --git a/components/esm/savedgame.hpp b/components/esm/savedgame.hpp index aa0429657b..26efae824e 100644 --- a/components/esm/savedgame.hpp +++ b/components/esm/savedgame.hpp @@ -4,6 +4,8 @@ #include #include +#include "defs.hpp" + namespace ESM { class ESMReader; @@ -17,14 +19,6 @@ namespace ESM static int sCurrentFormat; - struct TimeStamp - { - float mGameHour; - int mDay; - int mMonth; - int mYear; - }; - std::vector mContentFiles; std::string mPlayerName; int mPlayerLevel; @@ -36,7 +30,7 @@ namespace ESM std::string mPlayerClassName; std::string mPlayerCell; - TimeStamp mInGameTime; + EpochTimeStamp mInGameTime; double mTimePlayed; std::string mDescription; std::vector mScreenshot; // raw jpg-encoded data