1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
6 // Copyright (C) 2013-2014 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "light_cycle_manager.h"
26 #include "weather_manager_client.h"
27 #include "game_share/light_cycle.h"
28 #include "game_share/time_weather_season/weather_predict.h"
29 #include "precipitation.h"
30 #include "sheet_manager.h"
31 #include "client_cfg.h"
32 #include "continent.h"
33 #include "time_client.h"
34 #include "interface_v3/interface_manager.h"
36 #include "nel/misc/sheet_id.h"
37 #include "nel/3d/u_particle_system_instance.h"
39 #include "client_sheets/weather_function_params_sheet.h"
41 #include "r2/editor.h"
47 H_AUTO_DECL(RZ_Weather
)
49 CLightCycle WorldLightCycle
;
50 CLightCycleManager LightCycleManager
;
51 CWeatherManagerClient WeatherManager
;
52 CWeatherFunctionParamsSheet
*WeatherFunctionParams
= NULL
;
54 float ManualWeatherValue
= 0.f
;
55 EGSPD::CSeason::TSeason ManualSeasonValue
= EGSPD::CSeason::Spring
;
56 bool ForceTrueWeatherValue
= false;
57 float DayNightCycleHour
= 12.f
;
58 float ForcedDayNightCycleHour
= -1.0f
;
60 EGSPD::CSeason::TSeason CurrSeason
= EGSPD::CSeason::Invalid
;
61 EGSPD::CSeason::TSeason StartupSeason
= EGSPD::CSeason::Invalid
;
63 extern NLPACS::UGlobalRetriever
*GR
;
65 using namespace NLMISC
;
69 if( WeatherFunctionParams
)
70 delete WeatherFunctionParams
;
71 WeatherFunctionParams
= NULL
;
74 void buildLightCycleDesc(CLightCycleDesc
&dest
,EGSPD::CSeason::TSeason season
)
76 if (season
>= EGSPD::CSeason::Invalid
)
78 nlwarning("Invalid season");
81 dest
.NumHours
= WorldLightCycle
.NumHours
;
82 dest
.RealDayLength
= WorldLightCycle
.RealDayLength
;
83 dest
.MaxNumColorSteps
= WorldLightCycle
.MaxNumColorSteps
;
84 const CSeasonLightCycle
&slc
= WorldLightCycle
.SeasonLightCycle
[season
];
85 dest
.NightTransitionStartHour
= slc
.DayToDuskHour
;
86 dest
.NightTransitionEndHour
= slc
.NightHour
;
87 dest
.DawnTransitionStartHour
= slc
.NightToDayHour
;
88 dest
.DawnTransitionEndHour
= slc
.DayHour
;
90 float intervalDayNight
= slc
.NightHour
>= slc
.DayToDuskHour
? slc
.NightHour
- slc
.DayToDuskHour
: WorldLightCycle
.NumHours
- slc
.DayToDuskHour
+ slc
.NightHour
;
91 float intervalDayDusk
= slc
.DuskToNightHour
>= slc
.DayToDuskHour
? slc
.DuskToNightHour
- slc
.DayToDuskHour
: WorldLightCycle
.NumHours
- slc
.DayToDuskHour
+ slc
.DuskToNightHour
;
93 if (intervalDayDusk
!= 0.f
) dest
.DuskRatio
= intervalDayDusk
/ intervalDayNight
;
94 else dest
.DuskRatio
= 0.5f
;
97 void loadWorldLightCycle()
99 CEntitySheet
*sheet
= SheetMngr
.get(CSheetId("ryzom.light_cycle"));
101 if (sheet
->type() != CEntitySheet::LIGHT_CYCLE
)
103 nlwarning("Bad type for light cycle sheet");
106 CLightCycleSheet
*lightCycleSheet
= (CLightCycleSheet
*) sheet
;
107 WorldLightCycle
= lightCycleSheet
->LightCycle
;
110 void loadWeatherFunctionParams()
112 if (WeatherFunctionParams
== NULL
) WeatherFunctionParams
= new CWeatherFunctionParamsSheet
;
114 if (ClientCfg
.ManualWeatherSetup
)
116 WeatherFunctionParams
->CWeatherFunctionParamsSheetBase::build("ryzom.weather_function_params");
120 // Now, just get it from the sheet manager
121 std::vector
<CSheetId
> result
;
122 CSheetId::buildIdVector(result
, "weather_function_params");
125 nlwarning("Couldn't load weather_function_params");
128 if (result
.size() != 1)
130 nlwarning("Multiple .weather_function_params sheets available, taking first");
132 CEntitySheet
*sheet
= SheetMngr
.get(result
[0]);
133 if (sheet
->type() != CEntitySheet::WEATHER_FUNCTION_PARAMS
)
135 nlwarning("Bad type for weather_function_params sheet");
138 WeatherFunctionParams
= (CWeatherFunctionParamsSheet
*) sheet
;
142 // ***************************************************************************
144 void updateWeatherManager(CContinent
*continent
)
146 H_AUTO_USE(RZ_Weather
)
147 // build a weather context
150 wc
.LC
= &WorldLightCycle
;
151 wc
.WF
= continent
->WeatherFunction
;
153 // Update the weather manager
154 // NB nico : even when light hour is forced, we use the real time to compute the weather (so that the weather will reflect the real one, even if hour & light is fixed)
155 if(ClientCfg
.ManualWeatherSetup
&& !ForceTrueWeatherValue
)
157 WeatherManager
.manualUpdate(RT
.getRyzomDay(), (float) RT
.getRyzomTime(), wc
, ManualWeatherValue
, CurrSeason
);
161 WeatherManager
.update(RT
.getRyzomDay(), (float) RT
.getRyzomTime(), wc
);
165 // ***************************************************************************
167 void updateWeatherManager(const NLMISC::CMatrix
&camMatrix
, CContinent
*continent
)
169 H_AUTO_USE(RZ_Weather
)
170 // build a weather context
174 wc
.WF
= continent
->WeatherFunction
;
180 if(ClientCfg
.ManualWeatherSetup
&& !ForceTrueWeatherValue
)
182 WeatherManager
.manualUpdate(RT
.getRyzomDay(), (float) RT
.getRyzomTime(), wc
, ManualWeatherValue
, CurrSeason
, camMatrix
, *continent
);
186 // NB nico : even when light hour is forced, we use the real time to compute the weather (so that the weather will reflect the real one, even if hour & light is fixed)
187 WeatherManager
.update(RT
.getRyzomDay(), RT
.getRyzomTime(), wc
, camMatrix
, *continent
);
198 float LocalServerWeather
= 0.f
; // 'local' server driven weather, trying to reach the weather value given in the database as time passes (for smooth transition)
199 float ServerWeatherBlendFactor
= 0.f
; // blend factor between server driven weather & local computed pseudo random weather (based on the current date, gives same result for all clients)
200 bool ServerDrivenWeather
= false;
202 const float WEATHER_BLEND_SPEED
= 1.f
/ 8.f
; // number of seconds to blend betwen weather states
204 static NLMISC::CRefPtr
<NLMISC::CCDBNodeLeaf
> s_ServerWeatherValueDB
;
206 // ***************************************************************************
207 static uint16
getServerWeather()
209 CCDBNodeLeaf
*node
= s_ServerWeatherValueDB
? &*s_ServerWeatherValueDB
210 : &*(s_ServerWeatherValueDB
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:WEATHER:VALUE"));
212 return (uint16
) node
->getValue16();
215 // ***************************************************************************
216 void updateDBDrivenWeatherValue()
218 uint16 dbWeather
= getServerWeather();
222 ServerDrivenWeather
= false;
223 NLMISC::incrementalBlend(ServerWeatherBlendFactor
, 0.f
, WEATHER_BLEND_SPEED
* DT
);
227 float targetWeather
= (dbWeather
- 1) / 1022.f
;
228 if (!ServerDrivenWeather
)
230 // just switched to server driven weather ?
231 LocalServerWeather
= targetWeather
;
232 ServerDrivenWeather
= true;
234 // slowly blend from predicted weather to server driven weather
235 NLMISC::incrementalBlend(ServerWeatherBlendFactor
, 1.f
, WEATHER_BLEND_SPEED
* DT
);
237 // slowly blend between server driven weathers as values in the db changes
238 NLMISC::incrementalBlend(LocalServerWeather
, targetWeather
, WEATHER_BLEND_SPEED
* DT
);
243 // ***************************************************************************
244 float getBlendedWeather(uint64 day
,
246 const CWeatherFunctionParamsSheetBase
&wfp
,
247 const CWeatherFunction wf
[EGSPD::CSeason::Invalid
]
250 return NLMISC::blend(CPredictWeather::predictWeather(day
, hour
, wfp
, wf
), LocalServerWeather
, ServerWeatherBlendFactor
);
253 uint8
getSeasonDBValue()
255 CCDBNodeLeaf
*serverSeason
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:SEASON:VALUE");
256 return serverSeason
? serverSeason
->getValue8() : 0;
263 uint8 ServerSeasonValue
= 0;
264 bool ServerSeasonReceived
= false;
265 static bool WaitingServerSeason
= false;
266 static R2::CEditor::TMode OldR2EDMode
= R2::CEditor::NotInitialized
;
267 static EGSPD::CSeason::TSeason LastSeason
= EGSPD::CSeason::Unknown
;
269 // ***************************************************************************
270 EGSPD::CSeason::TSeason
computeCurrSeason()
272 // Note : when season isn't imposed by the server or by the editor, we use
273 // the season found in StartupSeason
274 // This mean that when the season change due to time, it won't be updated on the client
275 // This is a deliberate choice to avoid possibly disadvantaging freeze on the client while in combat
276 // To have this done correctly, the server should be aware of the change, and freeze action accordingly.
277 // When entering a new area, the 'StartupSeason' variable is updated however, because the player is safe at that time.
278 // (see Continent::select)
280 if (!ClientCfg
.R2EDEnabled
|| !ClientCfg
.Local
) // ... Standard client
283 if (ServerSeasonValue
!= 0)
285 ServerSeasonReceived
= false;
286 // server driven season
287 LastSeason
= (EGSPD::CSeason::TSeason
) ((ServerSeasonValue
- 1) & 3);
291 LastSeason
= ClientCfg
.ManualWeatherSetup
? ManualSeasonValue
: StartupSeason
;
296 // When we change act( different location) the Server TP the player at the good location
297 // So the manual setting of season is useless (R2::getEditor().getSeason()) because
298 // there is a huge risk to (Change to 2 times season)
299 // eg Act1 Season1, Island1, Act2 Season2, Island2
300 // If server tp occurs before, client received change of Season: there will be a unecessary TP
304 // When in edition mode, the editor tell what is the season (it is local and given
305 // by each act of the current scenario, so ignore server value here...)
306 if (R2::getEditor().getMode() == R2::CEditor::EditionMode
)
308 OldR2EDMode
= R2::getEditor().getMode();
309 switch(R2::getEditor().getSeason())
311 case R2::CEditor::Automatic
: LastSeason
= StartupSeason
; break;
312 case R2::CEditor::Spring
: LastSeason
= EGSPD::CSeason::Spring
; break;
313 case R2::CEditor::Summer
: LastSeason
= EGSPD::CSeason::Summer
; break;
314 case R2::CEditor::Autumn
: LastSeason
= EGSPD::CSeason::Autumn
; break;
315 case R2::CEditor::Winter
: LastSeason
= EGSPD::CSeason::Winter
; break;
316 case R2::CEditor::UnknownSeason
:
317 if (LastSeason
== EGSPD::CSeason::Unknown
)
319 // don't change the season unless this is the first call
320 LastSeason
= StartupSeason
; // default
328 // Leaving edit -> Test 'mode' or GoingToTest 'mode' :
329 if (OldR2EDMode
== R2::CEditor::EditionMode
)
331 // a transition from edition mode to test mode has been detected :
332 // At some point the server will impulse the current season, be it the same than the current one
333 // Until this occur, we just retain the last season that was set in the editor, because
334 // season change are VERY costly (several seconds freeze) and we don't want the season to change twice in a row just because we don't
335 // know its real value at this point.
336 WaitingServerSeason
= true;
338 OldR2EDMode
= R2::getEditor().getMode();
339 if (WaitingServerSeason
&& !ServerSeasonReceived
)
343 ServerSeasonReceived
= false;
344 WaitingServerSeason
= false;
345 // back to standard case
346 // At this point we ignore the 'ClientCfg.ManualWeatherSetup' flag, however, because it may be set
347 // when the "change weather" dialog is shown, but we only want to preview weather change, not the season change.
348 if (ServerSeasonValue
!= 0)
350 // server driven season
351 LastSeason
= (EGSPD::CSeason::TSeason
) ((ServerSeasonValue
- 1) & 3);
355 LastSeason
= StartupSeason
;