1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2017 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2016 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
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/>.
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"
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"
41 H_AUTO_DECL(RZ_WeatherManagerClient
)
43 using namespace NLMISC
;
47 extern NL3D::UScene
*Scene
;
48 extern NL3D::ULandscape
*Landscape
;
50 //================================================================================================
51 CWeatherManagerClient::CWeatherManagerClient() : _WindDir(0, 0, 0),
54 _ThunderStrike(false),
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;
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
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
;
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
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
;
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
;
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
)
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
);
192 const CWeatherSetup
*floorSetup
, *ceilSetup
;
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
;
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
;
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
;
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
;
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
;
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
;
279 //================================================================================================
280 void CWeatherManagerClient::manualUpdateImpl(uint64 day
, float hour
, const CWeatherContext
&wc
, float weatherValue
, EGSPD::CSeason::TSeason season
)
282 H_AUTO_USE(RZ_WeatherManagerClient
)
284 _WeatherValue
= weatherValue
;
285 nlassert(season
< EGSPD::CSeason::Invalid
);
286 float scaledWeatherValue
= weatherValue
* (wc
.WF
[season
].getNumWeatherSetups() - 1);
287 const CWeatherSetup
*floorSetup
, *ceilSetup
;
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
);
306 // wind for static & personnal FXs
307 NL3D::UParticleSystemInstance::setGlobalUserParamValue("WIND", wi
);
308 // wind for vegetables
312 float vegetBendIntensity
;
313 float vegetBendOffset
;
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
);
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
;
333 vegetBendOffset
= wf
->VegetableMaxBendOffset
* (wi
- wf
->VegetableWindIntensityThatStartBendOffset
) / (1.f
- wf
->VegetableWindIntensityThatStartBendOffset
);
337 Landscape
->setVegetableWind(_WindDir
, vegetWindFreq
, vegetBendIntensity
, vegetBendOffset
);
345 windTree
= wf
->TreeMinWindIntensity
+ (wf
->TreeMaxWindIntensity
- wf
->TreeMinWindIntensity
) * wi
;
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
);
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
)
446 dest
= CCloudState();
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
)
462 dest
= CCloudState();
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
;
519 rnd
.srand((uint32
) (tt
.Cycle
& 0xFFFFFFFF));
520 sint32 dummy
= rnd
.rand();
523 float v0
= rnd
.frand(1.f
);
524 rnd
.srand((uint32
) ((tt
.Cycle
+ 1) & 0xFFFFFFFF));
528 float v1
= rnd
.frand(1.f
);
529 lastValue
= tt
.SubCycle
* v1
+ (1.f
- tt
.SubCycle
) * v0
;
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
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
)
558 // convert to global time in seconds
559 double timeInSeconds
= cycle
* (double) wc
.WFP
->MinThunderPeriod
;
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
)
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;
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;
629 float manualThunderThreshold
= 0.f
;
634 manualThunderThreshold
= 1.f
- 0.5f
* wc
.WF
[manualSeason
].getThunderIntensity(manualWeatherValue
);
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
);
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
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;
685 _ThunderLevel
= wc
.WFP
->ThunderLength
!= 0.f
? 1.f
- (timeEllapsedSinceStrike
/ wc
.WFP
->ThunderLength
)
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
;
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
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
;
725 // ***************************************************************************