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.hpp

420 lines
13 KiB
C++

#ifndef GAME_MWWORLD_WEATHER_H
#define GAME_MWWORLD_WEATHER_H
#include <stdint.h>
#include <string>
#include <map>
#include <osg/Vec4f>
12 years ago
#include <components/fallback/fallback.hpp>
#include "../mwbase/soundmanager.hpp"
#include "../mwrender/sky.hpp"
namespace ESM
{
struct Region;
struct RegionWeatherState;
class ESMWriter;
class ESMReader;
}
namespace MWRender
{
class RenderingManager;
}
namespace Loading
{
class Listener;
}
namespace Fallback
{
class Map;
}
namespace MWWorld
{
class TimeStamp;
12 years ago
struct WeatherSetting
{
float mPreSunriseTime;
float mPostSunriseTime;
float mPreSunsetTime;
float mPostSunsetTime;
};
struct TimeOfDaySettings
{
float mNightStart;
float mNightEnd;
float mDayStart;
float mDayEnd;
std::map<std::string, WeatherSetting> mSunriseTransitions;
float mStarsPostSunsetStart;
float mStarsPreSunriseFinish;
float mStarsFadingDuration;
WeatherSetting getSetting(const std::string& type) const
{
std::map<std::string, WeatherSetting>::const_iterator it = mSunriseTransitions.find(type);
if (it != mSunriseTransitions.end())
{
return it->second;
}
else
{
return { 1.f, 1.f, 1.f, 1.f };
}
}
void addSetting(const Fallback::Map& fallback, const std::string& type)
{
WeatherSetting setting = {
fallback.getFallbackFloat("Weather_" + type + "_Pre-Sunrise_Time"),
fallback.getFallbackFloat("Weather_" + type + "_Post-Sunrise_Time"),
fallback.getFallbackFloat("Weather_" + type + "_Pre-Sunset_Time"),
fallback.getFallbackFloat("Weather_" + type + "_Post-Sunset_Time")
};
mSunriseTransitions[type] = setting;
}
};
/// Interpolates between 4 data points (sunrise, day, sunset, night) based on the time of day.
/// The template value could be a floating point number, or a color.
template <typename T>
class TimeOfDayInterpolator
{
public:
TimeOfDayInterpolator(const T& sunrise, const T& day, const T& sunset, const T& night)
: mSunriseValue(sunrise), mDayValue(day), mSunsetValue(sunset), mNightValue(night)
{
}
T getValue (const float gameHour, const TimeOfDaySettings& timeSettings, const std::string& prefix) const;
private:
T mSunriseValue, mDayValue, mSunsetValue, mNightValue;
};
/// Defines a single weather setting (according to INI)
class Weather
{
public:
Weather(const std::string& name,
const Fallback::Map& fallback,
float stormWindSpeed,
float rainSpeed,
float dlFactor,
float dlOffset,
const std::string& particleEffect);
std::string mCloudTexture;
// Sky (atmosphere) color
TimeOfDayInterpolator<osg::Vec4f> mSkyColor;
// Fog color
TimeOfDayInterpolator<osg::Vec4f> mFogColor;
// Ambient lighting color
TimeOfDayInterpolator<osg::Vec4f> mAmbientColor;
// Sun (directional) lighting color
TimeOfDayInterpolator<osg::Vec4f> mSunColor;
// Fog depth/density
TimeOfDayInterpolator<float> mLandFogDepth;
// Color modulation for the sun itself during sunset
osg::Vec4f mSunDiscSunsetColor;
// Used by scripts to animate signs, etc based on the wind (GetWindSpeed)
float mWindSpeed;
// Cloud animation speed multiplier
float mCloudSpeed;
// Value between 0 and 1, defines the strength of the sun glare effect.
// Also appears to modify how visible the sun, moons, and stars are for various weather effects.
float mGlareView;
// Fog factor and offset used with distant land rendering.
struct {
float FogFactor;
float FogOffset;
} mDL;
// Sound effect
// This is used for Blight, Ashstorm and Blizzard (Bloodmoon)
std::string mAmbientLoopSoundID;
// Is this an ash storm / blight storm? If so, the following will happen:
// - The particles and clouds will be oriented so they appear to come from the Red Mountain.
// - Characters will animate their hand to protect eyes from the storm when looking in its direction (idlestorm animation)
// - Slower movement when walking against the storm (fStromWalkMult)
bool mIsStorm;
// How fast does rain travel down?
// In Morrowind.ini this is set globally, but we may want to change it per weather later.
float mRainSpeed;
// How often does a new rain mesh spawn?
float mRainFrequency;
std::string mParticleEffect;
std::string mRainEffect;
// Note: For Weather Blight, there is a "Disease Chance" (=0.1) setting. But according to MWSFD this feature
// is broken in the vanilla game and was disabled.
float transitionDelta() const;
float cloudBlendFactor(const float transitionRatio) const;
float calculateThunder(const float transitionRatio, const float elapsedSeconds, const bool isPaused);
private:
float mTransitionDelta;
float mCloudsMaximumPercent;
// Note: In MW, only thunderstorms support these attributes, but in the interest of making weather more
// flexible, these settings are imported for all weather types. Only thunderstorms will normally have any
// non-zero values.
float mThunderFrequency;
float mThunderThreshold;
std::string mThunderSoundID[4];
float mFlashDecrement;
float mFlashBrightness;
void flashDecrement(const float elapsedSeconds);
float thunderChance(const float transitionRatio, const float elapsedSeconds) const;
void lightningAndThunder(void);
};
/// A class for storing a region's weather.
class RegionWeather
{
public:
explicit RegionWeather(const ESM::Region& region);
explicit RegionWeather(const ESM::RegionWeatherState& state);
operator ESM::RegionWeatherState() const;
void setChances(const std::vector<char>& chances);
void setWeather(int weatherID);
int getWeather();
private:
int mWeather;
std::vector<char> mChances;
void chooseNewWeather();
};
/// A class that acts as a model for the moons.
class MoonModel
{
public:
MoonModel(const std::string& name, const Fallback::Map& fallback);
MWRender::MoonState calculateState(const TimeStamp& gameTime) const;
private:
float mFadeInStart;
float mFadeInFinish;
float mFadeOutStart;
float mFadeOutFinish;
float mAxisOffset;
float mSpeed;
float mDailyIncrement;
float mFadeStartAngle;
float mFadeEndAngle;
float mMoonShadowEarlyFadeAngle;
float angle(const TimeStamp& gameTime) const;
float moonRiseHour(unsigned int daysPassed) const;
float rotation(float hours) const;
unsigned int phase(const TimeStamp& gameTime) const;
float shadowBlend(float angle) const;
float hourlyAlpha(float gameHour) const;
float earlyMoonShadowAlpha(float angle) const;
};
/// Interface for weather settings
class WeatherManager
{
public:
// Have to pass fallback and Store, can't use singleton since World isn't fully constructed yet at the time
WeatherManager(MWRender::RenderingManager& rendering,
const Fallback::Map& fallback,
MWWorld::ESMStore& store);
~WeatherManager();
/**
* Change the weather in the specified region
* @param region that should be changed
* @param ID of the weather setting to shift to
*/
void changeWeather(const std::string& regionID, const unsigned int weatherID);
void modRegion(const std::string& regionID, const std::vector<char>& chances);
void playerTeleported(const std::string& playerRegion, bool isExterior);
[General] Reimplement weather synchronization to allow soft transitions Although weather sync was added by Koncord to the rewrite in fd721143e226061d72f7b8931b8d4192a5327f47 in a way that used surprisingly few lines of code, it relied on the server requesting weather states every second from authority players and sending them to non-authority players, while also allowing only very sudden weather transitions across regions, i.e. if there was one player in the Ascadian Isles who had stormy weather, and another player with clear weather in the Bitter Coast Region walked across to the Ascadian Isles, that player was instantly made to have stormy weather with no kind of transition at all. My approach solves both of those problems. It solves the packet spam by only sending weather updates to the server when weather changes happen or when there are new arrivals to a weather authority's region, and it allows for both sudden weather transitions when players teleport to a region and for soft, gradual transitions when players walk across to a region. It is inspired by my previous actor sync, and uses a WorldRegionAuthority packet to set players as region authorities in a similar way to how ActorAuthority sets players as cell AI authorities. Weather changes are created only by the region authority for a given region, and weather packets are also only sent by that authority. However, it should be noted that gradual weather transitions are used by default in this implementation. To use sudden weather transitions, the serverside Lua scripts need to forward WorldWeather packets with the forceWeather boolean set to true. That is, however, already handled by our default Lua scripts in situations where it makes sense.
7 years ago
/*
Start of tes3mp addition
Make it possible to set a specific weather state for a region from elsewhere
in the code
*/
void setRegionWeather(const std::string& region, const unsigned int currentWeather, const unsigned int nextWeather,
const unsigned int queuedWeather, const float transitionFactor, bool force);
/*
End of tes3mp addition
*/
/**
* Per-frame update
* @param duration
* @param paused
*/
void update(float duration, bool paused, const TimeStamp& time, bool isExterior);
void stopSounds();
12 years ago
float getWindSpeed() const;
/// Are we in an ash or blight storm?
bool isInStorm() const;
osg::Vec3f getStormDirection() const;
void advanceTime(double hours, bool incremental);
unsigned int getWeatherID() const;
bool useTorches(float hour) const;
void write(ESM::ESMWriter& writer, Loading::Listener& progress);
bool readRecord(ESM::ESMReader& reader, uint32_t type);
void clear();
[General] Reimplement weather synchronization to allow soft transitions Although weather sync was added by Koncord to the rewrite in fd721143e226061d72f7b8931b8d4192a5327f47 in a way that used surprisingly few lines of code, it relied on the server requesting weather states every second from authority players and sending them to non-authority players, while also allowing only very sudden weather transitions across regions, i.e. if there was one player in the Ascadian Isles who had stormy weather, and another player with clear weather in the Bitter Coast Region walked across to the Ascadian Isles, that player was instantly made to have stormy weather with no kind of transition at all. My approach solves both of those problems. It solves the packet spam by only sending weather updates to the server when weather changes happen or when there are new arrivals to a weather authority's region, and it allows for both sudden weather transitions when players teleport to a region and for soft, gradual transitions when players walk across to a region. It is inspired by my previous actor sync, and uses a WorldRegionAuthority packet to set players as region authorities in a similar way to how ActorAuthority sets players as cell AI authorities. Weather changes are created only by the region authority for a given region, and weather packets are also only sent by that authority. However, it should be noted that gradual weather transitions are used by default in this implementation. To use sudden weather transitions, the serverside Lua scripts need to forward WorldWeather packets with the forceWeather boolean set to true. That is, however, already handled by our default Lua scripts in situations where it makes sense.
7 years ago
/*
Start of tes3mp addition
Make it possible to check whether the local WeatherManager has the
ability to create weather changes
*/
bool getWeatherCreationState();
/*
End of tes3mp addition
*/
/*
Start of tes3mp addition
Make it possible to enable and disable the local WeatherManager's ability
to create weather changes
*/
void setWeatherCreationState(bool state);
/*
End of tes3mp addition
*/
/*
Start of tes3mp addition
Make it possible to send the current weather in a WorldWeather packet
when requested from elsewhere in the code
*/
void sendWeather();
/*
End of tes3mp addition
*/
private:
MWWorld::ESMStore& mStore;
MWRender::RenderingManager& mRendering;
12 years ago
float mSunriseTime;
float mSunsetTime;
float mSunriseDuration;
float mSunsetDuration;
float mSunPreSunsetTime;
TimeOfDaySettings mTimeSettings;
// fading of night skydome
TimeOfDayInterpolator<float> mNightFade;
float mHoursBetweenWeatherChanges;
float mRainSpeed;
// underwater fog not really related to weather, but we handle it here because it's convenient
TimeOfDayInterpolator<float> mUnderwaterFog;
std::vector<Weather> mWeatherSettings;
MoonModel mMasser;
MoonModel mSecunda;
float mWindSpeed;
bool mIsStorm;
bool mPrecipitation;
osg::Vec3f mStormDirection;
std::string mCurrentRegion;
float mTimePassed;
bool mFastForward;
float mWeatherUpdateTime;
float mTransitionFactor;
int mCurrentWeather;
int mNextWeather;
int mQueuedWeather;
std::map<std::string, RegionWeather> mRegions;
MWRender::WeatherResult mResult;
MWBase::Sound *mAmbientSound;
std::string mPlayingSoundID;
[General] Reimplement weather synchronization to allow soft transitions Although weather sync was added by Koncord to the rewrite in fd721143e226061d72f7b8931b8d4192a5327f47 in a way that used surprisingly few lines of code, it relied on the server requesting weather states every second from authority players and sending them to non-authority players, while also allowing only very sudden weather transitions across regions, i.e. if there was one player in the Ascadian Isles who had stormy weather, and another player with clear weather in the Bitter Coast Region walked across to the Ascadian Isles, that player was instantly made to have stormy weather with no kind of transition at all. My approach solves both of those problems. It solves the packet spam by only sending weather updates to the server when weather changes happen or when there are new arrivals to a weather authority's region, and it allows for both sudden weather transitions when players teleport to a region and for soft, gradual transitions when players walk across to a region. It is inspired by my previous actor sync, and uses a WorldRegionAuthority packet to set players as region authorities in a similar way to how ActorAuthority sets players as cell AI authorities. Weather changes are created only by the region authority for a given region, and weather packets are also only sent by that authority. However, it should be noted that gradual weather transitions are used by default in this implementation. To use sudden weather transitions, the serverside Lua scripts need to forward WorldWeather packets with the forceWeather boolean set to true. That is, however, already handled by our default Lua scripts in situations where it makes sense.
7 years ago
/*
Start of tes3mp addition
Track whether the local WeatherManager should be creating any weather changes
by itself; when set to false, only weather changes sent by the server are used
*/
bool mWeatherCreationState = false;
/*
End of tes3mp addition
*/
void addWeather(const std::string& name,
const Fallback::Map& fallback,
float dlFactor, float dlOffset,
const std::string& particleEffect = "");
void importRegions();
void regionalWeatherChanged(const std::string& regionID, RegionWeather& region);
bool updateWeatherTime();
bool updateWeatherRegion(const std::string& playerRegion);
void updateWeatherTransitions(const float elapsedRealSeconds);
void forceWeather(const int weatherID);
bool inTransition();
void addWeatherTransition(const int weatherID);
void calculateWeatherResult(const float gameHour, const float elapsedSeconds, const bool isPaused);
void calculateResult(const int weatherID, const float gameHour);
void calculateTransitionResult(const float factor, const float gameHour);
};
}
#endif // GAME_MWWORLD_WEATHER_H