You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openmw-tes3mp/apps/openmw/mwworld/weather.cpp

786 lines
28 KiB
C++

#include "weather.hpp"
13 years ago
#include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwrender/renderingmanager.hpp"
#include "player.hpp"
#include "esmstore.hpp"
12 years ago
#include "fallback.hpp"
#include "cellstore.hpp"
using namespace Ogre;
using namespace MWWorld;
13 years ago
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)
{
return x * (1-factor) + y * factor;
}
Ogre::ColourValue lerp (const Ogre::ColourValue& x, const Ogre::ColourValue& y, float factor)
{
return x * (1-factor) + y * factor;
}
}
void WeatherManager::setFallbackWeather(Weather& weather,const std::string& name)
12 years ago
{
std::string upper=name;
upper[0]=toupper(name[0]);
weather.mCloudsMaximumPercent = mFallback->getFallbackFloat("Weather_"+upper+"_Clouds_Maximum_Percent");
weather.mTransitionDelta = mFallback->getFallbackFloat("Weather_"+upper+"_Transition_Delta");
weather.mSkySunriseColor=mFallback->getFallbackColour("Weather_"+upper+"_Sky_Sunrise_Color");
weather.mSkyDayColor = mFallback->getFallbackColour("Weather_"+upper+"_Sky_Day_Color");
weather.mSkySunsetColor = mFallback->getFallbackColour("Weather_"+upper+"_Sky_Sunset_Color");
weather.mSkyNightColor = mFallback->getFallbackColour("Weather_"+upper+"_Sky_Night_Color");
weather.mFogSunriseColor = mFallback->getFallbackColour("Weather_"+upper+"_Fog_Sunrise_Color");
weather.mFogDayColor = mFallback->getFallbackColour("Weather_"+upper+"_Fog_Day_Color");
weather.mFogSunsetColor = mFallback->getFallbackColour("Weather_"+upper+"_Fog_Sunset_Color");
weather.mFogNightColor = mFallback->getFallbackColour("Weather_"+upper+"_Fog_Night_Color");
weather.mAmbientSunriseColor = mFallback->getFallbackColour("Weather_"+upper+"_Ambient_Sunrise_Color");
weather.mAmbientDayColor = mFallback->getFallbackColour("Weather_"+upper+"_Ambient_Day_Color");
weather.mAmbientSunsetColor = mFallback->getFallbackColour("Weather_"+upper+"_Ambient_Sunset_Color");
weather.mAmbientNightColor = mFallback->getFallbackColour("Weather_"+upper+"_Ambient_Night_Color");
weather.mSunSunriseColor = mFallback->getFallbackColour("Weather_"+upper+"_Sun_Sunrise_Color");
weather.mSunDayColor = mFallback->getFallbackColour("Weather_"+upper+"_Sun_Day_Color");
weather.mSunSunsetColor = mFallback->getFallbackColour("Weather_"+upper+"_Sun_Sunset_Color");
weather.mSunNightColor = mFallback->getFallbackColour("Weather_"+upper+"_Sun_Night_Color");
weather.mSunDiscSunsetColor = mFallback->getFallbackColour("Weather_"+upper+"_Sun_Disc_Sunset_Color");
weather.mLandFogDayDepth = mFallback->getFallbackFloat("Weather_"+upper+"_Land_Fog_Day_Depth");
weather.mLandFogNightDepth = mFallback->getFallbackFloat("Weather_"+upper+"_Land_Fog_Night_Depth");
weather.mWindSpeed = mFallback->getFallbackFloat("Weather_"+upper+"_Wind_Speed");
weather.mCloudSpeed = mFallback->getFallbackFloat("Weather_"+upper+"_Cloud_Speed");
weather.mGlareView = mFallback->getFallbackFloat("Weather_"+upper+"_Glare_View");
mWeatherSettings[name] = weather;
12 years ago
}
13 years ago
12 years ago
float WeatherManager::calculateHourFade (const std::string& moonName) const
{
float fadeOutStart=mFallback->getFallbackFloat("Moons_"+moonName+"_Fade_Out_Start");
float fadeOutFinish=mFallback->getFallbackFloat("Moons_"+moonName+"_Fade_Out_Finish");
float fadeInStart=mFallback->getFallbackFloat("Moons_"+moonName+"_Fade_In_Start");
float fadeInFinish=mFallback->getFallbackFloat("Moons_"+moonName+"_Fade_In_Finish");
if (mHour >= fadeOutStart && mHour <= fadeOutFinish)
return (1 - ((mHour - fadeOutStart) / (fadeOutFinish - fadeOutStart)));
if (mHour >= fadeInStart && mHour <= fadeInFinish)
return (1 - ((mHour - fadeInStart) / (fadeInFinish - fadeInStart)));
else
return 1;
}
12 years ago
float WeatherManager::calculateAngleFade (const std::string& moonName, float angle) const
{
float endAngle=mFallback->getFallbackFloat("Moons_"+moonName+"_Fade_End_Angle");
float startAngle=mFallback->getFallbackFloat("Moons_"+moonName+"_Fade_Start_Angle");
if (angle <= startAngle && angle >= endAngle)
return (1 - ((angle - endAngle)/(startAngle-endAngle)));
else if (angle > startAngle)
return 0.f;
else
return 1.f;
}
WeatherManager::WeatherManager(MWRender::RenderingManager* rendering,MWWorld::Fallback* fallback) :
mHour(14), mCurrentWeather("clear"), mNextWeather(""), mFirstUpdate(true),
mWeatherUpdateTime(0), mThunderFlash(0), mThunderChance(0),
mThunderChanceNeeded(50), mThunderSoundDelay(0), mRemainingTransitionTime(0),
mTimePassed(0), mFallback(fallback), mWindSpeed(0.f), mRendering(rendering)
{
//Globals
mThunderSoundID0 = mFallback->getFallbackString("Weather_Thunderstorm_Thunder_Sound_ID_0");
mThunderSoundID1 = mFallback->getFallbackString("Weather_Thunderstorm_Thunder_Sound_ID_1");
mThunderSoundID2 = mFallback->getFallbackString("Weather_Thunderstorm_Thunder_Sound_ID_2");
mThunderSoundID3 = mFallback->getFallbackString("Weather_Thunderstorm_Thunder_Sound_ID_3");
mSunriseTime = mFallback->getFallbackFloat("Weather_Sunrise_Time");
mSunsetTime = mFallback->getFallbackFloat("Weather_Sunset_Time");
mSunriseDuration = mFallback->getFallbackFloat("Weather_Sunrise_Duration");
mSunsetDuration = mFallback->getFallbackFloat("Weather_Sunset_Duration");
mHoursBetweenWeatherChanges = mFallback->getFallbackFloat("Weather_Hours_Between_Weather_Changes");
mWeatherUpdateTime = mHoursBetweenWeatherChanges * 3600;
mThunderFrequency = mFallback->getFallbackFloat("Weather_Thunderstorm_Thunder_Frequency");
mThunderThreshold = mFallback->getFallbackFloat("Weather_Thunderstorm_Thunder_Threshold");
mThunderSoundDelay = 0.25;
//Some useful values
/* TODO: Use pre-sunrise_time, pre-sunset_time,
* post-sunrise_time, and post-sunset_time to better
* describe sunrise/sunset time.
* These values are fallbacks attached to weather.
*/
mNightStart = mSunsetTime + mSunsetDuration;
mNightEnd = mSunriseTime - 0.5;
mDayStart = mSunriseTime + mSunriseDuration;
mDayEnd = mSunsetTime;
//Weather
Weather clear;
clear.mCloudTexture = "tx_sky_clear.dds";
setFallbackWeather(clear,"clear");
Weather cloudy;
cloudy.mCloudTexture = "tx_sky_cloudy.dds";
setFallbackWeather(cloudy,"cloudy");
Weather foggy;
foggy.mCloudTexture = "tx_sky_foggy.dds";
setFallbackWeather(foggy,"foggy");
13 years ago
Weather thunderstorm;
thunderstorm.mCloudTexture = "tx_sky_thunder.dds";
thunderstorm.mRainLoopSoundID = "rain heavy";
setFallbackWeather(thunderstorm,"thunderstorm");
Weather rain;
rain.mCloudTexture = "tx_sky_rainy.dds";
rain.mRainLoopSoundID = "rain";
setFallbackWeather(rain,"rain");
Weather overcast;
overcast.mCloudTexture = "tx_sky_overcast.dds";
setFallbackWeather(overcast,"overcast");
Weather ashstorm;
ashstorm.mCloudTexture = "tx_sky_ashstorm.dds";
ashstorm.mAmbientLoopSoundID = "ashstorm";
setFallbackWeather(ashstorm,"ashstorm");
Weather blight;
blight.mCloudTexture = "tx_sky_blight.dds";
blight.mAmbientLoopSoundID = "blight";
setFallbackWeather(blight,"blight");
Weather snow;
snow.mCloudTexture = "tx_bm_sky_snow.dds";
setFallbackWeather(snow, "snow");
Weather blizzard;
blizzard.mCloudTexture = "tx_bm_sky_blizzard.dds";
blizzard.mAmbientLoopSoundID = "BM Blizzard";
setFallbackWeather(blizzard,"blizzard");
}
WeatherManager::~WeatherManager()
{
stopSounds(true);
}
void WeatherManager::setWeather(const String& weather, bool instant)
{
if (weather == mCurrentWeather && mNextWeather == "")
13 years ago
{
mFirstUpdate = false;
return;
13 years ago
}
if (instant || mFirstUpdate)
{
mNextWeather = "";
mCurrentWeather = weather;
}
else
{
if (mNextWeather != "")
13 years ago
{
// transition more than 50% finished?
if (mRemainingTransitionTime/(mWeatherSettings[mCurrentWeather].mTransitionDelta * 24.f * 3600) <= 0.5)
13 years ago
mCurrentWeather = mNextWeather;
}
mNextWeather = weather;
mRemainingTransitionTime = mWeatherSettings[mCurrentWeather].mTransitionDelta * 24.f * 3600.f;
}
mFirstUpdate = false;
}
void WeatherManager::setResult(const String& weatherType)
{
const Weather& current = mWeatherSettings[weatherType];
mResult.mCloudTexture = current.mCloudTexture;
mResult.mCloudBlendFactor = 0;
mResult.mCloudOpacity = current.mCloudsMaximumPercent;
mResult.mWindSpeed = current.mWindSpeed;
mResult.mCloudSpeed = current.mCloudSpeed;
mResult.mGlareView = current.mGlareView;
mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID;
mResult.mSunColor = current.mSunDiscSunsetColor;
mResult.mNight = (mHour < mSunriseTime || mHour > mNightStart - 1);
13 years ago
mResult.mFogDepth = mResult.mNight ? current.mLandFogNightDepth : current.mLandFogDayDepth;
13 years ago
// night
if (mHour <= mNightEnd || mHour >= mNightStart + 1)
{
mResult.mFogColor = current.mFogNightColor;
mResult.mAmbientColor = current.mAmbientNightColor;
mResult.mSunColor = current.mSunNightColor;
mResult.mSkyColor = current.mSkyNightColor;
mResult.mNightFade = 1.f;
}
13 years ago
// sunrise
else if (mHour >= mNightEnd && mHour <= mDayStart + 1)
{
if (mHour <= mSunriseTime)
{
// fade in
float advance = mSunriseTime - mHour;
13 years ago
float factor = advance / 0.5f;
mResult.mFogColor = lerp(current.mFogSunriseColor, current.mFogNightColor, factor);
mResult.mAmbientColor = lerp(current.mAmbientSunriseColor, current.mAmbientNightColor, factor);
mResult.mSunColor = lerp(current.mSunSunriseColor, current.mSunNightColor, factor);
mResult.mSkyColor = lerp(current.mSkySunriseColor, current.mSkyNightColor, factor);
mResult.mNightFade = factor;
}
13 years ago
else //if (mHour >= 6)
{
// fade out
float advance = mHour - mSunriseTime;
13 years ago
float factor = advance / 3.f;
mResult.mFogColor = lerp(current.mFogSunriseColor, current.mFogDayColor, factor);
mResult.mAmbientColor = lerp(current.mAmbientSunriseColor, current.mAmbientDayColor, factor);
mResult.mSunColor = lerp(current.mSunSunriseColor, current.mSunDayColor, factor);
mResult.mSkyColor = lerp(current.mSkySunriseColor, current.mSkyDayColor, factor);
}
}
13 years ago
// day
else if (mHour >= mDayStart + 1 && mHour <= mDayEnd - 1)
{
mResult.mFogColor = current.mFogDayColor;
mResult.mAmbientColor = current.mAmbientDayColor;
mResult.mSunColor = current.mSunDayColor;
mResult.mSkyColor = current.mSkyDayColor;
}
13 years ago
// sunset
else if (mHour >= mDayEnd - 1 && mHour <= mNightStart + 1)
{
if (mHour <= mDayEnd + 1)
{
// fade in
float advance = (mDayEnd + 1) - mHour;
13 years ago
float factor = (advance / 2);
mResult.mFogColor = lerp(current.mFogSunsetColor, current.mFogDayColor, factor);
mResult.mAmbientColor = lerp(current.mAmbientSunsetColor, current.mAmbientDayColor, factor);
mResult.mSunColor = lerp(current.mSunSunsetColor, current.mSunDayColor, factor);
mResult.mSkyColor = lerp(current.mSkySunsetColor, current.mSkyDayColor, factor);
}
13 years ago
else //if (mHour >= 19)
{
// fade out
float advance = mHour - (mDayEnd + 1);
13 years ago
float factor = advance / 2.f;
mResult.mFogColor = lerp(current.mFogSunsetColor, current.mFogNightColor, factor);
mResult.mAmbientColor = lerp(current.mAmbientSunsetColor, current.mAmbientNightColor, factor);
mResult.mSunColor = lerp(current.mSunSunsetColor, current.mSunNightColor, factor);
mResult.mSkyColor = lerp(current.mSkySunsetColor, current.mSkyNightColor, factor);
mResult.mNightFade = factor;
}
}
}
void WeatherManager::transition(float factor)
{
setResult(mCurrentWeather);
const WeatherResult current = mResult;
setResult(mNextWeather);
const WeatherResult other = mResult;
mResult.mCloudTexture = current.mCloudTexture;
mResult.mNextCloudTexture = other.mCloudTexture;
mResult.mCloudBlendFactor = factor;
mResult.mCloudOpacity = lerp(current.mCloudOpacity, other.mCloudOpacity, factor);
mResult.mFogColor = lerp(current.mFogColor, other.mFogColor, factor);
mResult.mSunColor = lerp(current.mSunColor, other.mSunColor, factor);
mResult.mSkyColor = lerp(current.mSkyColor, other.mSkyColor, factor);
mResult.mAmbientColor = lerp(current.mAmbientColor, other.mAmbientColor, factor);
mResult.mSunDiscColor = lerp(current.mSunDiscColor, other.mSunDiscColor, factor);
mResult.mFogDepth = lerp(current.mFogDepth, other.mFogDepth, factor);
mResult.mWindSpeed = lerp(current.mWindSpeed, other.mWindSpeed, factor);
mResult.mCloudSpeed = lerp(current.mCloudSpeed, other.mCloudSpeed, factor);
mResult.mCloudOpacity = lerp(current.mCloudOpacity, other.mCloudOpacity, factor);
mResult.mGlareView = lerp(current.mGlareView, other.mGlareView, factor);
mResult.mNightFade = lerp(current.mNightFade, other.mNightFade, factor);
mResult.mNight = current.mNight;
}
void WeatherManager::update(float duration)
{
float timePassed = mTimePassed;
mTimePassed = 0;
mWeatherUpdateTime -= timePassed;
MWBase::World* world = MWBase::Environment::get().getWorld();
const bool exterior = (world->isCellExterior() || world->isCellQuasiExterior());
if (!exterior)
{
mRendering->sunDisable(false);
mRendering->skyDisable();
mRendering->getSkyManager()->setLightningStrength(0.f);
stopSounds(true);
return;
}
switchToNextWeather(false);
if (mNextWeather != "")
{
mRemainingTransitionTime -= timePassed;
if (mRemainingTransitionTime < 0)
{
mCurrentWeather = mNextWeather;
mNextWeather = "";
}
}
13 years ago
if (mNextWeather != "")
transition(1 - (mRemainingTransitionTime / (mWeatherSettings[mCurrentWeather].mTransitionDelta * 24.f * 3600)));
else
setResult(mCurrentWeather);
mWindSpeed = mResult.mWindSpeed;
12 years ago
mRendering->configureFog(mResult.mFogDepth, mResult.mFogColor);
13 years ago
// disable sun during night
if (mHour >= mNightStart || mHour <= mSunriseTime)
mRendering->getSkyManager()->sunDisable();
else
mRendering->getSkyManager()->sunEnable();
// sun angle
float height;
//Day duration
float dayDuration = (mNightStart - 1) - mSunriseTime;
// rise at 6, set at 20
if (mHour >= mSunriseTime && mHour <= mNightStart)
height = 1 - std::abs(((mHour - dayDuration) / 7.f));
else if (mHour > mNightStart)
height = (mHour - mNightStart) / 4.f;
else //if (mHour > 0 && mHour < 6)
height = 1 - (mHour / mSunriseTime);
int facing = (mHour > 13.f) ? 1 : -1;
Vector3 final(
(height - 1) * facing,
(height - 1) * facing,
height);
mRendering->setSunDirection(final);
13 years ago
/*
* TODO: import separated fadeInStart/Finish, fadeOutStart/Finish
* for masser and secunda
*/
13 years ago
float fadeOutFinish=mFallback->getFallbackFloat("Moons_Masser_Fade_Out_Finish");
float fadeInStart=mFallback->getFallbackFloat("Moons_Masser_Fade_In_Start");
//moon calculations
float moonHeight;
if (mHour >= fadeInStart)
moonHeight = mHour - fadeInStart;
else if (mHour <= fadeOutFinish)
moonHeight = mHour + fadeOutFinish;
else
moonHeight = 0;
moonHeight /= (24.f - (fadeInStart - fadeOutFinish));
if (moonHeight != 0)
{
int facing = (moonHeight <= 1) ? 1 : -1;
Vector3 masser(
(moonHeight - 1) * facing,
(1 - moonHeight) * facing,
moonHeight);
13 years ago
Vector3 secunda(
(moonHeight - 1) * facing * 1.25,
(1 - moonHeight) * facing * 0.8,
moonHeight);
13 years ago
mRendering->getSkyManager()->setMasserDirection(masser);
mRendering->getSkyManager()->setSecundaDirection(secunda);
mRendering->getSkyManager()->masserEnable();
mRendering->getSkyManager()->secundaEnable();
13 years ago
float angle = (1-moonHeight) * 90.f * facing;
float masserHourFade = calculateHourFade("Masser");
float secundaHourFade = calculateHourFade("Secunda");
float masserAngleFade = calculateAngleFade("Masser", angle);
float secundaAngleFade = calculateAngleFade("Secunda", angle);
13 years ago
masserAngleFade *= masserHourFade;
secundaAngleFade *= secundaHourFade;
13 years ago
mRendering->getSkyManager()->setMasserFade(masserAngleFade);
mRendering->getSkyManager()->setSecundaFade(secundaAngleFade);
}
else
{
mRendering->getSkyManager()->masserDisable();
mRendering->getSkyManager()->secundaDisable();
}
13 years ago
if (mCurrentWeather == "thunderstorm" && mNextWeather == "")
{
if (mThunderFlash > 0)
13 years ago
{
// play the sound after a delay
mThunderSoundDelay -= duration;
if (mThunderSoundDelay <= 0)
13 years ago
{
// pick a random sound
int sound = rand() % 4;
std::string* soundName = NULL;
if (sound == 0) soundName = &mThunderSoundID0;
else if (sound == 1) soundName = &mThunderSoundID1;
else if (sound == 2) soundName = &mThunderSoundID2;
else if (sound == 3) soundName = &mThunderSoundID3;
MWBase::Environment::get().getSoundManager()->playSound(*soundName, 1.0, 1.0);
mThunderSoundDelay = 1000;
13 years ago
}
mThunderFlash -= duration;
if (mThunderFlash > 0)
mRendering->getSkyManager()->setLightningStrength( mThunderFlash / mThunderThreshold );
13 years ago
else
{
mThunderChanceNeeded = rand() % 100;
mThunderChance = 0;
mRendering->getSkyManager()->setLightningStrength( 0.f );
13 years ago
}
}
13 years ago
else
{
// no thunder active
mThunderChance += duration*4; // chance increases by 4 percent every second
if (mThunderChance >= mThunderChanceNeeded)
{
mThunderFlash = mThunderThreshold;
13 years ago
mRendering->getSkyManager()->setLightningStrength( mThunderFlash / mThunderThreshold );
13 years ago
mThunderSoundDelay = 0.25;
}
}
}
else
mRendering->getSkyManager()->setLightningStrength(0.f);
mRendering->setAmbientColour(mResult.mAmbientColor);
mRendering->sunEnable(false);
mRendering->setSunColour(mResult.mSunColor);
mRendering->getSkyManager()->setWeather(mResult);
// Play sounds
if (mNextWeather == "")
{
std::string ambientSnd = mWeatherSettings[mCurrentWeather].mAmbientLoopSoundID;
if (!ambientSnd.empty() && std::find(mSoundsPlaying.begin(), mSoundsPlaying.end(), ambientSnd) == mSoundsPlaying.end())
{
mSoundsPlaying.push_back(ambientSnd);
MWBase::Environment::get().getSoundManager()->playSound(ambientSnd, 1.0, 1.0, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop);
}
std::string rainSnd = mWeatherSettings[mCurrentWeather].mRainLoopSoundID;
if (!rainSnd.empty() && std::find(mSoundsPlaying.begin(), mSoundsPlaying.end(), rainSnd) == mSoundsPlaying.end())
{
mSoundsPlaying.push_back(rainSnd);
MWBase::Environment::get().getSoundManager()->playSound(rainSnd, 1.0, 1.0, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop);
}
}
stopSounds(false);
}
void WeatherManager::stopSounds(bool stopAll)
{
std::vector<std::string>::iterator it = mSoundsPlaying.begin();
while (it!=mSoundsPlaying.end())
{
if (stopAll ||
!((*it == mWeatherSettings[mCurrentWeather].mAmbientLoopSoundID) ||
(*it == mWeatherSettings[mCurrentWeather].mRainLoopSoundID)))
{
MWBase::Environment::get().getSoundManager()->stopSound(*it);
it = mSoundsPlaying.erase(it);
}
else
++it;
}
}
std::string WeatherManager::nextWeather(const ESM::Region* region) const
{
std::vector<char> probability;
RegionModMap::const_iterator iter = mRegionMods.find(Misc::StringUtils::lowerCase(region->mId));
if(iter != mRegionMods.end())
probability = iter->second;
else
{
probability.reserve(10);
probability.push_back(region->mData.mClear);
probability.push_back(region->mData.mCloudy);
probability.push_back(region->mData.mFoggy);
probability.push_back(region->mData.mOvercast);
probability.push_back(region->mData.mRain);
probability.push_back(region->mData.mThunder);
probability.push_back(region->mData.mAsh);
probability.push_back(region->mData.mBlight);
probability.push_back(region->mData.mA);
probability.push_back(region->mData.mB);
}
/*
* All probabilities must add to 100 (responsibility of the user).
* If chances A and B has values 30 and 70 then by generating
* 100 numbers 1..100, 30% will be lesser or equal 30 and
* 70% will be greater than 30 (in theory).
*/
int chance = (rand() % 100) + 1; // 1..100
int sum = 0;
unsigned int i = 0;
for (; i < probability.size(); ++i)
{
sum += probability[i];
if (chance < sum)
break;
}
switch (i)
{
case 1:
return "cloudy";
case 2:
return "foggy";
case 3:
return "overcast";
case 4:
return "rain";
case 5:
return "thunderstorm";
case 6:
return "ashstorm";
case 7:
return "blight";
case 8:
return "snow";
case 9:
return "blizzard";
default: // case 0
return "clear";
}
}
void WeatherManager::setHour(const float hour)
{
mHour = hour;
}
unsigned int WeatherManager::getWeatherID() const
{
// Source: http://www.uesp.net/wiki/Tes3Mod:GetCurrentWeather
if (mCurrentWeather == "clear")
return 0;
else if (mCurrentWeather == "cloudy")
return 1;
else if (mCurrentWeather == "foggy")
return 2;
else if (mCurrentWeather == "overcast")
return 3;
else if (mCurrentWeather == "rain")
return 4;
else if (mCurrentWeather == "thunderstorm")
return 5;
else if (mCurrentWeather == "ashstorm")
return 6;
else if (mCurrentWeather == "blight")
return 7;
else if (mCurrentWeather == "snow")
return 8;
else if (mCurrentWeather == "blizzard")
return 9;
else
return 0;
}
void WeatherManager::changeWeather(const std::string& region, const unsigned int id)
{
12 years ago
// make sure this region exists
MWBase::Environment::get().getWorld()->getStore().get<ESM::Region>().find(region);
12 years ago
std::string weather;
if (id==0)
weather = "clear";
else if (id==1)
weather = "cloudy";
else if (id==2)
weather = "foggy";
else if (id==3)
weather = "overcast";
else if (id==4)
weather = "rain";
else if (id==5)
weather = "thunderstorm";
else if (id==6)
weather = "ashstorm";
else if (id==7)
weather = "blight";
else if (id==8)
weather = "snow";
else if (id==9)
weather = "blizzard";
else
weather = "clear";
mRegionOverrides[Misc::StringUtils::lowerCase(region)] = weather;
12 years ago
std::string playerRegion = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell()->getCell()->mRegion;
12 years ago
if (Misc::StringUtils::ciEqual(region, playerRegion))
setWeather(weather);
}
12 years ago
void WeatherManager::modRegion(const std::string &regionid, const std::vector<char> &chances)
{
mRegionMods[Misc::StringUtils::lowerCase(regionid)] = chances;
// Start transitioning right away if the region no longer supports the current weather type
unsigned int current = getWeatherID();
if(current >= chances.size() || chances[current] == 0)
mWeatherUpdateTime = 0.0f;
}
12 years ago
float WeatherManager::getWindSpeed() const
{
return mWindSpeed;
}
bool WeatherManager::isDark() const
{
bool exterior = (MWBase::Environment::get().getWorld()->isCellExterior()
|| MWBase::Environment::get().getWorld()->isCellQuasiExterior());
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();
if (world->isCellExterior() || world->isCellQuasiExterior())
{
std::string regionstr = Misc::StringUtils::lowerCase(world->getPlayerPtr().getCell()->getCell()->mRegion);
if (mWeatherUpdateTime <= 0 || regionstr != mCurrentRegion)
{
mCurrentRegion = regionstr;
mWeatherUpdateTime = mHoursBetweenWeatherChanges * 3600;
std::string weatherType = "clear";
if (mRegionOverrides.find(regionstr) != mRegionOverrides.end())
{
weatherType = mRegionOverrides[regionstr];
}
else
{
// get weather probabilities for the current region
const ESM::Region *region = world->getStore().get<ESM::Region>().search (regionstr);
if (region != 0)
{
weatherType = nextWeather(region);
}
}
setWeather(weatherType, instantly);
}
}
}