summaryrefslogtreecommitdiff
path: root/apps/openmw/mwworld/weather.hpp
blob: b29ad9e994ac1eb5e9fdc07380b73123b252d6ef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
#ifndef GAME_MWWORLD_WEATHER_H
#define GAME_MWWORLD_WEATHER_H

#include <cstdint>
#include <string>
#include <map>

#include <osg/Vec4f>

#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;

    enum NightDayMode
    {
        Default = 0,
        ExteriorNight = 1,
        InteriorDay = 2
    };

    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 std::string& type)
        {
            WeatherSetting setting = {
                Fallback::Map::getFloat("Weather_" + type + "_Pre-Sunrise_Time"),
                Fallback::Map::getFloat("Weather_" + type + "_Post-Sunrise_Time"),
                Fallback::Map::getFloat("Weather_" + type + "_Pre-Sunset_Time"),
                Fallback::Map::getFloat("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:
        static osg::Vec3f defaultDirection();

        Weather(const std::string& name,
                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 mRainEntranceSpeed;

        // Maximum count of rain particles
        int mRainMaxRaindrops;

        // Radius of rain effect
        float mRainDiameter;

        // Transition threshold to spawn rain
        float mRainThreshold;

        // Height of rain particles spawn
        float mRainMinHeight;
        float mRainMaxHeight;

        std::string mParticleEffect;

        std::string mRainEffect;

        osg::Vec3f mStormDirection;

        // 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);

        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;
        MWRender::MoonState::Phase 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, 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);

        /**
         * Per-frame update
         * @param duration
         * @param paused
         */
        void update(float duration, bool paused, const TimeStamp& time, bool isExterior);

        void stopSounds();

        float getWindSpeed() const;
        NightDayMode getNightDayMode() 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();

    private:
        MWWorld::ESMStore& mStore;
        MWRender::RenderingManager& mRendering;
        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;
        float mCurrentWindSpeed;
        float mNextWindSpeed;
        bool mIsStorm;
        bool mPrecipitation;
        osg::Vec3f mStormDirection;

        std::string mCurrentRegion;
        float mTimePassed;
        bool mFastForward;
        float mWeatherUpdateTime;
        float mTransitionFactor;
        NightDayMode mNightDayMode;
        int mCurrentWeather;
        int mNextWeather;
        int mQueuedWeather;
        std::map<std::string, RegionWeather> mRegions;
        MWRender::WeatherResult mResult;

        MWBase::Sound *mAmbientSound;
        std::string mPlayingSoundID;

        void addWeather(const std::string& name,
                        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);
        float calculateWindSpeed(int weatherId, float currentSpeed);
    };
}

#endif // GAME_MWWORLD_WEATHER_H