Fix classique titles display
[ryzomcore.git] / ryzom / client / src / weather_manager_client.cpp
blob6f13c7ee5fa122287147da2fcd24e7aea9b1baf9
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2017 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2016 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "stdpch.h"
23 #include "game_share/light_cycle.h"
24 #include "game_share/time_weather_season/weather_predict.h"
26 #include "client_sheets/weather_function_params_sheet.h"
28 #include "weather.h"
29 #include "weather_manager_client.h"
30 #include "weather_setup_client.h"
31 #include "precipitation.h"
32 #include "continent.h"
33 #include "sheet_manager.h"
34 #include "sound_manager.h"
35 #include "client_cfg.h"
37 #ifdef DEBUG_NEW
38 #define new DEBUG_NEW
39 #endif
41 H_AUTO_DECL(RZ_WeatherManagerClient)
43 using namespace NLMISC;
44 using namespace std;
47 extern NL3D::UScene *Scene;
48 extern NL3D::ULandscape *Landscape;
50 //================================================================================================
51 CWeatherManagerClient::CWeatherManagerClient() : _WindDir(0, 0, 0),
52 _WeatherValue(0),
53 _ThunderLevel(0),
54 _ThunderStrike(false),
55 _LastEvalHour(0),
56 _LastEvalDay(0),
57 _LocalPrecipitationFactor(0.f)
62 //================================================================================================
63 void CWeatherManagerClient::init()
65 // Get the list of the weather setups sheets
66 std::vector<CSheetId> result;
67 CSheetId::buildIdVector(result, "weather_setup");
68 std::vector<const CWeatherSetupSheetBase *> wsSheets;
69 std::vector<std::string> wsSheetsNames;
71 for(uint k = 0; k < result.size(); ++k)
73 CEntitySheet *sheet = SheetMngr.get(result[k]);
74 if (sheet && sheet->type() == CEntitySheet::WEATHER_SETUP)
76 //nlwarning("Sheet name = %s, weather setup = %s", result[k].toString().c_str(), NLMISC::CStringMapper::unmap((dynamic_cast<CWeatherSetupSheetBase *>(sheet))->SetupName).c_str());
77 wsSheetsNames.push_back(result[k].toString());
78 wsSheets.push_back(dynamic_cast<CWeatherSetupSheetBase *>(sheet));
81 init(wsSheets, wsSheetsNames);
84 //================================================================================================
85 CWeatherSetup *CWeatherManagerClient::newWeatherSetup() const
87 // client version of weather setup
88 return new CWeatherSetupClient;
91 //================================================================================================
92 void CWeatherManagerClient::setupLoaded(CWeatherSetup *setup)
94 CWeatherSetupClient *wsc = NLMISC::safe_cast<CWeatherSetupClient *>(setup);
95 wsc->WeatherStateClient.setup(setup->WeatherState, _PrecipitationMap);
99 //================================================================================================
100 void CWeatherManagerClient::init(const std::vector<const CWeatherSetupSheetBase *> &sheets, const std::vector<std::string> &sheetNames)
102 CWeatherManager::init(sheets, sheetNames);
103 initPrecipitationFXs();
106 //================================================================================================
107 void CWeatherManagerClient::initPrecipitationFXs()
109 CPrecipitationDesc desc;
110 for(TPrecipitationMap::iterator it = _PrecipitationMap.begin(); it != _PrecipitationMap.end(); ++it)
112 desc.FxName = it->first + ".ps";
113 desc.ReleasableModel = true;
114 desc.GridSize = 7;
115 desc.UseBBoxSize = true;
116 it->second.init(desc);
120 //================================================================================================
121 /** Update a vector of precipitation with the given pos
123 static void updatePrecipitationVect(std::vector<CPrecipitation *> &vect, const NLMISC::CMatrix &camMat, NLPACS::UGlobalRetriever *gr)
125 H_AUTO_USE(RZ_WeatherManagerClient)
126 for(std::vector<CPrecipitation *>::iterator it = vect.begin(); it != vect.end(); ++it)
128 (*it)->update(camMat, gr);
132 //================================================================================================
133 void CWeatherManagerClient::update(uint64 day, float hour, const CWeatherContext &wc)
135 H_AUTO_USE(RZ_WeatherManagerClient)
136 // get the weather value for the current date
137 nlassert(wc.WFP);
138 float weatherValue = ::getBlendedWeather(day, hour, *(wc.WFP), wc.WF);
139 // build current weather state
140 EGSPD::CSeason::TSeason season = CRyzomTime::getSeasonByDay((uint32)day);
142 manualUpdateImpl(day, hour, wc, weatherValue, season);
143 _LastEvalHour = hour;
144 _LastEvalDay = day;
147 //================================================================================================
148 void CWeatherManagerClient::update(uint64 day, float hour, const CWeatherContext &wc, const NLMISC::CMatrix &camMat, const CContinent &continent)
150 H_AUTO_USE(RZ_WeatherManagerClient)
151 // get the weather value for the current date
152 nlassert(wc.WFP);
153 float weatherValue = ::getBlendedWeather(day, hour, *(wc.WFP), wc.WF);
155 // calculate next weather cycle and ingame hour for it
156 uint64 cycle = ((day * wc.WFP->DayLength) + (uint) hour) / wc.WFP->CycleLength;
157 uint64 cycleDay = ((cycle + 1) * wc.WFP->CycleLength) / wc.WFP->DayLength;
158 _NextWeatherHour = ((cycle + 1) * wc.WFP->CycleLength) % wc.WFP->DayLength;
159 _NextWeatherValue = ::getBlendedWeather(cycleDay, _NextWeatherHour, *(wc.WFP), wc.WF);
161 // build current weather state
162 EGSPD::CSeason::TSeason season = CRyzomTime::getSeasonByDay((uint32)day);
164 manualUpdateImpl(day, hour, wc, weatherValue, season, camMat, continent);
165 _LastEvalHour = hour;
166 _LastEvalDay = day;
169 //================================================================================================
170 void CWeatherManagerClient::manualUpdate(uint64 day, float hour, const CWeatherContext &wc, float weatherValue, EGSPD::CSeason::TSeason season, const NLMISC::CMatrix &camMat, const CContinent &continent)
172 manualUpdateImpl(day, hour, wc, weatherValue, season, camMat, continent);
173 _LastEvalHour = hour;
174 _LastEvalDay = day;
177 //================================================================================================
178 void CWeatherManagerClient::manualUpdateImpl(uint64 day, float hour, const CWeatherContext &wc, float weatherValue, EGSPD::CSeason::TSeason season, const NLMISC::CMatrix &camMat, const CContinent &continent)
180 H_AUTO_USE(RZ_WeatherManagerClient)
181 if (!wc.WF) return;
182 manualUpdateImpl(day, hour, wc, weatherValue, season);
183 setupFXs(camMat, wc.GR, continent);
184 setupWind(&(wc.WF[season]));
185 float scaledWeatherValue = weatherValue * (wc.WF[season].getNumWeatherSetups() - 1);
186 updateThunder(day, hour, wc, true, scaledWeatherValue, season);
188 // Sound stuff
189 if (SoundMngr != 0)
192 const CWeatherSetup *floorSetup, *ceilSetup;
193 float blendFactor;
194 wc.WF[season].getClosestWeatherSetups(scaledWeatherValue, floorSetup, ceilSetup, blendFactor);
196 float userVarClouds1 = 0.f;
197 float userVarClouds2 = 0.f;
198 float userVarStorm = 0.f;
200 static TStringId strClouds1 = CStringMapper::map("clouds1");
201 static TStringId strClouds2 = CStringMapper::map("clouds2");
202 static TStringId strHumidity1 = CStringMapper::map("humidity1");
203 static TStringId strHumidity2 = CStringMapper::map("humidity2");
204 static TStringId strStorm= CStringMapper::map("storm");
207 // contrib from previous setup
208 if (_CurrWeatherState.FirstSetupName == strClouds1 ||
209 _CurrWeatherState.FirstSetupName == strHumidity1)
211 if (floorSetup && !floorSetup->WeatherState.FXInfos.empty() && !floorSetup->WeatherState.FXInfos[0].Name.empty())
213 userVarClouds1 += (1.f - _CurrWeatherState.BlendFactor) * _LocalPrecipitationFactor * floorSetup->WeatherState.FXInfos[0].Ratio;
216 else
217 if (_CurrWeatherState.FirstSetupName == strClouds2 ||
218 _CurrWeatherState.FirstSetupName == strHumidity2)
220 if (floorSetup && !floorSetup->WeatherState.FXInfos.empty() && !floorSetup->WeatherState.FXInfos[0].Name.empty())
222 userVarClouds2 += (1.f - _CurrWeatherState.BlendFactor) * _LocalPrecipitationFactor * floorSetup->WeatherState.FXInfos[0].Ratio;
225 else
226 if (_CurrWeatherState.FirstSetupName == strStorm)
228 if (floorSetup && !floorSetup->WeatherState.FXInfos.empty() && floorSetup->WeatherState.FXInfos[0].Name.empty())
230 userVarClouds2 += (1.f - _CurrWeatherState.BlendFactor) * _LocalPrecipitationFactor * floorSetup->WeatherState.FXInfos[0].Ratio;
232 userVarStorm += (1.f - _CurrWeatherState.BlendFactor) * _LocalPrecipitationFactor;
234 // contrib from next setup
235 if (_CurrWeatherState.SecondSetupName == strClouds1 ||
236 _CurrWeatherState.SecondSetupName == strHumidity1)
238 if (ceilSetup && !ceilSetup->WeatherState.FXInfos.empty() && !ceilSetup->WeatherState.FXInfos[0].Name.empty())
240 userVarClouds1 += _CurrWeatherState.BlendFactor * _LocalPrecipitationFactor * ceilSetup->WeatherState.FXInfos[0].Ratio;
243 else
244 if (_CurrWeatherState.SecondSetupName == strClouds2 ||
245 _CurrWeatherState.SecondSetupName == strHumidity2)
247 if (ceilSetup && !ceilSetup->WeatherState.FXInfos.empty() && !ceilSetup->WeatherState.FXInfos[0].Name.empty())
249 userVarClouds2 += _CurrWeatherState.BlendFactor * _LocalPrecipitationFactor * ceilSetup->WeatherState.FXInfos[0].Ratio;
252 else
253 if (_CurrWeatherState.SecondSetupName == strStorm)
255 if (ceilSetup && !ceilSetup->WeatherState.FXInfos.empty() && !ceilSetup->WeatherState.FXInfos[0].Name.empty())
257 userVarClouds2 += _CurrWeatherState.BlendFactor * _LocalPrecipitationFactor * ceilSetup->WeatherState.FXInfos[0].Ratio;
259 userVarStorm += _CurrWeatherState.BlendFactor * _LocalPrecipitationFactor;
262 // update vars
263 SoundMngr->getMixer()->setUserVar(strClouds1, 1.f - (1.f - userVarClouds1) * (1.f - userVarClouds1) * (1.f - userVarClouds1));
264 SoundMngr->getMixer()->setUserVar(strClouds2, 1.f - (1.f - userVarClouds2) * (1.f - userVarClouds2) * (1.f - userVarClouds2));
265 SoundMngr->getMixer()->setUserVar(strStorm, 1.f - (1.f - userVarStorm) * (1.f - userVarStorm) * (1.f - userVarStorm));
271 //================================================================================================
272 void CWeatherManagerClient::manualUpdate(uint64 day, float hour, const CWeatherContext &wc, float weatherValue, EGSPD::CSeason::TSeason season)
274 manualUpdateImpl(day, hour, wc, weatherValue, season);
275 _LastEvalHour = hour;
276 _LastEvalDay = day;
279 //================================================================================================
280 void CWeatherManagerClient::manualUpdateImpl(uint64 day, float hour, const CWeatherContext &wc, float weatherValue, EGSPD::CSeason::TSeason season)
282 H_AUTO_USE(RZ_WeatherManagerClient)
283 if (!wc.WF) return;
284 _WeatherValue = weatherValue;
285 nlassert(season < EGSPD::CSeason::Invalid);
286 float scaledWeatherValue = weatherValue * (wc.WF[season].getNumWeatherSetups() - 1);
287 const CWeatherSetup *floorSetup, *ceilSetup;
288 float blendFactor;
289 wc.WF[season].getClosestWeatherSetups(scaledWeatherValue, floorSetup, ceilSetup, blendFactor);
290 if (floorSetup && ceilSetup)
292 // blend general part
293 CWeatherState::blend(_CurrWeatherState, floorSetup->WeatherState, ceilSetup->WeatherState, blendFactor);
294 // blend client specific part
295 CWeatherStateClient::blend(_CurrWeatherStateClient, safe_cast<const CWeatherSetupClient *>(floorSetup)->WeatherStateClient, safe_cast<const CWeatherSetupClient *>(ceilSetup)->WeatherStateClient, blendFactor);
299 //================================================================================================
300 void CWeatherManagerClient::setupWind(const CWeatherFunction *wf)
302 H_AUTO_USE(RZ_WeatherManagerClient)
303 float wi = _CurrWeatherState.WindIntensity;
304 NLMISC::clamp(_CurrWeatherState.WindIntensity, 0.f, 1.f);
305 // TEMP
306 // wind for static & personnal FXs
307 NL3D::UParticleSystemInstance::setGlobalUserParamValue("WIND", wi);
308 // wind for vegetables
309 if (Landscape)
311 float vegetWindFreq;
312 float vegetBendIntensity;
313 float vegetBendOffset;
315 if (!wf)
317 static const float windBendMin = 0.5f; // static for tests in debug mode
318 static const float startWindBendMin = 0.6f;
319 vegetWindFreq = 5.f * wi;
320 vegetBendIntensity = wi;
321 vegetBendOffset = wi < startWindBendMin ? 0.f : windBendMin * (wi - startWindBendMin) / (1.f - startWindBendMin);
323 else
325 vegetWindFreq = wf->VegetableMinWindFrequency + (wf->VegetableMaxWindFrequency - wf->VegetableMinWindFrequency) * wi;
326 vegetBendIntensity = wf->VegetableMinBendIntensity + (wf->VegetableMaxBendIntensity - wf->VegetableMinBendIntensity) * wi;
327 if (wi < wf->VegetableWindIntensityThatStartBendOffset)
329 vegetBendOffset = 0.f;
331 else
333 vegetBendOffset = wf->VegetableMaxBendOffset * (wi - wf->VegetableWindIntensityThatStartBendOffset) / (1.f - wf->VegetableWindIntensityThatStartBendOffset);
337 Landscape->setVegetableWind(_WindDir, vegetWindFreq, vegetBendIntensity, vegetBendOffset);
339 // wind for trees
340 if (Scene)
342 float windTree;
343 if (wf)
345 windTree = wf->TreeMinWindIntensity + (wf->TreeMaxWindIntensity - wf->TreeMinWindIntensity) * wi;
347 else
349 windTree = 0.1f + 0.9f * wi;
351 Scene->setGlobalWindPower(windTree);
355 //================================================================================================
356 void CWeatherManagerClient::setupFXs(const NLMISC::CMatrix &camMat, NLPACS::UGlobalRetriever *gr, const CContinent &continent)
358 H_AUTO_USE(RZ_WeatherManagerClient)
359 static TPrecipitationVect askedPrecipitations;
360 static TPrecipitationVect releasablePrecipitations;
362 // Read the precipitation local ratio
363 CVector pos = camMat.getPos();
364 CRGBAF localNoPrecipitation = continent.FogMap.getMapValue (CFogMapBuild::NoPrecipitation, pos.x, pos.y, CRGBAF(0.f, 0.f, 0.f, 0.f));
365 _LocalPrecipitationFactor = 1.f - localNoPrecipitation.R;
367 uint numAskedFXs = (uint)_CurrWeatherStateClient.FXs.size();
368 askedPrecipitations.resize(numAskedFXs);
369 uint k;
370 for(k = 0; k < numAskedFXs; ++k)
372 askedPrecipitations[k] = _CurrWeatherStateClient.FXs[k].Precipitation;
373 _CurrWeatherStateClient.FXs[k].Precipitation->setStrenght (_CurrWeatherStateClient.FXs[k].Ratio * _LocalPrecipitationFactor);
375 std::sort(askedPrecipitations.begin(), askedPrecipitations.end()); // sort em for std::set_
376 // See which FXs where asked to be removed, but are now needed again (remove them from the waiting/shutting_down list)
377 releasablePrecipitations.clear();
378 std::set_difference(_WaitingPrecipitations.begin(), _WaitingPrecipitations.end(), askedPrecipitations.begin(), askedPrecipitations.end(), std::back_inserter(releasablePrecipitations));
380 if (!releasablePrecipitations.empty())
382 for(uint k = 0; k < releasablePrecipitations.size(); ++k)
384 nlinfo("Shutting down precipitation reused : %s", releasablePrecipitations[k]->getDesc().FxName.c_str() );
388 _WaitingPrecipitations.swap(releasablePrecipitations);
389 // see which FXs are to be removed
390 // NB : we are working on vectors, but they are likely to be very smalls
391 releasablePrecipitations.clear();
392 std::set_difference(_ActivePrecipitations.begin(), _ActivePrecipitations.end(), askedPrecipitations.begin(), askedPrecipitations.end(), std::back_inserter(releasablePrecipitations));
394 for(k = 0; k < releasablePrecipitations.size(); ++k)
396 releasablePrecipitations[k]->setStrenght(0);
399 if (!releasablePrecipitations.empty())
401 for(uint k = 0; k < releasablePrecipitations.size(); ++k)
403 nlinfo("Precipitation put in shutting down list : %s", releasablePrecipitations[k]->getDesc().FxName.c_str() );
407 // put in the waiting precipitation list
408 _WaitingPrecipitations.insert(_WaitingPrecipitations.end(), releasablePrecipitations.begin(), releasablePrecipitations.end());
409 // set new active FX list
410 _ActivePrecipitations.swap(askedPrecipitations);
411 // tmp for debug : dump precipitation list if it has changed
413 if (_ActivePrecipitations.size() != askedPrecipitations.size() ||
414 !std::equal(_ActivePrecipitations.begin(), _ActivePrecipitations.end(), askedPrecipitations.begin()) )
416 for(uint k = 0; k < _ActivePrecipitations.size(); ++k)
418 nlinfo("New precipitation list : %s", _ActivePrecipitations[k]->getDesc().FxName.c_str() );
422 // update precipitations
423 updatePrecipitationVect(_ActivePrecipitations, camMat, gr);
424 // update waiting precipitations
425 updatePrecipitationVect(_WaitingPrecipitations, camMat, gr);
426 // Remove waiting precipitation that are not running anymore (no more particles)
427 TPrecipitationVect::iterator lastValid = std::remove_if(_WaitingPrecipitations.begin(), _WaitingPrecipitations.end(), std::not1(std::mem_fun(&CPrecipitation::isRunning)));
428 _WaitingPrecipitations.erase(lastValid, _WaitingPrecipitations.end());
431 //================================================================================================
432 void CWeatherManagerClient::setWindDir(const NLMISC::CVector &dir)
434 H_AUTO_USE(RZ_WeatherManagerClient)
435 _WindDir.set(dir.x, dir.y, 0.f);
436 _WindDir.normalize();
437 NL3D::UParticleSystemInstance::setGlobalVectorValue("WIND", _WindDir);
440 //================================================================================================
441 void CWeatherManagerClient::computeCloudState(uint64 day, float hour, const CWeatherContext &wc, CCloudState &dest) const
443 H_AUTO_USE(RZ_WeatherManagerClient)
444 if (!wc.WF)
446 dest = CCloudState();
447 return;
449 nlassert(wc.WFP);
450 float weatherValue = ::getBlendedWeather(day, hour, *(wc.WFP), wc.WF);
451 // build current weather state
452 uint season = CRyzomTime::getSeasonByDay((uint32)day);
453 computeCloudState(weatherValue, (EGSPD::CSeason::TSeason) season, dest, wc.WF);
456 //================================================================================================
457 void CWeatherManagerClient::computeCloudState(float weatherValue, EGSPD::CSeason::TSeason season, CCloudState &dest, const CWeatherFunction wf[EGSPD::CSeason::Invalid]) const
459 H_AUTO_USE(RZ_WeatherManagerClient)
460 if (!wf)
462 dest = CCloudState();
463 return;
465 nlassert(season < EGSPD::CSeason::Invalid);
466 wf[season].getCloudState(weatherValue * (wf[season].getNumWeatherSetups() - 1), dest);
469 //================================================================================================
470 void CWeatherManagerClient::release()
472 for(TPrecipitationMap::iterator it = _PrecipitationMap.begin(); it != _PrecipitationMap.end(); ++it)
474 it->second.release();
476 NLMISC::contReset(_PrecipitationMap);
477 NLMISC::contReset(_ActivePrecipitations);
478 NLMISC::contReset(_WaitingPrecipitations);
479 CWeatherManager::release();
480 _CurrWeatherStateClient = CWeatherStateClient();
483 //================================================================================================
484 void CWeatherManagerClient::drawPrecipitationClipGrids(NL3D::UDriver &drv) const
486 for(TPrecipitationVect::const_iterator it = _ActivePrecipitations.begin(); it != _ActivePrecipitations.end(); ++it)
488 (*it)->drawClipGrid(drv);
492 //================================================================================================
494 // alias for the thunder time measure
495 typedef CWeatherManagerClient::CThunderTimeMeasure CThunderTime;
497 static inline bool operator == (const CThunderTime &lhs, const CThunderTime &rhs)
499 return lhs.Cycle == rhs.Cycle && lhs.SubCycle == rhs.SubCycle;
502 static inline bool operator != (const CThunderTime &lhs, const CThunderTime &rhs)
504 return !(lhs == rhs);
508 /** Eval the thunder function value at the given date
510 static float evalThunderFunction(const CThunderTime &tt)
512 H_AUTO_USE(RZ_WeatherManagerClient)
513 // cache previous result
514 static CThunderTime lastTime;
515 static float lastValue;
516 if (tt != lastTime)
518 CRandom rnd;
519 rnd.srand((uint32) (tt.Cycle & 0xFFFFFFFF));
520 sint32 dummy = rnd.rand();
521 dummy = rnd.rand();
522 dummy = rnd.rand();
523 float v0 = rnd.frand(1.f);
524 rnd.srand((uint32) ((tt.Cycle + 1) & 0xFFFFFFFF));
525 dummy = rnd.rand();
526 dummy = rnd.rand();
527 dummy = rnd.rand();
528 float v1 = rnd.frand(1.f);
529 lastValue = tt.SubCycle * v1 + (1.f - tt.SubCycle) * v0;
530 lastTime = tt;
532 return lastValue;
535 /** Convert a day / hour to a CThunderTime
537 static inline void toThunderTime(uint64 day, float hour, CThunderTime &dest, const CWeatherContext &wc)
539 H_AUTO_USE(RZ_WeatherManagerClient)
540 // convert day part to seconds
541 nlassert(wc.LC);
542 nlassert(wc.WFP);
543 double timeInSeconds = day * (double) wc.LC->RealDayLength + ((double) hour / wc.LC->NumHours) * wc.LC->RealDayLength;
545 // convert date in second to the thunder cycle date
546 double cycle = timeInSeconds / wc.WFP->MinThunderPeriod;
547 dest.Cycle = (uint64) cycle;
548 dest.SubCycle = (float) fmod(cycle, 1);
551 /** Convert CThunderTime cycle number to a day / hour pair
553 static inline void toGlobalTime(uint64 &day, float &hour, uint64 cycle, const CWeatherContext &wc)
555 H_AUTO_USE(RZ_WeatherManagerClient)
556 nlassert(wc.WFP);
557 nlassert(wc.LC);
558 // convert to global time in seconds
559 double timeInSeconds = cycle * (double) wc.WFP->MinThunderPeriod;
560 double dDay;
561 dDay = timeInSeconds / wc.LC->RealDayLength;
562 day = (uint64) floor(dDay);
563 hour = (float) fmod((float)dDay, wc.LC->NumHours);
566 /** Difference between 2 thunder time, expressed in thunder cycles
568 static inline float diff(const CThunderTime &t0, const CThunderTime &t1)
570 H_AUTO_USE(RZ_WeatherManagerClient)
571 if (t0.Cycle == t1.Cycle) return t1.SubCycle - t0.SubCycle;
572 return 1.f - t0.SubCycle + t1.SubCycle + (float) (t1.Cycle - t0.Cycle - 1);
575 /** Add the given number of cycle to a CThunderTime
577 static inline void add(CThunderTime &dest, float duration)
579 H_AUTO_USE(RZ_WeatherManagerClient)
580 dest.Cycle += (uint64) floorf(duration);
581 dest.SubCycle += fmodf(duration, 1.f); // add fractionnal part
582 dest.Cycle += (uint64) floorf(dest.SubCycle); // add carry
583 dest.SubCycle = fmodf(dest.SubCycle, 1.f);
586 // blend between 2 thunder times
587 static inline void blend(CThunderTime &dest, const CThunderTime &t0, const CThunderTime &t1, float blendFactor)
589 H_AUTO_USE(RZ_WeatherManagerClient)
590 dest = t0;
591 add(dest, blendFactor * diff(t0, t1));
594 //================================================================================================
595 /** Generate a new thunder strike if the weather function goes over the threshold
596 * \return true if a strike wa generated
598 bool CWeatherManagerClient::updateThunderState(CThunderTime &t0, CThunderTime &t1, float thunderThreshold)
600 H_AUTO_USE(RZ_WeatherManagerClient)
601 float thunderValue0 = evalThunderFunction(t0);
602 float thunderValue1 = evalThunderFunction(t1);
603 if (thunderValue0 <= thunderThreshold && thunderValue1 > thunderThreshold)
605 // The value went over the threshold -> generate a thunder strike
606 // See at which date the strike started exactly
607 float blendFactor = (thunderThreshold - thunderValue0) / (thunderValue1 - thunderValue0);
608 // set the strike date
609 blend(_ThunderStrikeDate, t0, t1, blendFactor);
610 _ThunderStrike = true;
611 return true;
613 return false;
616 //================================================================================================
617 void CWeatherManagerClient::updateThunder(uint64 day, float hour, const CWeatherContext &wc, bool manual, float manualWeatherValue, EGSPD::CSeason::TSeason manualSeason)
619 H_AUTO_USE(RZ_WeatherManagerClient)
620 // we use a random function that trigger thunder.
621 // A thunder strike happens when the function is above of a threshold.
622 // The function needs to get back below the threshold before another thunder strike can happen
624 // first, build the 2 dates between which we should evaluate the thunder value;
625 CThunderTime t0;
626 CThunderTime t1;
629 float manualThunderThreshold = 0.f;
630 if (manual)
632 if (wc.WF)
634 manualThunderThreshold = 1.f - 0.5f * wc.WF[manualSeason].getThunderIntensity(manualWeatherValue);
636 else
638 manualThunderThreshold = 0.5f;
643 toThunderTime(_LastEvalDay, _LastEvalHour, t0, wc); // start date
644 toThunderTime(day, hour, t1, wc); // end date
646 if (diff(t0, t1) > 1) // we don't need to look further that 1 thunder cycle to the past, because there can be only one thunder strike every other cycles.
648 t0.Cycle = t1.Cycle - 1;
649 t0.SubCycle = t1.SubCycle;
652 if (t0.Cycle == t1.Cycle) // we stay in the same cycle, so the function is linear
654 float threshold = manual ? manualThunderThreshold : getThunderThreshold(t0.Cycle, wc);
655 updateThunderState(t0, t1, threshold);
657 else
659 // Change of thunder cycle between the 2 dates
660 CThunderTime middleTime;
661 middleTime.Cycle = t1.Cycle;
662 middleTime.SubCycle = 0.f;
663 float threshold = manual ? manualThunderThreshold : getThunderThreshold(t0.Cycle,wc);
664 bool strike = updateThunderState(t0, middleTime, threshold); // eval end of previous cycle
665 if (!strike) // we are sure there won't be another strike during the next cycle
667 threshold = manual ? manualThunderThreshold : getThunderThreshold(t1.Cycle,wc);
668 updateThunderState(middleTime, t1, threshold); // eval start of current cycle
672 // update the thunder value
673 nlassert(wc.WFP);
674 if (_ThunderStrike)
676 // if too much time has ellapsed since the last thunder strike, disable it
677 float timeEllapsedSinceStrike = diff(_ThunderStrikeDate, t1) * wc.WFP->MinThunderPeriod ;
678 if (timeEllapsedSinceStrike >= wc.WFP->ThunderLength)
680 _ThunderStrike = false;
681 _ThunderLevel = 0.f;
683 else
685 _ThunderLevel = wc.WFP->ThunderLength != 0.f ? 1.f - (timeEllapsedSinceStrike / wc.WFP->ThunderLength)
686 : 0.f;
691 //================================================================================================
692 float CWeatherManagerClient::getThunderIntensity(uint64 day, float hour, const CWeatherContext &wc)
694 H_AUTO_USE(RZ_WeatherManagerClient)
695 if (!wc.WF) return 0.f;
696 nlassert(wc.WFP);
697 float weatherValue = ::getBlendedWeather(day, hour, *(wc.WFP), wc.WF);
698 uint season = CRyzomTime::getSeasonByDay((uint32)day);
699 return wc.WF[season].getThunderIntensity(weatherValue * (wc.WF[season].getNumWeatherSetups() - 1));
702 //================================================================================================
703 float CWeatherManagerClient::getThunderThreshold(uint64 thunderCycle, const CWeatherContext &wc)
705 H_AUTO_USE(RZ_WeatherManagerClient)
706 // convert the thunder cycle to a day / hour pair
707 uint64 day;
708 float hour;
709 toGlobalTime(day, hour, thunderCycle, wc);
710 return 1.f - 0.5f * getThunderIntensity(day, hour, wc);
713 // ***************************************************************************
715 CWeatherContext::CWeatherContext ()
717 H_AUTO_USE(RZ_WeatherManagerClient)
718 if (WeatherFunctionParams == NULL) WeatherFunctionParams = new CWeatherFunctionParamsSheet;
719 WFP = WeatherFunctionParams;
720 LC = &WorldLightCycle;
721 WF = NULL;
722 GR = NULL;
725 // ***************************************************************************