// Ryzom - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#include "stdpch.h"
//
#include "game_share/light_cycle.h"
#include "game_share/time_weather_season/weather_predict.h"
//
#include "client_sheets/weather_function_params_sheet.h"
//
#include "weather.h"
#include "weather_manager_client.h"
#include "weather_setup_client.h"
#include "precipitation.h"
#include "continent.h"
#include "sheet_manager.h"
#include "sound_manager.h"
#include "client_cfg.h"
H_AUTO_DECL(RZ_WeatherManagerClient)
using namespace NLMISC;
using namespace std;
extern NL3D::UScene *Scene;
extern NL3D::ULandscape *Landscape;
//================================================================================================
CWeatherManagerClient::CWeatherManagerClient() : _WindDir(0, 0, 0),
_WeatherValue(0),
_ThunderLevel(0),
_ThunderStrike(false),
_LastEvalHour(0),
_LastEvalDay(0),
_LocalPrecipitationFactor(0.f)
{
}
//================================================================================================
void CWeatherManagerClient::init()
{
// Get the list of the weather setups sheets
std::vector result;
CSheetId::buildIdVector(result, "weather_setup");
std::vector wsSheets;
std::vector wsSheetsNames;
for(uint k = 0; k < result.size(); ++k)
{
CEntitySheet *sheet = SheetMngr.get(result[k]);
if (sheet && sheet->type() == CEntitySheet::WEATHER_SETUP)
{
//nlwarning("Sheet name = %s, weather setup = %s", result[k].toString().c_str(), NLMISC::CStringMapper::unmap((dynamic_cast(sheet))->SetupName).c_str());
wsSheetsNames.push_back(result[k].toString());
wsSheets.push_back(dynamic_cast(sheet));
}
}
init(wsSheets, wsSheetsNames);
}
//================================================================================================
CWeatherSetup *CWeatherManagerClient::newWeatherSetup() const
{
// client version of weather setup
return new CWeatherSetupClient;
}
//================================================================================================
void CWeatherManagerClient::setupLoaded(CWeatherSetup *setup)
{
CWeatherSetupClient *wsc = NLMISC::safe_cast(setup);
wsc->WeatherStateClient.setup(setup->WeatherState, _PrecipitationMap);
}
//================================================================================================
void CWeatherManagerClient::init(const std::vector &sheets, const std::vector &sheetNames)
{
CWeatherManager::init(sheets, sheetNames);
initPrecipitationFXs();
}
//================================================================================================
void CWeatherManagerClient::initPrecipitationFXs()
{
CPrecipitationDesc desc;
for(TPrecipitationMap::iterator it = _PrecipitationMap.begin(); it != _PrecipitationMap.end(); ++it)
{
desc.FxName = it->first + ".ps";
desc.ReleasableModel = true;
desc.GridSize = 7;
desc.UseBBoxSize = true;
it->second.init(desc);
}
}
//================================================================================================
/** Update a vector of precipitation with the given pos
*/
static void updatePrecipitationVect(std::vector &vect, const NLMISC::CMatrix &camMat, NLPACS::UGlobalRetriever *gr)
{
H_AUTO_USE(RZ_WeatherManagerClient)
for(std::vector::iterator it = vect.begin(); it != vect.end(); ++it)
{
(*it)->update(camMat, gr);
}
}
//================================================================================================
void CWeatherManagerClient::update(uint64 day, float hour, const CWeatherContext &wc)
{
H_AUTO_USE(RZ_WeatherManagerClient)
// get the weather value for the current date
nlassert(wc.WFP);
float weatherValue = ::getBlendedWeather(day, hour, *(wc.WFP), wc.WF);
// build current weather state
EGSPD::CSeason::TSeason season = CRyzomTime::getSeasonByDay((uint32)day);
//
manualUpdateImpl(day, hour, wc, weatherValue, season);
_LastEvalHour = hour;
_LastEvalDay = day;
}
//================================================================================================
void CWeatherManagerClient::update(uint64 day, float hour, const CWeatherContext &wc, const NLMISC::CMatrix &camMat, const CContinent &continent)
{
H_AUTO_USE(RZ_WeatherManagerClient)
// get the weather value for the current date
nlassert(wc.WFP);
float weatherValue = ::getBlendedWeather(day, hour, *(wc.WFP), wc.WF);
// calculate next weather cycle and ingame hour for it
uint64 cycle = ((day * wc.WFP->DayLength) + (uint) hour) / wc.WFP->CycleLength;
uint64 cycleDay = ((cycle + 1) * wc.WFP->CycleLength) / wc.WFP->DayLength;
_NextWeatherHour = ((cycle + 1) * wc.WFP->CycleLength) % wc.WFP->DayLength;
_NextWeatherValue = ::getBlendedWeather(cycleDay, _NextWeatherHour, *(wc.WFP), wc.WF);
// build current weather state
EGSPD::CSeason::TSeason season = CRyzomTime::getSeasonByDay((uint32)day);
//
manualUpdateImpl(day, hour, wc, weatherValue, season, camMat, continent);
_LastEvalHour = hour;
_LastEvalDay = day;
}
//================================================================================================
void CWeatherManagerClient::manualUpdate(uint64 day, float hour, const CWeatherContext &wc, float weatherValue, EGSPD::CSeason::TSeason season, const NLMISC::CMatrix &camMat, const CContinent &continent)
{
manualUpdateImpl(day, hour, wc, weatherValue, season, camMat, continent);
_LastEvalHour = hour;
_LastEvalDay = day;
}
//================================================================================================
void CWeatherManagerClient::manualUpdateImpl(uint64 day, float hour, const CWeatherContext &wc, float weatherValue, EGSPD::CSeason::TSeason season, const NLMISC::CMatrix &camMat, const CContinent &continent)
{
H_AUTO_USE(RZ_WeatherManagerClient)
if (!wc.WF) return;
manualUpdateImpl(day, hour, wc, weatherValue, season);
setupFXs(camMat, wc.GR, continent);
setupWind(&(wc.WF[season]));
float scaledWeatherValue = weatherValue * (wc.WF[season].getNumWeatherSetups() - 1);
updateThunder(day, hour, wc, true, scaledWeatherValue, season);
// Sound stuff
if (SoundMngr != 0)
{
const CWeatherSetup *floorSetup, *ceilSetup;
float blendFactor;
wc.WF[season].getClosestWeatherSetups(scaledWeatherValue, floorSetup, ceilSetup, blendFactor);
float userVarClouds1 = 0.f;
float userVarClouds2 = 0.f;
float userVarStorm = 0.f;
//
static TStringId strClouds1 = CStringMapper::map("clouds1");
static TStringId strClouds2 = CStringMapper::map("clouds2");
static TStringId strHumidity1 = CStringMapper::map("humidity1");
static TStringId strHumidity2 = CStringMapper::map("humidity2");
static TStringId strStorm= CStringMapper::map("storm");
// contrib from previous setup
if (_CurrWeatherState.FirstSetupName == strClouds1 ||
_CurrWeatherState.FirstSetupName == strHumidity1)
{
if (floorSetup && !floorSetup->WeatherState.FXInfos.empty() && !floorSetup->WeatherState.FXInfos[0].Name.empty())
{
userVarClouds1 += (1.f - _CurrWeatherState.BlendFactor) * _LocalPrecipitationFactor * floorSetup->WeatherState.FXInfos[0].Ratio;
}
}
else
if (_CurrWeatherState.FirstSetupName == strClouds2 ||
_CurrWeatherState.FirstSetupName == strHumidity2)
{
if (floorSetup && !floorSetup->WeatherState.FXInfos.empty() && !floorSetup->WeatherState.FXInfos[0].Name.empty())
{
userVarClouds2 += (1.f - _CurrWeatherState.BlendFactor) * _LocalPrecipitationFactor * floorSetup->WeatherState.FXInfos[0].Ratio;
}
}
else
if (_CurrWeatherState.FirstSetupName == strStorm)
{
if (floorSetup && !floorSetup->WeatherState.FXInfos.empty() && floorSetup->WeatherState.FXInfos[0].Name.empty())
{
userVarClouds2 += (1.f - _CurrWeatherState.BlendFactor) * _LocalPrecipitationFactor * floorSetup->WeatherState.FXInfos[0].Ratio;
}
userVarStorm += (1.f - _CurrWeatherState.BlendFactor) * _LocalPrecipitationFactor;
}
// contrib from next setup
if (_CurrWeatherState.SecondSetupName == strClouds1 ||
_CurrWeatherState.SecondSetupName == strHumidity1)
{
if (ceilSetup && !ceilSetup->WeatherState.FXInfos.empty() && !ceilSetup->WeatherState.FXInfos[0].Name.empty())
{
userVarClouds1 += _CurrWeatherState.BlendFactor * _LocalPrecipitationFactor * ceilSetup->WeatherState.FXInfos[0].Ratio;
}
}
else
if (_CurrWeatherState.SecondSetupName == strClouds2 ||
_CurrWeatherState.SecondSetupName == strHumidity2)
{
if (ceilSetup && !ceilSetup->WeatherState.FXInfos.empty() && !ceilSetup->WeatherState.FXInfos[0].Name.empty())
{
userVarClouds2 += _CurrWeatherState.BlendFactor * _LocalPrecipitationFactor * ceilSetup->WeatherState.FXInfos[0].Ratio;
}
}
else
if (_CurrWeatherState.SecondSetupName == strStorm)
{
if (ceilSetup && !ceilSetup->WeatherState.FXInfos.empty() && !ceilSetup->WeatherState.FXInfos[0].Name.empty())
{
userVarClouds2 += _CurrWeatherState.BlendFactor * _LocalPrecipitationFactor * ceilSetup->WeatherState.FXInfos[0].Ratio;
}
userVarStorm += _CurrWeatherState.BlendFactor * _LocalPrecipitationFactor;
}
// update vars
SoundMngr->getMixer()->setUserVar(strClouds1, 1.f - (1.f - userVarClouds1) * (1.f - userVarClouds1) * (1.f - userVarClouds1));
SoundMngr->getMixer()->setUserVar(strClouds2, 1.f - (1.f - userVarClouds2) * (1.f - userVarClouds2) * (1.f - userVarClouds2));
SoundMngr->getMixer()->setUserVar(strStorm, 1.f - (1.f - userVarStorm) * (1.f - userVarStorm) * (1.f - userVarStorm));
}
}
//================================================================================================
void CWeatherManagerClient::manualUpdate(uint64 day, float hour, const CWeatherContext &wc, float weatherValue, EGSPD::CSeason::TSeason season)
{
manualUpdateImpl(day, hour, wc, weatherValue, season);
_LastEvalHour = hour;
_LastEvalDay = day;
}
//================================================================================================
void CWeatherManagerClient::manualUpdateImpl(uint64 day, float hour, const CWeatherContext &wc, float weatherValue, EGSPD::CSeason::TSeason season)
{
H_AUTO_USE(RZ_WeatherManagerClient)
if (!wc.WF) return;
_WeatherValue = weatherValue;
nlassert(season < EGSPD::CSeason::Invalid);
float scaledWeatherValue = weatherValue * (wc.WF[season].getNumWeatherSetups() - 1);
const CWeatherSetup *floorSetup, *ceilSetup;
float blendFactor;
wc.WF[season].getClosestWeatherSetups(scaledWeatherValue, floorSetup, ceilSetup, blendFactor);
if (floorSetup && ceilSetup)
{
// blend general part
CWeatherState::blend(_CurrWeatherState, floorSetup->WeatherState, ceilSetup->WeatherState, blendFactor);
// blend client specific part
CWeatherStateClient::blend(_CurrWeatherStateClient, safe_cast(floorSetup)->WeatherStateClient, safe_cast(ceilSetup)->WeatherStateClient, blendFactor);
}
}
//================================================================================================
void CWeatherManagerClient::setupWind(const CWeatherFunction *wf)
{
H_AUTO_USE(RZ_WeatherManagerClient)
float wi = _CurrWeatherState.WindIntensity;
NLMISC::clamp(_CurrWeatherState.WindIntensity, 0.f, 1.f);
// TEMP
// wind for static & personnal FXs
NL3D::UParticleSystemInstance::setGlobalUserParamValue("WIND", wi);
// wind for vegetables
if (Landscape)
{
float vegetWindFreq;
float vegetBendIntensity;
float vegetBendOffset;
if (!wf)
{
static const float windBendMin = 0.5f; // static for tests in debug mode
static const float startWindBendMin = 0.6f;
vegetWindFreq = 5.f * wi;
vegetBendIntensity = wi;
vegetBendOffset = wi < startWindBendMin ? 0.f : windBendMin * (wi - startWindBendMin) / (1.f - startWindBendMin);
}
else
{
vegetWindFreq = wf->VegetableMinWindFrequency + (wf->VegetableMaxWindFrequency - wf->VegetableMinWindFrequency) * wi;
vegetBendIntensity = wf->VegetableMinBendIntensity + (wf->VegetableMaxBendIntensity - wf->VegetableMinBendIntensity) * wi;
if (wi < wf->VegetableWindIntensityThatStartBendOffset)
{
vegetBendOffset = 0.f;
}
else
{
vegetBendOffset = wf->VegetableMaxBendOffset * (wi - wf->VegetableWindIntensityThatStartBendOffset) / (1.f - wf->VegetableWindIntensityThatStartBendOffset);
}
}
Landscape->setVegetableWind(_WindDir, vegetWindFreq, vegetBendIntensity, vegetBendOffset);
}
// wind for trees
if (Scene)
{
float windTree;
if (wf)
{
windTree = wf->TreeMinWindIntensity + (wf->TreeMaxWindIntensity - wf->TreeMinWindIntensity) * wi;
}
else
{
windTree = 0.1f + 0.9f * wi;
}
Scene->setGlobalWindPower(windTree);
}
}
//================================================================================================
void CWeatherManagerClient::setupFXs(const NLMISC::CMatrix &camMat, NLPACS::UGlobalRetriever *gr, const CContinent &continent)
{
H_AUTO_USE(RZ_WeatherManagerClient)
static TPrecipitationVect askedPrecipitations;
static TPrecipitationVect releasablePrecipitations;
// Read the precipitation local ratio
CVector pos = camMat.getPos();
CRGBAF localNoPrecipitation = continent.FogMap.getMapValue (CFogMapBuild::NoPrecipitation, pos.x, pos.y, CRGBAF(0.f, 0.f, 0.f, 0.f));
_LocalPrecipitationFactor = 1.f - localNoPrecipitation.R;
//
uint numAskedFXs = (uint)_CurrWeatherStateClient.FXs.size();
askedPrecipitations.resize(numAskedFXs);
uint k;
for(k = 0; k < numAskedFXs; ++k)
{
askedPrecipitations[k] = _CurrWeatherStateClient.FXs[k].Precipitation;
_CurrWeatherStateClient.FXs[k].Precipitation->setStrenght (_CurrWeatherStateClient.FXs[k].Ratio * _LocalPrecipitationFactor);
}
std::sort(askedPrecipitations.begin(), askedPrecipitations.end()); // sort em for std::set_
// See which FXs where asked to be removed, but are now needed again (remove them from the waiting/shutting_down list)
releasablePrecipitations.clear();
std::set_difference(_WaitingPrecipitations.begin(), _WaitingPrecipitations.end(), askedPrecipitations.begin(), askedPrecipitations.end(), std::back_inserter(releasablePrecipitations));
/*
if (!releasablePrecipitations.empty())
{
for(uint k = 0; k < releasablePrecipitations.size(); ++k)
{
nlinfo("Shutting down precipitation reused : %s", releasablePrecipitations[k]->getDesc().FxName.c_str() );
}
}
*/
_WaitingPrecipitations.swap(releasablePrecipitations);
// see which FXs are to be removed
// NB : we are working on vectors, but they are likely to be very smalls
releasablePrecipitations.clear();
std::set_difference(_ActivePrecipitations.begin(), _ActivePrecipitations.end(), askedPrecipitations.begin(), askedPrecipitations.end(), std::back_inserter(releasablePrecipitations));
//
for(k = 0; k < releasablePrecipitations.size(); ++k)
{
releasablePrecipitations[k]->setStrenght(0);
}
/*
if (!releasablePrecipitations.empty())
{
for(uint k = 0; k < releasablePrecipitations.size(); ++k)
{
nlinfo("Precipitation put in shutting down list : %s", releasablePrecipitations[k]->getDesc().FxName.c_str() );
}
}
*/
// put in the waiting precipitation list
_WaitingPrecipitations.insert(_WaitingPrecipitations.end(), releasablePrecipitations.begin(), releasablePrecipitations.end());
// set new active FX list
_ActivePrecipitations.swap(askedPrecipitations);
// tmp for debug : dump precipitation list if it has changed
/*
if (_ActivePrecipitations.size() != askedPrecipitations.size() ||
!std::equal(_ActivePrecipitations.begin(), _ActivePrecipitations.end(), askedPrecipitations.begin()) )
{
for(uint k = 0; k < _ActivePrecipitations.size(); ++k)
{
nlinfo("New precipitation list : %s", _ActivePrecipitations[k]->getDesc().FxName.c_str() );
}
}
*/
// update precipitations
updatePrecipitationVect(_ActivePrecipitations, camMat, gr);
// update waiting precipitations
updatePrecipitationVect(_WaitingPrecipitations, camMat, gr);
// Remove waiting precipitation that are not running anymore (no more particles)
TPrecipitationVect::iterator lastValid = std::remove_if(_WaitingPrecipitations.begin(), _WaitingPrecipitations.end(), std::not1(std::mem_fun(&CPrecipitation::isRunning)));
_WaitingPrecipitations.erase(lastValid, _WaitingPrecipitations.end());
}
//================================================================================================
void CWeatherManagerClient::setWindDir(const NLMISC::CVector &dir)
{
H_AUTO_USE(RZ_WeatherManagerClient)
_WindDir.set(dir.x, dir.y, 0.f);
_WindDir.normalize();
NL3D::UParticleSystemInstance::setGlobalVectorValue("WIND", _WindDir);
}
//================================================================================================
void CWeatherManagerClient::computeCloudState(uint64 day, float hour, const CWeatherContext &wc, CCloudState &dest) const
{
H_AUTO_USE(RZ_WeatherManagerClient)
if (!wc.WF)
{
dest = CCloudState();
return;
}
nlassert(wc.WFP);
float weatherValue = ::getBlendedWeather(day, hour, *(wc.WFP), wc.WF);
// build current weather state
uint season = CRyzomTime::getSeasonByDay((uint32)day);
computeCloudState(weatherValue, (EGSPD::CSeason::TSeason) season, dest, wc.WF);
}
//================================================================================================
void CWeatherManagerClient::computeCloudState(float weatherValue, EGSPD::CSeason::TSeason season, CCloudState &dest, const CWeatherFunction wf[EGSPD::CSeason::Invalid]) const
{
H_AUTO_USE(RZ_WeatherManagerClient)
if (!wf)
{
dest = CCloudState();
return;
}
nlassert(season < EGSPD::CSeason::Invalid);
wf[season].getCloudState(weatherValue * (wf[season].getNumWeatherSetups() - 1), dest);
}
//================================================================================================
void CWeatherManagerClient::release()
{
for(TPrecipitationMap::iterator it = _PrecipitationMap.begin(); it != _PrecipitationMap.end(); ++it)
{
it->second.release();
}
NLMISC::contReset(_PrecipitationMap);
NLMISC::contReset(_ActivePrecipitations);
NLMISC::contReset(_WaitingPrecipitations);
CWeatherManager::release();
_CurrWeatherStateClient = CWeatherStateClient();
}
//================================================================================================
void CWeatherManagerClient::drawPrecipitationClipGrids(NL3D::UDriver &drv) const
{
for(TPrecipitationVect::const_iterator it = _ActivePrecipitations.begin(); it != _ActivePrecipitations.end(); ++it)
{
(*it)->drawClipGrid(drv);
}
}
//================================================================================================
// alias for the thunder time measure
typedef CWeatherManagerClient::CThunderTimeMeasure CThunderTime;
static inline bool operator == (const CThunderTime &lhs, const CThunderTime &rhs)
{
return lhs.Cycle == rhs.Cycle && lhs.SubCycle == rhs.SubCycle;
}
static inline bool operator != (const CThunderTime &lhs, const CThunderTime &rhs)
{
return !(lhs == rhs);
}
/** Eval the thunder function value at the given date
*/
static float evalThunderFunction(const CThunderTime &tt)
{
H_AUTO_USE(RZ_WeatherManagerClient)
// cache previous result
static CThunderTime lastTime;
static float lastValue;
if (tt != lastTime)
{
CRandom rnd;
rnd.srand((uint32) (tt.Cycle & 0xFFFFFFFF));
sint32 dummy = rnd.rand();
dummy = rnd.rand();
dummy = rnd.rand();
float v0 = rnd.frand(1.f);
rnd.srand((uint32) ((tt.Cycle + 1) & 0xFFFFFFFF));
dummy = rnd.rand();
dummy = rnd.rand();
dummy = rnd.rand();
float v1 = rnd.frand(1.f);
lastValue = tt.SubCycle * v1 + (1.f - tt.SubCycle) * v0;
lastTime = tt;
}
return lastValue;
}
/** Convert a day / hour to a CThunderTime
*/
static inline void toThunderTime(uint64 day, float hour, CThunderTime &dest, const CWeatherContext &wc)
{
H_AUTO_USE(RZ_WeatherManagerClient)
// convert day part to seconds
nlassert(wc.LC);
nlassert(wc.WFP);
double timeInSeconds = day * (double) wc.LC->RealDayLength + ((double) hour / wc.LC->NumHours) * wc.LC->RealDayLength;
// convert date in second to the thunder cycle date
double cycle = timeInSeconds / wc.WFP->MinThunderPeriod;
dest.Cycle = (uint64) cycle;
dest.SubCycle = (float) fmod(cycle, 1);
}
/** Convert CThunderTime cycle number to a day / hour pair
*/
static inline void toGlobalTime(uint64 &day, float &hour, uint64 cycle, const CWeatherContext &wc)
{
H_AUTO_USE(RZ_WeatherManagerClient)
nlassert(wc.WFP);
nlassert(wc.LC);
// convert to global time in seconds
double timeInSeconds = cycle * (double) wc.WFP->MinThunderPeriod;
double dDay;
dDay = timeInSeconds / wc.LC->RealDayLength;
day = (uint64) floor(dDay);
hour = (float) fmod((float)dDay, wc.LC->NumHours);
}
/** Difference between 2 thunder time, expressed in thunder cycles
*/
static inline float diff(const CThunderTime &t0, const CThunderTime &t1)
{
H_AUTO_USE(RZ_WeatherManagerClient)
if (t0.Cycle == t1.Cycle) return t1.SubCycle - t0.SubCycle;
return 1.f - t0.SubCycle + t1.SubCycle + (float) (t1.Cycle - t0.Cycle - 1);
}
/** Add the given number of cycle to a CThunderTime
*/
static inline void add(CThunderTime &dest, float duration)
{
H_AUTO_USE(RZ_WeatherManagerClient)
dest.Cycle += (uint64) floorf(duration);
dest.SubCycle += fmodf(duration, 1.f); // add fractionnal part
dest.Cycle += (uint64) floorf(dest.SubCycle); // add carry
dest.SubCycle = fmodf(dest.SubCycle, 1.f);
}
// blend between 2 thunder times
static inline void blend(CThunderTime &dest, const CThunderTime &t0, const CThunderTime &t1, float blendFactor)
{
H_AUTO_USE(RZ_WeatherManagerClient)
dest = t0;
add(dest, blendFactor * diff(t0, t1));
}
//================================================================================================
/** Generate a new thunder strike if the weather function goes over the threshold
* \return true if a strike wa generated
*/
bool CWeatherManagerClient::updateThunderState(CThunderTime &t0, CThunderTime &t1, float thunderThreshold)
{
H_AUTO_USE(RZ_WeatherManagerClient)
float thunderValue0 = evalThunderFunction(t0);
float thunderValue1 = evalThunderFunction(t1);
if (thunderValue0 <= thunderThreshold && thunderValue1 > thunderThreshold)
{
// The value went over the threshold -> generate a thunder strike
// See at which date the strike started exactly
float blendFactor = (thunderThreshold - thunderValue0) / (thunderValue1 - thunderValue0);
// set the strike date
blend(_ThunderStrikeDate, t0, t1, blendFactor);
_ThunderStrike = true;
return true;
}
return false;
}
//================================================================================================
void CWeatherManagerClient::updateThunder(uint64 day, float hour, const CWeatherContext &wc, bool manual, float manualWeatherValue, EGSPD::CSeason::TSeason manualSeason)
{
H_AUTO_USE(RZ_WeatherManagerClient)
// we use a random function that trigger thunder.
// A thunder strike happens when the function is above of a threshold.
// The function needs to get back below the threshold before another thunder strike can happen
// first, build the 2 dates between which we should evaluate the thunder value;
CThunderTime t0;
CThunderTime t1;
float manualThunderThreshold = 0.f;
if (manual)
{
if (wc.WF)
{
manualThunderThreshold = 1.f - 0.5f * wc.WF[manualSeason].getThunderIntensity(manualWeatherValue);
}
else
{
manualThunderThreshold = 0.5f;
}
}
toThunderTime(_LastEvalDay, _LastEvalHour, t0, wc); // start date
toThunderTime(day, hour, t1, wc); // end date
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.
{
t0.Cycle = t1.Cycle - 1;
t0.SubCycle = t1.SubCycle;
}
if (t0.Cycle == t1.Cycle) // we stay in the same cycle, so the function is linear
{
float threshold = manual ? manualThunderThreshold : getThunderThreshold(t0.Cycle, wc);
updateThunderState(t0, t1, threshold);
}
else
{
// Change of thunder cycle between the 2 dates
CThunderTime middleTime;
middleTime.Cycle = t1.Cycle;
middleTime.SubCycle = 0.f;
float threshold = manual ? manualThunderThreshold : getThunderThreshold(t0.Cycle,wc);
bool strike = updateThunderState(t0, middleTime, threshold); // eval end of previous cycle
if (!strike) // we are sure there won't be another strike during the next cycle
{
threshold = manual ? manualThunderThreshold : getThunderThreshold(t1.Cycle,wc);
updateThunderState(middleTime, t1, threshold); // eval start of current cycle
}
}
// update the thunder value
nlassert(wc.WFP);
if (_ThunderStrike)
{
// if too much time has ellapsed since the last thunder strike, disable it
float timeEllapsedSinceStrike = diff(_ThunderStrikeDate, t1) * wc.WFP->MinThunderPeriod ;
if (timeEllapsedSinceStrike >= wc.WFP->ThunderLength)
{
_ThunderStrike = false;
_ThunderLevel = 0.f;
}
else
{
_ThunderLevel = wc.WFP->ThunderLength != 0.f ? 1.f - (timeEllapsedSinceStrike / wc.WFP->ThunderLength)
: 0.f;
}
}
}
//================================================================================================
float CWeatherManagerClient::getThunderIntensity(uint64 day, float hour, const CWeatherContext &wc)
{
H_AUTO_USE(RZ_WeatherManagerClient)
if (!wc.WF) return 0.f;
nlassert(wc.WFP);
float weatherValue = ::getBlendedWeather(day, hour, *(wc.WFP), wc.WF);
uint season = CRyzomTime::getSeasonByDay((uint32)day);
return wc.WF[season].getThunderIntensity(weatherValue * (wc.WF[season].getNumWeatherSetups() - 1));
}
//================================================================================================
float CWeatherManagerClient::getThunderThreshold(uint64 thunderCycle, const CWeatherContext &wc)
{
H_AUTO_USE(RZ_WeatherManagerClient)
// convert the thunder cycle to a day / hour pair
uint64 day;
float hour;
toGlobalTime(day, hour, thunderCycle, wc);
return 1.f - 0.5f * getThunderIntensity(day, hour, wc);
}
// ***************************************************************************
CWeatherContext::CWeatherContext ()
{
H_AUTO_USE(RZ_WeatherManagerClient)
if (WeatherFunctionParams == NULL) WeatherFunctionParams = new CWeatherFunctionParamsSheet;
WFP = WeatherFunctionParams;
LC = &WorldLightCycle;
WF = NULL;
GR = NULL;
}
// ***************************************************************************