#include "weather.hpp" #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwrender/renderingmanager.hpp" #include "player.hpp" #include "esmstore.hpp" #include "fallback.hpp" #include "cellstore.hpp" using namespace Ogre; using namespace MWWorld; using namespace MWSound; 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) { 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"); weather.mCloudTexture = mFallback->getFallbackString("Weather_"+upper+"_Cloud_Texture"); bool usesPrecip = mFallback->getFallbackBool("Weather_"+upper+"_Using_Precip"); if (usesPrecip) weather.mRainEffect = "meshes\\raindrop.nif"; weather.mRainSpeed = mRainSpeed; weather.mRainFrequency = mFallback->getFallbackFloat("Weather_"+upper+"_Rain_Entrance_Speed"); /* Unhandled: Rain Diameter=600 ? Rain Height Min=200 ? Rain Height Max=700 ? Rain Threshold=0.6 ? Max Raindrops=650 ? */ weather.mIsStorm = (name == "ashstorm" || name == "blight"); mWeatherSettings[name] = weather; } 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; } 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), mIsStorm(false), mStormDirection(0,1,0) { //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; mRainSpeed = mFallback->getFallbackFloat("Weather_Precip_Gravity"); //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; setFallbackWeather(clear,"clear"); Weather cloudy; setFallbackWeather(cloudy,"cloudy"); Weather foggy; setFallbackWeather(foggy,"foggy"); Weather thunderstorm; thunderstorm.mRainLoopSoundID = "rain heavy"; thunderstorm.mRainEffect = "meshes\\raindrop.nif"; setFallbackWeather(thunderstorm,"thunderstorm"); Weather rain; rain.mRainLoopSoundID = "rain"; rain.mRainEffect = "meshes\\raindrop.nif"; setFallbackWeather(rain,"rain"); Weather overcast; setFallbackWeather(overcast,"overcast"); Weather ashstorm; ashstorm.mAmbientLoopSoundID = "ashstorm"; ashstorm.mParticleEffect = "meshes\\ashcloud.nif"; setFallbackWeather(ashstorm,"ashstorm"); Weather blight; blight.mAmbientLoopSoundID = "blight"; blight.mParticleEffect = "meshes\\blightcloud.nif"; setFallbackWeather(blight,"blight"); Weather snow; snow.mParticleEffect = "meshes\\snow.nif"; setFallbackWeather(snow, "snow"); Weather blizzard; blizzard.mAmbientLoopSoundID = "BM Blizzard"; blizzard.mParticleEffect = "meshes\\blizzard.nif"; setFallbackWeather(blizzard,"blizzard"); } WeatherManager::~WeatherManager() { stopSounds(true); } void WeatherManager::setWeather(const String& weather, bool instant) { if (weather == mCurrentWeather && mNextWeather == "") { mFirstUpdate = false; return; } if (instant || mFirstUpdate) { mNextWeather = ""; mCurrentWeather = weather; } else { if (mNextWeather != "") { // transition more than 50% finished? if (mRemainingTransitionTime/(mWeatherSettings[mCurrentWeather].mTransitionDelta * 24.f * 3600) <= 0.5) 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.mIsStorm = current.mIsStorm; mResult.mRainSpeed = current.mRainSpeed; mResult.mRainFrequency = current.mRainFrequency; mResult.mParticleEffect = current.mParticleEffect; mResult.mRainEffect = current.mRainEffect; mResult.mNight = (mHour < mSunriseTime || mHour > mNightStart - 1); mResult.mFogDepth = mResult.mNight ? current.mLandFogNightDepth : current.mLandFogDayDepth; // 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; } // sunrise else if (mHour >= mNightEnd && mHour <= mDayStart + 1) { if (mHour <= mSunriseTime) { // fade in float advance = mSunriseTime - mHour; 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; } else //if (mHour >= 6) { // fade out float advance = mHour - mSunriseTime; 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); } } // 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; } // sunset else if (mHour >= mDayEnd - 1 && mHour <= mNightStart + 1) { if (mHour <= mDayEnd + 1) { // fade in float advance = (mDayEnd + 1) - mHour; 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); } else //if (mHour >= 19) { // fade out float advance = mHour - (mDayEnd + 1); 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; mResult.mIsStorm = current.mIsStorm; mResult.mParticleEffect = current.mParticleEffect; mResult.mRainEffect = current.mRainEffect; mResult.mRainSpeed = current.mRainSpeed; mResult.mRainFrequency = current.mRainFrequency; } 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->skyDisable(); mRendering->getSkyManager()->setLightningStrength(0.f); stopSounds(true); return; } switchToNextWeather(false); if (mNextWeather != "") { mRemainingTransitionTime -= timePassed; if (mRemainingTransitionTime < 0) { mCurrentWeather = mNextWeather; mNextWeather = ""; } } if (mNextWeather != "") transition(1 - (mRemainingTransitionTime / (mWeatherSettings[mCurrentWeather].mTransitionDelta * 24.f * 3600))); else setResult(mCurrentWeather); mWindSpeed = mResult.mWindSpeed; mIsStorm = mResult.mIsStorm; if (mIsStorm) { MWWorld::Ptr player = world->getPlayerPtr(); Ogre::Vector3 playerPos (player.getRefData().getPosition().pos); Ogre::Vector3 redMountainPos (19950, 72032, 27831); mStormDirection = (playerPos - redMountainPos); mStormDirection.z = 0; mRendering->getSkyManager()->setStormDirection(mStormDirection); } mRendering->configureFog(mResult.mFogDepth, mResult.mFogColor); // 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; bool sun_is_moon = mHour >= mNightStart || mHour <= mSunriseTime; Vector3 final( (height - 1) * facing, (height - 1) * facing, height); mRendering->setSunDirection(final, sun_is_moon); /* * TODO: import separated fadeInStart/Finish, fadeOutStart/Finish * for masser and secunda */ 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); Vector3 secunda( (moonHeight - 1) * facing * 1.25, (1 - moonHeight) * facing * 0.8, moonHeight); mRendering->getSkyManager()->setMasserDirection(masser); mRendering->getSkyManager()->setSecundaDirection(secunda); mRendering->getSkyManager()->masserEnable(); mRendering->getSkyManager()->secundaEnable(); 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); masserAngleFade *= masserHourFade; secundaAngleFade *= secundaHourFade; mRendering->getSkyManager()->setMasserFade(masserAngleFade); mRendering->getSkyManager()->setSecundaFade(secundaAngleFade); } else { mRendering->getSkyManager()->masserDisable(); mRendering->getSkyManager()->secundaDisable(); } if (mCurrentWeather == "thunderstorm" && mNextWeather == "") { if (mThunderFlash > 0) { // play the sound after a delay mThunderSoundDelay -= duration; if (mThunderSoundDelay <= 0) { // 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; } mThunderFlash -= duration; if (mThunderFlash > 0) mRendering->getSkyManager()->setLightningStrength( mThunderFlash / mThunderThreshold ); else { mThunderChanceNeeded = rand() % 100; mThunderChance = 0; mRendering->getSkyManager()->setLightningStrength( 0.f ); } } else { // no thunder active mThunderChance += duration*4; // chance increases by 4 percent every second if (mThunderChance >= mThunderChanceNeeded) { mThunderFlash = mThunderThreshold; mRendering->getSkyManager()->setLightningStrength( mThunderFlash / mThunderThreshold ); 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::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 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) { // make sure this region exists MWBase::Environment::get().getWorld()->getStore().get().find(region); 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; std::string playerRegion = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell()->getCell()->mRegion; if (Misc::StringUtils::ciEqual(region, playerRegion)) setWeather(weather); } void WeatherManager::modRegion(const std::string ®ionid, const std::vector &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; } 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, Loading::Listener& progress) { 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); state.save(writer); writer.endRecord(ESM::REC_WTHR); progress.increaseProgress(); } bool WeatherManager::readRecord(ESM::ESMReader& reader, int32_t type) { if(ESM::REC_WTHR == type) { // 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(); mRegionMods.clear(); 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; } 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().search (regionstr); if (region != 0) { weatherType = nextWeather(region); } } setWeather(weatherType, instantly); } } } bool WeatherManager::isInStorm() const { return mIsStorm; } Ogre::Vector3 WeatherManager::getStormDirection() const { return mStormDirection; }