Merge branch '138-toggle-free-look-with-hotkey' into main/gingo-test
[ryzomcore.git] / ryzom / client / src / weather.cpp
blobf4ba5014a7e657b15be6bb083c221f5da8deb63e
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
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>
7 //
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/>.
24 #include "stdpch.h"
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"
43 #ifdef DEBUG_NEW
44 #define new DEBUG_NEW
45 #endif
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;
67 void releaseWeather()
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");
79 return;
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"));
100 nlassert(sheet);
101 if (sheet->type() != CEntitySheet::LIGHT_CYCLE)
103 nlwarning("Bad type for light cycle sheet");
104 return;
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");
118 else
120 // Now, just get it from the sheet manager
121 std::vector <CSheetId> result;
122 CSheetId::buildIdVector(result, "weather_function_params");
123 if (result.empty())
125 nlwarning("Couldn't load weather_function_params");
126 return;
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");
136 return;
138 WeatherFunctionParams = (CWeatherFunctionParamsSheet *) sheet;
142 // ***************************************************************************
144 void updateWeatherManager(CContinent *continent)
146 H_AUTO_USE(RZ_Weather)
147 // build a weather context
148 CWeatherContext wc;
149 wc.GR = NULL;
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);
159 else
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
171 CWeatherContext wc;
172 wc.GR = GR;
173 if(continent)
174 wc.WF = continent->WeatherFunction;
175 else
176 wc.WF = NULL;
178 if (continent)
180 if(ClientCfg.ManualWeatherSetup && !ForceTrueWeatherValue)
182 WeatherManager.manualUpdate(RT.getRyzomDay(), (float) RT.getRyzomTime(), wc, ManualWeatherValue, CurrSeason, camMatrix, *continent);
184 else
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);
193 ///////////////////
194 // WEATHER VALUE //
195 ///////////////////
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"));
211 if (!node) return 0;
212 return (uint16) node->getValue16();
215 // ***************************************************************************
216 void updateDBDrivenWeatherValue()
218 uint16 dbWeather = getServerWeather();
219 if (dbWeather == 0)
221 // not server driven
222 ServerDrivenWeather = false;
223 NLMISC::incrementalBlend(ServerWeatherBlendFactor, 0.f, WEATHER_BLEND_SPEED * DT);
225 else
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,
245 float hour,
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;
259 ////////////
260 // SEASON //
261 ////////////
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
282 // standard case
283 if (ServerSeasonValue != 0)
285 ServerSeasonReceived = false;
286 // server driven season
287 LastSeason = (EGSPD::CSeason::TSeason) ((ServerSeasonValue - 1) & 3);
289 else
291 LastSeason = ClientCfg.ManualWeatherSetup ? ManualSeasonValue : StartupSeason;
293 return LastSeason;
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
322 break;
323 default:
324 nlassert(0);
325 break;
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)
341 return LastSeason;
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);
353 else
355 LastSeason = StartupSeason;
357 return LastSeason;