1 /* Copyright (C) 2017 Wildfire Games.
2 * This file is part of 0 A.D.
3 *
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * 0 A.D. is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include "precompiled.h"
19
20 #include "MapReader.h"
21
22 #include "graphics/Camera.h"
23 #include "graphics/CinemaManager.h"
24 #include "graphics/Entity.h"
25 #include "graphics/GameView.h"
26 #include "graphics/MapGenerator.h"
27 #include "graphics/Patch.h"
28 #include "graphics/Terrain.h"
29 #include "graphics/TerrainTextureEntry.h"
30 #include "graphics/TerrainTextureManager.h"
31 #include "lib/timer.h"
32 #include "lib/external_libraries/libsdl.h"
33 #include "maths/MathUtil.h"
34 #include "ps/CLogger.h"
35 #include "ps/Loader.h"
36 #include "ps/LoaderThunks.h"
37 #include "ps/World.h"
38 #include "ps/XML/Xeromyces.h"
39 #include "renderer/PostprocManager.h"
40 #include "renderer/SkyManager.h"
41 #include "renderer/WaterManager.h"
42 #include "simulation2/Simulation2.h"
43 #include "simulation2/components/ICmpCinemaManager.h"
44 #include "simulation2/components/ICmpObstruction.h"
45 #include "simulation2/components/ICmpOwnership.h"
46 #include "simulation2/components/ICmpPlayer.h"
47 #include "simulation2/components/ICmpPlayerManager.h"
48 #include "simulation2/components/ICmpPosition.h"
49 #include "simulation2/components/ICmpTerrain.h"
50 #include "simulation2/components/ICmpVisual.h"
51 #include "simulation2/components/ICmpWaterManager.h"
52
53 #include <boost/algorithm/string/predicate.hpp>
54
55
CMapReader()56 CMapReader::CMapReader()
57 : xml_reader(0), m_PatchesPerSide(0), m_MapGen(0)
58 {
59 cur_terrain_tex = 0; // important - resets generator state
60 }
61
62 // LoadMap: try to load the map from given file; reinitialise the scene to new data if successful
LoadMap(const VfsPath & pathname,JSRuntime * rt,JS::HandleValue settings,CTerrain * pTerrain_,WaterManager * pWaterMan_,SkyManager * pSkyMan_,CLightEnv * pLightEnv_,CGameView * pGameView_,CCinemaManager * pCinema_,CTriggerManager * pTrigMan_,CPostprocManager * pPostproc_,CSimulation2 * pSimulation2_,const CSimContext * pSimContext_,int playerID_,bool skipEntities)63 void CMapReader::LoadMap(const VfsPath& pathname, JSRuntime* rt, JS::HandleValue settings, CTerrain *pTerrain_,
64 WaterManager* pWaterMan_, SkyManager* pSkyMan_,
65 CLightEnv *pLightEnv_, CGameView *pGameView_, CCinemaManager* pCinema_, CTriggerManager* pTrigMan_, CPostprocManager* pPostproc_,
66 CSimulation2 *pSimulation2_, const CSimContext* pSimContext_, int playerID_, bool skipEntities)
67 {
68 pTerrain = pTerrain_;
69 pLightEnv = pLightEnv_;
70 pGameView = pGameView_;
71 pWaterMan = pWaterMan_;
72 pSkyMan = pSkyMan_;
73 pCinema = pCinema_;
74 pTrigMan = pTrigMan_;
75 pPostproc = pPostproc_;
76 pSimulation2 = pSimulation2_;
77 pSimContext = pSimContext_;
78 m_PlayerID = playerID_;
79 m_SkipEntities = skipEntities;
80 m_StartingCameraTarget = INVALID_ENTITY;
81 m_ScriptSettings.init(rt, settings);
82
83 filename_xml = pathname.ChangeExtension(L".xml");
84
85 // In some cases (particularly tests) we don't want to bother storing a large
86 // mostly-empty .pmp file, so we let the XML file specify basic terrain instead.
87 // If there's an .xml file and no .pmp, then we're probably in this XML-only mode
88 only_xml = false;
89 if (!VfsFileExists(pathname) && VfsFileExists(filename_xml))
90 {
91 only_xml = true;
92 }
93
94 file_format_version = CMapIO::FILE_VERSION; // default if there's no .pmp
95
96 if (!only_xml)
97 {
98 // [25ms]
99 unpacker.Read(pathname, "PSMP");
100 file_format_version = unpacker.GetVersion();
101 }
102
103 // check oldest supported version
104 if (file_format_version < FILE_READ_VERSION)
105 throw PSERROR_File_InvalidVersion();
106
107 // delete all existing entities
108 if (pSimulation2)
109 pSimulation2->ResetState();
110
111 // reset post effects
112 if (pPostproc)
113 pPostproc->SetPostEffect(L"default");
114
115 // load map or script settings script
116 if (settings.isUndefined())
117 RegMemFun(this, &CMapReader::LoadScriptSettings, L"CMapReader::LoadScriptSettings", 50);
118 else
119 RegMemFun(this, &CMapReader::LoadRMSettings, L"CMapReader::LoadRMSettings", 50);
120
121 // load player settings script (must be done before reading map)
122 RegMemFun(this, &CMapReader::LoadPlayerSettings, L"CMapReader::LoadPlayerSettings", 50);
123
124 // unpack the data
125 if (!only_xml)
126 RegMemFun(this, &CMapReader::UnpackMap, L"CMapReader::UnpackMap", 1200);
127
128 // read the corresponding XML file
129 RegMemFun(this, &CMapReader::ReadXML, L"CMapReader::ReadXML", 50);
130
131 // apply terrain data to the world
132 RegMemFun(this, &CMapReader::ApplyTerrainData, L"CMapReader::ApplyTerrainData", 5);
133
134 // read entities
135 RegMemFun(this, &CMapReader::ReadXMLEntities, L"CMapReader::ReadXMLEntities", 5800);
136
137 // apply misc data to the world
138 RegMemFun(this, &CMapReader::ApplyData, L"CMapReader::ApplyData", 5);
139
140 // load map settings script (must be done after reading map)
141 RegMemFun(this, &CMapReader::LoadMapSettings, L"CMapReader::LoadMapSettings", 5);
142 }
143
144 // LoadRandomMap: try to load the map data; reinitialise the scene to new data if successful
LoadRandomMap(const CStrW & scriptFile,JSRuntime * rt,JS::HandleValue settings,CTerrain * pTerrain_,WaterManager * pWaterMan_,SkyManager * pSkyMan_,CLightEnv * pLightEnv_,CGameView * pGameView_,CCinemaManager * pCinema_,CTriggerManager * pTrigMan_,CPostprocManager * pPostproc_,CSimulation2 * pSimulation2_,int playerID_)145 void CMapReader::LoadRandomMap(const CStrW& scriptFile, JSRuntime* rt, JS::HandleValue settings, CTerrain *pTerrain_,
146 WaterManager* pWaterMan_, SkyManager* pSkyMan_,
147 CLightEnv *pLightEnv_, CGameView *pGameView_, CCinemaManager* pCinema_, CTriggerManager* pTrigMan_, CPostprocManager* pPostproc_,
148 CSimulation2 *pSimulation2_, int playerID_)
149 {
150 m_ScriptFile = scriptFile;
151 pSimulation2 = pSimulation2_;
152 pSimContext = pSimulation2 ? &pSimulation2->GetSimContext() : NULL;
153 m_ScriptSettings.init(rt, settings);
154 pTerrain = pTerrain_;
155 pLightEnv = pLightEnv_;
156 pGameView = pGameView_;
157 pWaterMan = pWaterMan_;
158 pSkyMan = pSkyMan_;
159 pCinema = pCinema_;
160 pTrigMan = pTrigMan_;
161 pPostproc = pPostproc_;
162 m_PlayerID = playerID_;
163 m_SkipEntities = false;
164 m_StartingCameraTarget = INVALID_ENTITY;
165
166 // delete all existing entities
167 if (pSimulation2)
168 pSimulation2->ResetState();
169
170 only_xml = false;
171
172 // copy random map settings (before entity creation)
173 RegMemFun(this, &CMapReader::LoadRMSettings, L"CMapReader::LoadRMSettings", 50);
174
175 // load player settings script (must be done before reading map)
176 RegMemFun(this, &CMapReader::LoadPlayerSettings, L"CMapReader::LoadPlayerSettings", 50);
177
178 // load map generator with random map script
179 RegMemFun(this, &CMapReader::GenerateMap, L"CMapReader::GenerateMap", 20000);
180
181 // parse RMS results into terrain structure
182 RegMemFun(this, &CMapReader::ParseTerrain, L"CMapReader::ParseTerrain", 500);
183
184 // parse RMS results into environment settings
185 RegMemFun(this, &CMapReader::ParseEnvironment, L"CMapReader::ParseEnvironment", 5);
186
187 // parse RMS results into camera settings
188 RegMemFun(this, &CMapReader::ParseCamera, L"CMapReader::ParseCamera", 5);
189
190 // apply terrain data to the world
191 RegMemFun(this, &CMapReader::ApplyTerrainData, L"CMapReader::ApplyTerrainData", 5);
192
193 // parse RMS results into entities
194 RegMemFun(this, &CMapReader::ParseEntities, L"CMapReader::ParseEntities", 1000);
195
196 // apply misc data to the world
197 RegMemFun(this, &CMapReader::ApplyData, L"CMapReader::ApplyData", 5);
198
199 // load map settings script (must be done after reading map)
200 RegMemFun(this, &CMapReader::LoadMapSettings, L"CMapReader::LoadMapSettings", 5);
201 }
202
203 // UnpackMap: unpack the given data from the raw data stream into local variables
UnpackMap()204 int CMapReader::UnpackMap()
205 {
206 return UnpackTerrain();
207 }
208
209 // UnpackTerrain: unpack the terrain from the end of the input data stream
210 // - data: map size, heightmap, list of textures used by map, texture tile assignments
UnpackTerrain()211 int CMapReader::UnpackTerrain()
212 {
213 // yield after this time is reached. balances increased progress bar
214 // smoothness vs. slowing down loading.
215 const double end_time = timer_Time() + 200e-3;
216
217 // first call to generator (this is skipped after first call,
218 // i.e. when the loop below was interrupted)
219 if (cur_terrain_tex == 0)
220 {
221 m_PatchesPerSide = (ssize_t)unpacker.UnpackSize();
222
223 // unpack heightmap [600us]
224 size_t verticesPerSide = m_PatchesPerSide*PATCH_SIZE+1;
225 m_Heightmap.resize(SQR(verticesPerSide));
226 unpacker.UnpackRaw(&m_Heightmap[0], SQR(verticesPerSide)*sizeof(u16));
227
228 // unpack # textures
229 num_terrain_tex = unpacker.UnpackSize();
230 m_TerrainTextures.reserve(num_terrain_tex);
231 }
232
233 // unpack texture names; find handle for each texture.
234 // interruptible.
235 while (cur_terrain_tex < num_terrain_tex)
236 {
237 CStr texturename;
238 unpacker.UnpackString(texturename);
239
240 ENSURE(CTerrainTextureManager::IsInitialised()); // we need this for the terrain properties (even when graphics are disabled)
241 CTerrainTextureEntry* texentry = g_TexMan.FindTexture(texturename);
242 m_TerrainTextures.push_back(texentry);
243
244 cur_terrain_tex++;
245 LDR_CHECK_TIMEOUT(cur_terrain_tex, num_terrain_tex);
246 }
247
248 // unpack tile data [3ms]
249 ssize_t tilesPerSide = m_PatchesPerSide*PATCH_SIZE;
250 m_Tiles.resize(size_t(SQR(tilesPerSide)));
251 unpacker.UnpackRaw(&m_Tiles[0], sizeof(STileDesc)*m_Tiles.size());
252
253 // reset generator state.
254 cur_terrain_tex = 0;
255
256 return 0;
257 }
258
ApplyTerrainData()259 int CMapReader::ApplyTerrainData()
260 {
261 if (m_PatchesPerSide == 0)
262 {
263 // we'll probably crash when trying to use this map later
264 throw PSERROR_Game_World_MapLoadFailed("Error loading map: no terrain data.\nCheck application log for details.");
265 }
266
267 if (!only_xml)
268 {
269 // initialise the terrain
270 pTerrain->Initialize(m_PatchesPerSide, &m_Heightmap[0]);
271
272 // setup the textures on the minipatches
273 STileDesc* tileptr = &m_Tiles[0];
274 for (ssize_t j=0; j<m_PatchesPerSide; j++) {
275 for (ssize_t i=0; i<m_PatchesPerSide; i++) {
276 for (ssize_t m=0; m<PATCH_SIZE; m++) {
277 for (ssize_t k=0; k<PATCH_SIZE; k++) {
278 CMiniPatch& mp = pTerrain->GetPatch(i,j)->m_MiniPatches[m][k]; // can't fail
279
280 mp.Tex = m_TerrainTextures[tileptr->m_Tex1Index];
281 mp.Priority = tileptr->m_Priority;
282
283 tileptr++;
284 }
285 }
286 }
287 }
288 }
289
290 CmpPtr<ICmpTerrain> cmpTerrain(*pSimContext, SYSTEM_ENTITY);
291 if (cmpTerrain)
292 cmpTerrain->ReloadTerrain();
293
294 return 0;
295 }
296
297 // ApplyData: take all the input data, and rebuild the scene from it
ApplyData()298 int CMapReader::ApplyData()
299 {
300 // copy over the lighting parameters
301 if (pLightEnv)
302 *pLightEnv = m_LightEnv;
303
304 CmpPtr<ICmpPlayerManager> cmpPlayerManager(*pSimContext, SYSTEM_ENTITY);
305
306 if (pGameView && cmpPlayerManager)
307 {
308 // Default to global camera (with constraints)
309 pGameView->ResetCameraTarget(pGameView->GetCamera()->GetFocus());
310
311 // TODO: Starting rotation?
312 CmpPtr<ICmpPlayer> cmpPlayer(*pSimContext, cmpPlayerManager->GetPlayerByID(m_PlayerID));
313 if (cmpPlayer && cmpPlayer->HasStartingCamera())
314 {
315 // Use player starting camera
316 CFixedVector3D pos = cmpPlayer->GetStartingCameraPos();
317 pGameView->ResetCameraTarget(CVector3D(pos.X.ToFloat(), pos.Y.ToFloat(), pos.Z.ToFloat()));
318 }
319 else if (m_StartingCameraTarget != INVALID_ENTITY)
320 {
321 // Point camera at entity
322 CmpPtr<ICmpPosition> cmpPosition(*pSimContext, m_StartingCameraTarget);
323 if (cmpPosition)
324 {
325 CFixedVector3D pos = cmpPosition->GetPosition();
326 pGameView->ResetCameraTarget(CVector3D(pos.X.ToFloat(), pos.Y.ToFloat(), pos.Z.ToFloat()));
327 }
328 }
329 }
330
331 return 0;
332 }
333
334 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
335
336 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
337
338
LoadMap(const VfsPath & pathname)339 PSRETURN CMapSummaryReader::LoadMap(const VfsPath& pathname)
340 {
341 VfsPath filename_xml = pathname.ChangeExtension(L".xml");
342
343 CXeromyces xmb_file;
344 if (xmb_file.Load(g_VFS, filename_xml, "scenario") != PSRETURN_OK)
345 return PSRETURN_File_ReadFailed;
346
347 // Define all the relevant elements used in the XML file
348 #define EL(x) int el_##x = xmb_file.GetElementID(#x)
349 #define AT(x) int at_##x = xmb_file.GetAttributeID(#x)
350 EL(scenario);
351 EL(scriptsettings);
352 #undef AT
353 #undef EL
354
355 XMBElement root = xmb_file.GetRoot();
356 ENSURE(root.GetNodeName() == el_scenario);
357
358 XERO_ITER_EL(root, child)
359 {
360 int child_name = child.GetNodeName();
361 if (child_name == el_scriptsettings)
362 {
363 m_ScriptSettings = child.GetText();
364 }
365 }
366
367 return PSRETURN_OK;
368 }
369
GetMapSettings(const ScriptInterface & scriptInterface,JS::MutableHandleValue ret)370 void CMapSummaryReader::GetMapSettings(const ScriptInterface& scriptInterface, JS::MutableHandleValue ret)
371 {
372 JSContext* cx = scriptInterface.GetContext();
373 JSAutoRequest rq(cx);
374
375 scriptInterface.Eval("({})", ret);
376 if (m_ScriptSettings.empty())
377 return;
378
379 JS::RootedValue scriptSettingsVal(cx);
380 scriptInterface.ParseJSON(m_ScriptSettings, &scriptSettingsVal);
381 scriptInterface.SetProperty(ret, "settings", scriptSettingsVal, false);
382 }
383
384 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
385
386 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
387
388
389 // Holds various state data while reading maps, so that loading can be
390 // interrupted (e.g. to update the progress display) then later resumed.
391 class CXMLReader
392 {
393 NONCOPYABLE(CXMLReader);
394 public:
CXMLReader(const VfsPath & xml_filename,CMapReader & mapReader)395 CXMLReader(const VfsPath& xml_filename, CMapReader& mapReader)
396 : m_MapReader(mapReader), nodes(NULL, 0, NULL)
397 {
398 Init(xml_filename);
399 }
400
401 CStr ReadScriptSettings();
402
403 // read everything except for entities
404 void ReadXML();
405
406 // return semantics: see Loader.cpp!LoadFunc.
407 int ProgressiveReadEntities();
408
409 private:
410 CXeromyces xmb_file;
411
412 CMapReader& m_MapReader;
413
414 int el_entity;
415 int el_tracks;
416 int el_template, el_player;
417 int el_position, el_orientation, el_obstruction;
418 int el_actor;
419 int at_x, at_y, at_z;
420 int at_group, at_group2;
421 int at_angle;
422 int at_uid;
423 int at_seed;
424
425 XMBElementList nodes; // children of root
426
427 // loop counters
428 size_t node_idx;
429 size_t entity_idx;
430
431 // # entities+nonentities processed and total (for progress calc)
432 int completed_jobs, total_jobs;
433
434 // maximum used entity ID, so we can safely allocate new ones
435 entity_id_t max_uid;
436
437 void Init(const VfsPath& xml_filename);
438
439 void ReadTerrain(XMBElement parent);
440 void ReadEnvironment(XMBElement parent);
441 void ReadCamera(XMBElement parent);
442 void ReadPaths(XMBElement parent);
443 void ReadTriggers(XMBElement parent);
444 int ReadEntities(XMBElement parent, double end_time);
445 };
446
447
Init(const VfsPath & xml_filename)448 void CXMLReader::Init(const VfsPath& xml_filename)
449 {
450 // must only assign once, so do it here
451 node_idx = entity_idx = 0;
452
453 if (xmb_file.Load(g_VFS, xml_filename, "scenario") != PSRETURN_OK)
454 throw PSERROR_File_ReadFailed();
455
456 // define the elements and attributes that are frequently used in the XML file,
457 // so we don't need to do lots of string construction and comparison when
458 // reading the data.
459 // (Needs to be synchronised with the list in CXMLReader - ugh)
460 #define EL(x) el_##x = xmb_file.GetElementID(#x)
461 #define AT(x) at_##x = xmb_file.GetAttributeID(#x)
462 EL(entity);
463 EL(tracks);
464 EL(template);
465 EL(player);
466 EL(position);
467 EL(orientation);
468 EL(obstruction);
469 EL(actor);
470 AT(x); AT(y); AT(z);
471 AT(group); AT(group2);
472 AT(angle);
473 AT(uid);
474 AT(seed);
475 #undef AT
476 #undef EL
477
478 XMBElement root = xmb_file.GetRoot();
479 ENSURE(xmb_file.GetElementString(root.GetNodeName()) == "Scenario");
480 nodes = root.GetChildNodes();
481
482 // find out total number of entities+nonentities
483 // (used when calculating progress)
484 completed_jobs = 0;
485 total_jobs = 0;
486 for (XMBElement node : nodes)
487 total_jobs += node.GetChildNodes().size();
488
489 // Find the maximum entity ID, so we can safely allocate new IDs without conflicts
490
491 max_uid = SYSTEM_ENTITY;
492
493 XMBElement ents = nodes.GetFirstNamedItem(xmb_file.GetElementID("Entities"));
494 XERO_ITER_EL(ents, ent)
495 {
496 CStr uid = ent.GetAttributes().GetNamedItem(at_uid);
497 max_uid = std::max(max_uid, (entity_id_t)uid.ToUInt());
498 }
499 }
500
501
ReadScriptSettings()502 CStr CXMLReader::ReadScriptSettings()
503 {
504 XMBElement root = xmb_file.GetRoot();
505 ENSURE(xmb_file.GetElementString(root.GetNodeName()) == "Scenario");
506 nodes = root.GetChildNodes();
507
508 XMBElement settings = nodes.GetFirstNamedItem(xmb_file.GetElementID("ScriptSettings"));
509
510 return settings.GetText();
511 }
512
513
ReadTerrain(XMBElement parent)514 void CXMLReader::ReadTerrain(XMBElement parent)
515 {
516 #define AT(x) int at_##x = xmb_file.GetAttributeID(#x)
517 AT(patches);
518 AT(texture);
519 AT(priority);
520 AT(height);
521 #undef AT
522
523 ssize_t patches = 9;
524 CStr texture = "grass1_spring";
525 int priority = 0;
526 u16 height = 16384;
527
528 XERO_ITER_ATTR(parent, attr)
529 {
530 if (attr.Name == at_patches)
531 patches = attr.Value.ToInt();
532 else if (attr.Name == at_texture)
533 texture = attr.Value;
534 else if (attr.Name == at_priority)
535 priority = attr.Value.ToInt();
536 else if (attr.Name == at_height)
537 height = (u16)attr.Value.ToInt();
538 }
539
540 m_MapReader.m_PatchesPerSide = patches;
541
542 // Load the texture
543 ENSURE(CTerrainTextureManager::IsInitialised()); // we need this for the terrain properties (even when graphics are disabled)
544 CTerrainTextureEntry* texentry = g_TexMan.FindTexture(texture);
545
546 m_MapReader.pTerrain->Initialize(patches, NULL);
547
548 // Fill the heightmap
549 u16* heightmap = m_MapReader.pTerrain->GetHeightMap();
550 ssize_t verticesPerSide = m_MapReader.pTerrain->GetVerticesPerSide();
551 for (ssize_t i = 0; i < SQR(verticesPerSide); ++i)
552 heightmap[i] = height;
553
554 // Fill the texture map
555 for (ssize_t pz = 0; pz < patches; ++pz)
556 {
557 for (ssize_t px = 0; px < patches; ++px)
558 {
559 CPatch* patch = m_MapReader.pTerrain->GetPatch(px, pz); // can't fail
560
561 for (ssize_t z = 0; z < PATCH_SIZE; ++z)
562 {
563 for (ssize_t x = 0; x < PATCH_SIZE; ++x)
564 {
565 patch->m_MiniPatches[z][x].Tex = texentry;
566 patch->m_MiniPatches[z][x].Priority = priority;
567 }
568 }
569 }
570 }
571 }
572
ReadEnvironment(XMBElement parent)573 void CXMLReader::ReadEnvironment(XMBElement parent)
574 {
575 #define EL(x) int el_##x = xmb_file.GetElementID(#x)
576 #define AT(x) int at_##x = xmb_file.GetAttributeID(#x)
577 EL(posteffect);
578 EL(skyset);
579 EL(suncolor);
580 EL(sunelevation);
581 EL(sunrotation);
582 EL(terrainambientcolor);
583 EL(unitsambientcolor);
584 EL(water);
585 EL(waterbody);
586 EL(type);
587 EL(color);
588 EL(tint);
589 EL(height);
590 EL(waviness);
591 EL(murkiness);
592 EL(windangle);
593 EL(fog);
594 EL(fogcolor);
595 EL(fogfactor);
596 EL(fogthickness);
597 EL(postproc);
598 EL(brightness);
599 EL(contrast);
600 EL(saturation);
601 EL(bloom);
602 AT(r); AT(g); AT(b);
603 #undef AT
604 #undef EL
605
606 XERO_ITER_EL(parent, element)
607 {
608 int element_name = element.GetNodeName();
609
610 XMBAttributeList attrs = element.GetAttributes();
611
612 if (element_name == el_skyset)
613 {
614 if (m_MapReader.pSkyMan)
615 m_MapReader.pSkyMan->SetSkySet(element.GetText().FromUTF8());
616 }
617 else if (element_name == el_suncolor)
618 {
619 m_MapReader.m_LightEnv.m_SunColor = RGBColor(
620 attrs.GetNamedItem(at_r).ToFloat(),
621 attrs.GetNamedItem(at_g).ToFloat(),
622 attrs.GetNamedItem(at_b).ToFloat());
623 }
624 else if (element_name == el_sunelevation)
625 {
626 m_MapReader.m_LightEnv.m_Elevation = attrs.GetNamedItem(at_angle).ToFloat();
627 }
628 else if (element_name == el_sunrotation)
629 {
630 m_MapReader.m_LightEnv.m_Rotation = attrs.GetNamedItem(at_angle).ToFloat();
631 }
632 else if (element_name == el_terrainambientcolor)
633 {
634 m_MapReader.m_LightEnv.m_TerrainAmbientColor = RGBColor(
635 attrs.GetNamedItem(at_r).ToFloat(),
636 attrs.GetNamedItem(at_g).ToFloat(),
637 attrs.GetNamedItem(at_b).ToFloat());
638 }
639 else if (element_name == el_unitsambientcolor)
640 {
641 m_MapReader.m_LightEnv.m_UnitsAmbientColor = RGBColor(
642 attrs.GetNamedItem(at_r).ToFloat(),
643 attrs.GetNamedItem(at_g).ToFloat(),
644 attrs.GetNamedItem(at_b).ToFloat());
645 }
646 else if (element_name == el_fog)
647 {
648 XERO_ITER_EL(element, fog)
649 {
650 int element_name = fog.GetNodeName();
651 if (element_name == el_fogcolor)
652 {
653 XMBAttributeList attrs = fog.GetAttributes();
654 m_MapReader.m_LightEnv.m_FogColor = RGBColor(
655 attrs.GetNamedItem(at_r).ToFloat(),
656 attrs.GetNamedItem(at_g).ToFloat(),
657 attrs.GetNamedItem(at_b).ToFloat());
658 }
659 else if (element_name == el_fogfactor)
660 {
661 m_MapReader.m_LightEnv.m_FogFactor = fog.GetText().ToFloat();
662 }
663 else if (element_name == el_fogthickness)
664 {
665 m_MapReader.m_LightEnv.m_FogMax = fog.GetText().ToFloat();
666 }
667 }
668 }
669 else if (element_name == el_postproc)
670 {
671 XERO_ITER_EL(element, postproc)
672 {
673 int element_name = postproc.GetNodeName();
674 if (element_name == el_brightness)
675 {
676 m_MapReader.m_LightEnv.m_Brightness = postproc.GetText().ToFloat();
677 }
678 else if (element_name == el_contrast)
679 {
680 m_MapReader.m_LightEnv.m_Contrast = postproc.GetText().ToFloat();
681 }
682 else if (element_name == el_saturation)
683 {
684 m_MapReader.m_LightEnv.m_Saturation = postproc.GetText().ToFloat();
685 }
686 else if (element_name == el_bloom)
687 {
688 m_MapReader.m_LightEnv.m_Bloom = postproc.GetText().ToFloat();
689 }
690 else if (element_name == el_posteffect)
691 {
692 if (m_MapReader.pPostproc)
693 m_MapReader.pPostproc->SetPostEffect(postproc.GetText().FromUTF8());
694 }
695 }
696 }
697 else if (element_name == el_water)
698 {
699 XERO_ITER_EL(element, waterbody)
700 {
701 ENSURE(waterbody.GetNodeName() == el_waterbody);
702 XERO_ITER_EL(waterbody, waterelement)
703 {
704 int element_name = waterelement.GetNodeName();
705 if (element_name == el_height)
706 {
707 CmpPtr<ICmpWaterManager> cmpWaterManager(*m_MapReader.pSimContext, SYSTEM_ENTITY);
708 ENSURE(cmpWaterManager);
709 cmpWaterManager->SetWaterLevel(entity_pos_t::FromString(waterelement.GetText()));
710 continue;
711 }
712
713 // The rest are purely graphical effects, and should be ignored if
714 // graphics are disabled
715 if (!m_MapReader.pWaterMan)
716 continue;
717
718 if (element_name == el_type)
719 {
720 if (waterelement.GetText() == "default")
721 m_MapReader.pWaterMan->m_WaterType = L"ocean";
722 else
723 m_MapReader.pWaterMan->m_WaterType = waterelement.GetText().FromUTF8();
724 }
725 #define READ_COLOR(el, out) \
726 else if (element_name == el) \
727 { \
728 XMBAttributeList attrs = waterelement.GetAttributes(); \
729 out = CColor( \
730 attrs.GetNamedItem(at_r).ToFloat(), \
731 attrs.GetNamedItem(at_g).ToFloat(), \
732 attrs.GetNamedItem(at_b).ToFloat(), \
733 1.f); \
734 }
735
736 #define READ_FLOAT(el, out) \
737 else if (element_name == el) \
738 { \
739 out = waterelement.GetText().ToFloat(); \
740 } \
741
742 READ_COLOR(el_color, m_MapReader.pWaterMan->m_WaterColor)
743 READ_COLOR(el_tint, m_MapReader.pWaterMan->m_WaterTint)
744 READ_FLOAT(el_waviness, m_MapReader.pWaterMan->m_Waviness)
745 READ_FLOAT(el_murkiness, m_MapReader.pWaterMan->m_Murkiness)
746 READ_FLOAT(el_windangle, m_MapReader.pWaterMan->m_WindAngle)
747
748 #undef READ_FLOAT
749 #undef READ_COLOR
750
751 else
752 debug_warn(L"Invalid map XML data");
753 }
754
755 }
756 }
757 else
758 debug_warn(L"Invalid map XML data");
759 }
760
761 m_MapReader.m_LightEnv.CalculateSunDirection();
762 }
763
ReadCamera(XMBElement parent)764 void CXMLReader::ReadCamera(XMBElement parent)
765 {
766 // defaults if we don't find player starting camera
767 #define EL(x) int el_##x = xmb_file.GetElementID(#x)
768 #define AT(x) int at_##x = xmb_file.GetAttributeID(#x)
769 EL(declination);
770 EL(rotation);
771 EL(position);
772 AT(angle);
773 AT(x); AT(y); AT(z);
774 #undef AT
775 #undef EL
776
777 float declination = DEGTORAD(30.f), rotation = DEGTORAD(-45.f);
778 CVector3D translation = CVector3D(100, 150, -100);
779
780 XERO_ITER_EL(parent, element)
781 {
782 int element_name = element.GetNodeName();
783
784 XMBAttributeList attrs = element.GetAttributes();
785 if (element_name == el_declination)
786 {
787 declination = attrs.GetNamedItem(at_angle).ToFloat();
788 }
789 else if (element_name == el_rotation)
790 {
791 rotation = attrs.GetNamedItem(at_angle).ToFloat();
792 }
793 else if (element_name == el_position)
794 {
795 translation = CVector3D(
796 attrs.GetNamedItem(at_x).ToFloat(),
797 attrs.GetNamedItem(at_y).ToFloat(),
798 attrs.GetNamedItem(at_z).ToFloat());
799 }
800 else
801 debug_warn(L"Invalid map XML data");
802 }
803
804 if (m_MapReader.pGameView)
805 {
806 m_MapReader.pGameView->GetCamera()->m_Orientation.SetXRotation(declination);
807 m_MapReader.pGameView->GetCamera()->m_Orientation.RotateY(rotation);
808 m_MapReader.pGameView->GetCamera()->m_Orientation.Translate(translation);
809 m_MapReader.pGameView->GetCamera()->UpdateFrustum();
810 }
811 }
812
ReadPaths(XMBElement parent)813 void CXMLReader::ReadPaths(XMBElement parent)
814 {
815 #define EL(x) int el_##x = xmb_file.GetElementID(#x)
816 #define AT(x) int at_##x = xmb_file.GetAttributeID(#x)
817
818 EL(path);
819 EL(rotation);
820 EL(node);
821 EL(position);
822 EL(target);
823 AT(name);
824 AT(timescale);
825 AT(orientation);
826 AT(mode);
827 AT(style);
828 AT(x);
829 AT(y);
830 AT(z);
831 AT(deltatime);
832
833 #undef EL
834 #undef AT
835
836 CmpPtr<ICmpCinemaManager> cmpCinemaManager(*m_MapReader.pSimContext, SYSTEM_ENTITY);
837 XERO_ITER_EL(parent, element)
838 {
839 int elementName = element.GetNodeName();
840
841 if (elementName == el_path)
842 {
843 CCinemaData pathData;
844 XMBAttributeList attrs = element.GetAttributes();
845 CStrW pathName(attrs.GetNamedItem(at_name).FromUTF8());
846 pathData.m_Name = pathName;
847 pathData.m_Timescale = fixed::FromString(attrs.GetNamedItem(at_timescale));
848 pathData.m_Orientation = attrs.GetNamedItem(at_orientation).FromUTF8();
849 pathData.m_Mode = attrs.GetNamedItem(at_mode).FromUTF8();
850 pathData.m_Style = attrs.GetNamedItem(at_style).FromUTF8();
851
852 TNSpline positionSpline, targetSpline;
853 fixed lastPositionTime = fixed::Zero();
854 fixed lastTargetTime = fixed::Zero();
855
856 XERO_ITER_EL(element, pathChild)
857 {
858 elementName = pathChild.GetNodeName();
859 attrs = pathChild.GetAttributes();
860
861 // Load node data used for spline
862 if (elementName == el_node)
863 {
864 lastPositionTime += fixed::FromString(attrs.GetNamedItem(at_deltatime));
865 lastTargetTime += fixed::FromString(attrs.GetNamedItem(at_deltatime));
866 XERO_ITER_EL(pathChild, nodeChild)
867 {
868 elementName = nodeChild.GetNodeName();
869 attrs = nodeChild.GetAttributes();
870
871 if (elementName == el_position)
872 {
873 CFixedVector3D position(fixed::FromString(attrs.GetNamedItem(at_x)),
874 fixed::FromString(attrs.GetNamedItem(at_y)),
875 fixed::FromString(attrs.GetNamedItem(at_z)));
876
877 positionSpline.AddNode(position, CFixedVector3D(), lastPositionTime);
878 lastPositionTime = fixed::Zero();
879 }
880 else if (elementName == el_rotation)
881 {
882 // TODO: Implement rotation slerp/spline as another object
883 }
884 else if (elementName == el_target)
885 {
886 CFixedVector3D targetPosition(fixed::FromString(attrs.GetNamedItem(at_x)),
887 fixed::FromString(attrs.GetNamedItem(at_y)),
888 fixed::FromString(attrs.GetNamedItem(at_z)));
889
890 targetSpline.AddNode(targetPosition, CFixedVector3D(), lastTargetTime);
891 lastTargetTime = fixed::Zero();
892 }
893 else
894 LOGWARNING("Invalid cinematic element for node child");
895 }
896 }
897 else
898 LOGWARNING("Invalid cinematic element for path child");
899 }
900
901 // Construct cinema path with data gathered
902 CCinemaPath path(pathData, positionSpline, targetSpline);
903 if (path.Empty())
904 {
905 LOGWARNING("Path with name '%s' is empty", pathName.ToUTF8());
906 return;
907 }
908
909 if (!cmpCinemaManager)
910 continue;
911 if (!cmpCinemaManager->HasPath(pathName))
912 cmpCinemaManager->AddPath(path);
913 else
914 LOGWARNING("Path with name '%s' already exists", pathName.ToUTF8());
915 }
916 else
917 LOGWARNING("Invalid path child with name '%s'", element.GetText());
918 }
919 }
920
ReadTriggers(XMBElement UNUSED (parent))921 void CXMLReader::ReadTriggers(XMBElement UNUSED(parent))
922 {
923 }
924
ReadEntities(XMBElement parent,double end_time)925 int CXMLReader::ReadEntities(XMBElement parent, double end_time)
926 {
927 XMBElementList entities = parent.GetChildNodes();
928
929 ENSURE(m_MapReader.pSimulation2);
930 CSimulation2& sim = *m_MapReader.pSimulation2;
931 CmpPtr<ICmpPlayerManager> cmpPlayerManager(sim, SYSTEM_ENTITY);
932
933 while (entity_idx < entities.size())
934 {
935 // all new state at this scope and below doesn't need to be
936 // wrapped, since we only yield after a complete iteration.
937
938 XMBElement entity = entities[entity_idx++];
939 ENSURE(entity.GetNodeName() == el_entity);
940
941 XMBAttributeList attrs = entity.GetAttributes();
942 CStr uid = attrs.GetNamedItem(at_uid);
943 ENSURE(!uid.empty());
944 int EntityUid = uid.ToInt();
945
946 CStrW TemplateName;
947 int PlayerID = 0;
948 CFixedVector3D Position;
949 CFixedVector3D Orientation;
950 long Seed = -1;
951
952 // Obstruction control groups.
953 entity_id_t ControlGroup = INVALID_ENTITY;
954 entity_id_t ControlGroup2 = INVALID_ENTITY;
955
956 XERO_ITER_EL(entity, setting)
957 {
958 int element_name = setting.GetNodeName();
959
960 // <template>
961 if (element_name == el_template)
962 {
963 TemplateName = setting.GetText().FromUTF8();
964 }
965 // <player>
966 else if (element_name == el_player)
967 {
968 PlayerID = setting.GetText().ToInt();
969 }
970 // <position>
971 else if (element_name == el_position)
972 {
973 XMBAttributeList attrs = setting.GetAttributes();
974 Position = CFixedVector3D(
975 fixed::FromString(attrs.GetNamedItem(at_x)),
976 fixed::FromString(attrs.GetNamedItem(at_y)),
977 fixed::FromString(attrs.GetNamedItem(at_z)));
978 }
979 // <orientation>
980 else if (element_name == el_orientation)
981 {
982 XMBAttributeList attrs = setting.GetAttributes();
983 Orientation = CFixedVector3D(
984 fixed::FromString(attrs.GetNamedItem(at_x)),
985 fixed::FromString(attrs.GetNamedItem(at_y)),
986 fixed::FromString(attrs.GetNamedItem(at_z)));
987 // TODO: what happens if some attributes are missing?
988 }
989 // <obstruction>
990 else if (element_name == el_obstruction)
991 {
992 XMBAttributeList attrs = setting.GetAttributes();
993 ControlGroup = attrs.GetNamedItem(at_group).ToInt();
994 ControlGroup2 = attrs.GetNamedItem(at_group2).ToInt();
995 }
996 // <actor>
997 else if (element_name == el_actor)
998 {
999 XMBAttributeList attrs = setting.GetAttributes();
1000 CStr seedStr = attrs.GetNamedItem(at_seed);
1001 if (!seedStr.empty())
1002 {
1003 Seed = seedStr.ToLong();
1004 ENSURE(Seed >= 0);
1005 }
1006 }
1007 else
1008 debug_warn(L"Invalid map XML data");
1009 }
1010
1011 entity_id_t ent = sim.AddEntity(TemplateName, EntityUid);
1012 entity_id_t player = cmpPlayerManager->GetPlayerByID(PlayerID);
1013 if (ent == INVALID_ENTITY || player == INVALID_ENTITY)
1014 { // Don't add entities with invalid player IDs
1015 LOGERROR("Failed to load entity template '%s'", utf8_from_wstring(TemplateName));
1016 }
1017 else
1018 {
1019 CmpPtr<ICmpPosition> cmpPosition(sim, ent);
1020 if (cmpPosition)
1021 {
1022 cmpPosition->JumpTo(Position.X, Position.Z);
1023 cmpPosition->SetYRotation(Orientation.Y);
1024 // TODO: other parts of the position
1025 }
1026
1027 CmpPtr<ICmpOwnership> cmpOwnership(sim, ent);
1028 if (cmpOwnership)
1029 cmpOwnership->SetOwner(PlayerID);
1030
1031 CmpPtr<ICmpObstruction> cmpObstruction(sim, ent);
1032 if (cmpObstruction)
1033 {
1034 if (ControlGroup != INVALID_ENTITY)
1035 cmpObstruction->SetControlGroup(ControlGroup);
1036 if (ControlGroup2 != INVALID_ENTITY)
1037 cmpObstruction->SetControlGroup2(ControlGroup2);
1038
1039 cmpObstruction->ResolveFoundationCollisions();
1040 }
1041
1042 CmpPtr<ICmpVisual> cmpVisual(sim, ent);
1043 if (cmpVisual)
1044 {
1045 if (Seed != -1)
1046 cmpVisual->SetActorSeed((u32)Seed);
1047 // TODO: variation/selection strings
1048 }
1049
1050 if (PlayerID == m_MapReader.m_PlayerID && (boost::algorithm::ends_with(TemplateName, L"civil_centre") || m_MapReader.m_StartingCameraTarget == INVALID_ENTITY))
1051 {
1052 // Focus on civil centre or first entity owned by player
1053 m_MapReader.m_StartingCameraTarget = ent;
1054 }
1055 }
1056
1057 completed_jobs++;
1058 LDR_CHECK_TIMEOUT(completed_jobs, total_jobs);
1059 }
1060
1061 return 0;
1062 }
1063
ReadXML()1064 void CXMLReader::ReadXML()
1065 {
1066 for (XMBElement node : nodes)
1067 {
1068 CStr name = xmb_file.GetElementString(node.GetNodeName());
1069 if (name == "Terrain")
1070 {
1071 ReadTerrain(node);
1072 }
1073 else if (name == "Environment")
1074 {
1075 ReadEnvironment(node);
1076 }
1077 else if (name == "Camera")
1078 {
1079 ReadCamera(node);
1080 }
1081 else if (name == "ScriptSettings")
1082 {
1083 // Already loaded - this is to prevent an assertion
1084 }
1085 else if (name == "Entities")
1086 {
1087 // Handled by ProgressiveReadEntities instead
1088 }
1089 else if (name == "Paths")
1090 {
1091 ReadPaths(node);
1092 }
1093 else if (name == "Triggers")
1094 {
1095 ReadTriggers(node);
1096 }
1097 else if (name == "Script")
1098 {
1099 if (m_MapReader.pSimulation2)
1100 m_MapReader.pSimulation2->SetStartupScript(node.GetText());
1101 }
1102 else
1103 {
1104 debug_printf("Invalid XML element in map file: %s\n", name.c_str());
1105 debug_warn(L"Invalid map XML data");
1106 }
1107 }
1108 }
1109
ProgressiveReadEntities()1110 int CXMLReader::ProgressiveReadEntities()
1111 {
1112 // yield after this time is reached. balances increased progress bar
1113 // smoothness vs. slowing down loading.
1114 const double end_time = timer_Time() + 200e-3;
1115
1116 int ret;
1117
1118 while (node_idx < nodes.size())
1119 {
1120 XMBElement node = nodes[node_idx];
1121 CStr name = xmb_file.GetElementString(node.GetNodeName());
1122 if (name == "Entities")
1123 {
1124 if (!m_MapReader.m_SkipEntities)
1125 {
1126 ret = ReadEntities(node, end_time);
1127 if (ret != 0) // error or timed out
1128 return ret;
1129 }
1130 }
1131
1132 node_idx++;
1133 }
1134
1135 return 0;
1136 }
1137
1138 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1139
1140 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1141
1142
1143 // load script settings from map
LoadScriptSettings()1144 int CMapReader::LoadScriptSettings()
1145 {
1146 if (!xml_reader)
1147 xml_reader = new CXMLReader(filename_xml, *this);
1148
1149 // parse the script settings
1150 if (pSimulation2)
1151 pSimulation2->SetMapSettings(xml_reader->ReadScriptSettings());
1152
1153 return 0;
1154 }
1155
1156 // load player settings script
LoadPlayerSettings()1157 int CMapReader::LoadPlayerSettings()
1158 {
1159 if (pSimulation2)
1160 pSimulation2->LoadPlayerSettings(true);
1161 return 0;
1162 }
1163
1164 // load map settings script
LoadMapSettings()1165 int CMapReader::LoadMapSettings()
1166 {
1167 if (pSimulation2)
1168 pSimulation2->LoadMapSettings();
1169 return 0;
1170 }
1171
ReadXML()1172 int CMapReader::ReadXML()
1173 {
1174 if (!xml_reader)
1175 xml_reader = new CXMLReader(filename_xml, *this);
1176
1177 xml_reader->ReadXML();
1178
1179 return 0;
1180 }
1181
1182 // progressive
ReadXMLEntities()1183 int CMapReader::ReadXMLEntities()
1184 {
1185 if (!xml_reader)
1186 xml_reader = new CXMLReader(filename_xml, *this);
1187
1188 int ret = xml_reader->ProgressiveReadEntities();
1189 // finished or failed
1190 if (ret <= 0)
1191 {
1192 SAFE_DELETE(xml_reader);
1193 }
1194
1195 return ret;
1196 }
1197
1198 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1199
1200 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1201
1202
LoadRMSettings()1203 int CMapReader::LoadRMSettings()
1204 {
1205 // copy random map settings over to sim
1206 ENSURE(pSimulation2);
1207 pSimulation2->SetMapSettings(m_ScriptSettings);
1208
1209 return 0;
1210 }
1211
GenerateMap()1212 int CMapReader::GenerateMap()
1213 {
1214 JSContext* cx = pSimulation2->GetScriptInterface().GetContext();
1215 JSAutoRequest rq(cx);
1216
1217 if (!m_MapGen)
1218 {
1219 // Initialize map generator
1220 m_MapGen = new CMapGenerator();
1221
1222 VfsPath scriptPath;
1223
1224 if (m_ScriptFile.length())
1225 scriptPath = L"maps/random/"+m_ScriptFile;
1226
1227 // Stringify settings to pass across threads
1228 std::string scriptSettings = pSimulation2->GetScriptInterface().StringifyJSON(&m_ScriptSettings);
1229
1230 // Try to generate map
1231 m_MapGen->GenerateMap(scriptPath, scriptSettings);
1232 }
1233
1234 // Check status
1235 int progress = m_MapGen->GetProgress();
1236 if (progress < 0)
1237 {
1238 // RMS failed - return to main menu
1239 throw PSERROR_Game_World_MapLoadFailed("Error generating random map.\nCheck application log for details.");
1240 }
1241 else if (progress == 0)
1242 {
1243 // Finished, get results as StructuredClone object, which must be read to obtain the JS::Value
1244 shared_ptr<ScriptInterface::StructuredClone> results = m_MapGen->GetResults();
1245
1246 // Parse data into simulation context
1247 JS::RootedValue data(cx);
1248 pSimulation2->GetScriptInterface().ReadStructuredClone(results, &data);
1249
1250 if (data.isUndefined())
1251 {
1252 // RMS failed - return to main menu
1253 throw PSERROR_Game_World_MapLoadFailed("Error generating random map.\nCheck application log for details.");
1254 }
1255 else
1256 {
1257 m_MapData.init(cx, data);
1258 }
1259 }
1260 else
1261 {
1262 // Still working
1263
1264 // Sleep for a while, slowing down the rendering thread
1265 // to allow more CPU for the map generator thread
1266 SDL_Delay(100);
1267 }
1268
1269 // return progress
1270 return progress;
1271 };
1272
1273
ParseTerrain()1274 int CMapReader::ParseTerrain()
1275 {
1276 TIMER(L"ParseTerrain");
1277 JSContext* cx = pSimulation2->GetScriptInterface().GetContext();
1278 JSAutoRequest rq(cx);
1279
1280 // parse terrain from map data
1281 // an error here should stop the loading process
1282 #define GET_TERRAIN_PROPERTY(val, prop, out)\
1283 if (!pSimulation2->GetScriptInterface().GetProperty(val, #prop, out))\
1284 { LOGERROR("CMapReader::ParseTerrain() failed to get '%s' property", #prop);\
1285 throw PSERROR_Game_World_MapLoadFailed("Error parsing terrain data.\nCheck application log for details"); }
1286
1287 u32 size;
1288 GET_TERRAIN_PROPERTY(m_MapData, size, size)
1289
1290 m_PatchesPerSide = size / PATCH_SIZE;
1291
1292 // flat heightmap of u16 data
1293 GET_TERRAIN_PROPERTY(m_MapData, height, m_Heightmap)
1294
1295 // load textures
1296 std::vector<std::string> textureNames;
1297 GET_TERRAIN_PROPERTY(m_MapData, textureNames, textureNames)
1298 num_terrain_tex = textureNames.size();
1299
1300 while (cur_terrain_tex < num_terrain_tex)
1301 {
1302 ENSURE(CTerrainTextureManager::IsInitialised()); // we need this for the terrain properties (even when graphics are disabled)
1303 CTerrainTextureEntry* texentry = g_TexMan.FindTexture(textureNames[cur_terrain_tex]);
1304 m_TerrainTextures.push_back(texentry);
1305
1306 cur_terrain_tex++;
1307 }
1308
1309 // build tile data
1310 m_Tiles.resize(SQR(size));
1311
1312 JS::RootedValue tileData(cx);
1313 GET_TERRAIN_PROPERTY(m_MapData, tileData, &tileData)
1314
1315 // parse tile data object into flat arrays
1316 std::vector<u16> tileIndex;
1317 std::vector<u16> tilePriority;
1318 GET_TERRAIN_PROPERTY(tileData, index, tileIndex);
1319 GET_TERRAIN_PROPERTY(tileData, priority, tilePriority);
1320
1321 ENSURE(SQR(size) == tileIndex.size() && SQR(size) == tilePriority.size());
1322
1323 // reorder by patches and store
1324 for (size_t x = 0; x < size; ++x)
1325 {
1326 size_t patchX = x / PATCH_SIZE;
1327 size_t offX = x % PATCH_SIZE;
1328 for (size_t y = 0; y < size; ++y)
1329 {
1330 size_t patchY = y / PATCH_SIZE;
1331 size_t offY = y % PATCH_SIZE;
1332
1333 STileDesc tile;
1334 tile.m_Tex1Index = tileIndex[y*size + x];
1335 tile.m_Tex2Index = 0xFFFF;
1336 tile.m_Priority = tilePriority[y*size + x];
1337
1338 m_Tiles[(patchY * m_PatchesPerSide + patchX) * SQR(PATCH_SIZE) + (offY * PATCH_SIZE + offX)] = tile;
1339 }
1340 }
1341
1342 // reset generator state
1343 cur_terrain_tex = 0;
1344
1345 #undef GET_TERRAIN_PROPERTY
1346
1347 return 0;
1348 }
1349
ParseEntities()1350 int CMapReader::ParseEntities()
1351 {
1352 TIMER(L"ParseEntities");
1353 JSContext* cx = pSimulation2->GetScriptInterface().GetContext();
1354 JSAutoRequest rq(cx);
1355
1356 // parse entities from map data
1357 std::vector<Entity> entities;
1358
1359 if (!pSimulation2->GetScriptInterface().GetProperty(m_MapData, "entities", entities))
1360 LOGWARNING("CMapReader::ParseEntities() failed to get 'entities' property");
1361
1362 CSimulation2& sim = *pSimulation2;
1363 CmpPtr<ICmpPlayerManager> cmpPlayerManager(sim, SYSTEM_ENTITY);
1364
1365 size_t entity_idx = 0;
1366 size_t num_entities = entities.size();
1367
1368 Entity currEnt;
1369
1370 while (entity_idx < num_entities)
1371 {
1372 // Get current entity struct
1373 currEnt = entities[entity_idx];
1374
1375 entity_id_t ent = pSimulation2->AddEntity(currEnt.templateName, currEnt.entityID);
1376 entity_id_t player = cmpPlayerManager->GetPlayerByID(currEnt.playerID);
1377 if (ent == INVALID_ENTITY || player == INVALID_ENTITY)
1378 { // Don't add entities with invalid player IDs
1379 LOGERROR("Failed to load entity template '%s'", utf8_from_wstring(currEnt.templateName));
1380 }
1381 else
1382 {
1383 CmpPtr<ICmpPosition> cmpPosition(sim, ent);
1384 if (cmpPosition)
1385 {
1386 cmpPosition->JumpTo(currEnt.position.X * (int)TERRAIN_TILE_SIZE, currEnt.position.Z * (int)TERRAIN_TILE_SIZE);
1387 cmpPosition->SetYRotation(currEnt.rotation.Y);
1388 // TODO: other parts of the position
1389 }
1390
1391 CmpPtr<ICmpOwnership> cmpOwnership(sim, ent);
1392 if (cmpOwnership)
1393 cmpOwnership->SetOwner(currEnt.playerID);
1394
1395 // Detect and fix collisions between foundation-blocking entities.
1396 // This presently serves to copy wall tower control groups to wall
1397 // segments, allowing players to expand RMS-generated walls.
1398 CmpPtr<ICmpObstruction> cmpObstruction(sim, ent);
1399 if (cmpObstruction)
1400 cmpObstruction->ResolveFoundationCollisions();
1401
1402 if (currEnt.playerID == m_PlayerID && (boost::algorithm::ends_with(currEnt.templateName, L"civil_centre") || m_StartingCameraTarget == INVALID_ENTITY))
1403 {
1404 // Focus on civil centre or first entity owned by player
1405 m_StartingCameraTarget = currEnt.entityID;
1406 }
1407 }
1408
1409 entity_idx++;
1410 }
1411
1412 return 0;
1413 }
1414
ParseEnvironment()1415 int CMapReader::ParseEnvironment()
1416 {
1417 // parse environment settings from map data
1418 JSContext* cx = pSimulation2->GetScriptInterface().GetContext();
1419 JSAutoRequest rq(cx);
1420
1421 #define GET_ENVIRONMENT_PROPERTY(val, prop, out)\
1422 if (!pSimulation2->GetScriptInterface().GetProperty(val, #prop, out))\
1423 LOGWARNING("CMapReader::ParseEnvironment() failed to get '%s' property", #prop);
1424
1425 JS::RootedValue envObj(cx);
1426 GET_ENVIRONMENT_PROPERTY(m_MapData, Environment, &envObj)
1427
1428 if (envObj.isUndefined())
1429 {
1430 LOGWARNING("CMapReader::ParseEnvironment(): Environment settings not found");
1431 return 0;
1432 }
1433
1434 if (pPostproc)
1435 pPostproc->SetPostEffect(L"default");
1436
1437 std::wstring skySet;
1438 GET_ENVIRONMENT_PROPERTY(envObj, SkySet, skySet)
1439 if (pSkyMan)
1440 pSkyMan->SetSkySet(skySet);
1441
1442 CColor sunColor;
1443 GET_ENVIRONMENT_PROPERTY(envObj, SunColor, sunColor)
1444 m_LightEnv.m_SunColor = RGBColor(sunColor.r, sunColor.g, sunColor.b);
1445
1446 GET_ENVIRONMENT_PROPERTY(envObj, SunElevation, m_LightEnv.m_Elevation)
1447 GET_ENVIRONMENT_PROPERTY(envObj, SunRotation, m_LightEnv.m_Rotation)
1448
1449 CColor terrainAmbientColor;
1450 GET_ENVIRONMENT_PROPERTY(envObj, TerrainAmbientColor, terrainAmbientColor)
1451 m_LightEnv.m_TerrainAmbientColor = RGBColor(terrainAmbientColor.r, terrainAmbientColor.g, terrainAmbientColor.b);
1452
1453 CColor unitsAmbientColor;
1454 GET_ENVIRONMENT_PROPERTY(envObj, UnitsAmbientColor, unitsAmbientColor)
1455 m_LightEnv.m_UnitsAmbientColor = RGBColor(unitsAmbientColor.r, unitsAmbientColor.g, unitsAmbientColor.b);
1456
1457 // Water properties
1458 JS::RootedValue waterObj(cx);
1459 GET_ENVIRONMENT_PROPERTY(envObj, Water, &waterObj)
1460
1461 JS::RootedValue waterBodyObj(cx);
1462 GET_ENVIRONMENT_PROPERTY(waterObj, WaterBody, &waterBodyObj)
1463
1464 // Water level - necessary
1465 float waterHeight;
1466 GET_ENVIRONMENT_PROPERTY(waterBodyObj, Height, waterHeight)
1467
1468 CmpPtr<ICmpWaterManager> cmpWaterManager(*pSimulation2, SYSTEM_ENTITY);
1469 ENSURE(cmpWaterManager);
1470 cmpWaterManager->SetWaterLevel(entity_pos_t::FromFloat(waterHeight));
1471
1472 // If we have graphics, get rest of settings
1473 if (pWaterMan)
1474 {
1475 GET_ENVIRONMENT_PROPERTY(waterBodyObj, Type, pWaterMan->m_WaterType)
1476 if (pWaterMan->m_WaterType == L"default")
1477 pWaterMan->m_WaterType = L"ocean";
1478 GET_ENVIRONMENT_PROPERTY(waterBodyObj, Color, pWaterMan->m_WaterColor)
1479 GET_ENVIRONMENT_PROPERTY(waterBodyObj, Tint, pWaterMan->m_WaterTint)
1480 GET_ENVIRONMENT_PROPERTY(waterBodyObj, Waviness, pWaterMan->m_Waviness)
1481 GET_ENVIRONMENT_PROPERTY(waterBodyObj, Murkiness, pWaterMan->m_Murkiness)
1482 GET_ENVIRONMENT_PROPERTY(waterBodyObj, WindAngle, pWaterMan->m_WindAngle)
1483 }
1484
1485 JS::RootedValue fogObject(cx);
1486 GET_ENVIRONMENT_PROPERTY(envObj, Fog, &fogObject);
1487
1488 GET_ENVIRONMENT_PROPERTY(fogObject, FogFactor, m_LightEnv.m_FogFactor);
1489 GET_ENVIRONMENT_PROPERTY(fogObject, FogThickness, m_LightEnv.m_FogMax);
1490
1491 CColor fogColor;
1492 GET_ENVIRONMENT_PROPERTY(fogObject, FogColor, fogColor);
1493 m_LightEnv.m_FogColor = RGBColor(fogColor.r, fogColor.g, fogColor.b);
1494
1495 JS::RootedValue postprocObject(cx);
1496 GET_ENVIRONMENT_PROPERTY(envObj, Postproc, &postprocObject);
1497
1498 std::wstring postProcEffect;
1499 GET_ENVIRONMENT_PROPERTY(postprocObject, PostprocEffect, postProcEffect);
1500
1501 if (pPostproc)
1502 pPostproc->SetPostEffect(postProcEffect);
1503
1504 GET_ENVIRONMENT_PROPERTY(postprocObject, Brightness, m_LightEnv.m_Brightness);
1505 GET_ENVIRONMENT_PROPERTY(postprocObject, Contrast, m_LightEnv.m_Contrast);
1506 GET_ENVIRONMENT_PROPERTY(postprocObject, Saturation, m_LightEnv.m_Saturation);
1507 GET_ENVIRONMENT_PROPERTY(postprocObject, Bloom, m_LightEnv.m_Bloom);
1508
1509 m_LightEnv.CalculateSunDirection();
1510
1511 #undef GET_ENVIRONMENT_PROPERTY
1512
1513 return 0;
1514 }
1515
ParseCamera()1516 int CMapReader::ParseCamera()
1517 {
1518 JSContext* cx = pSimulation2->GetScriptInterface().GetContext();
1519 JSAutoRequest rq(cx);
1520 // parse camera settings from map data
1521 // defaults if we don't find player starting camera
1522 float declination = DEGTORAD(30.f), rotation = DEGTORAD(-45.f);
1523 CVector3D translation = CVector3D(100, 150, -100);
1524
1525 #define GET_CAMERA_PROPERTY(val, prop, out)\
1526 if (!pSimulation2->GetScriptInterface().GetProperty(val, #prop, out))\
1527 LOGWARNING("CMapReader::ParseCamera() failed to get '%s' property", #prop);
1528
1529 JS::RootedValue cameraObj(cx);
1530 GET_CAMERA_PROPERTY(m_MapData, Camera, &cameraObj)
1531
1532 if (!cameraObj.isUndefined())
1533 { // If camera property exists, read values
1534 CFixedVector3D pos;
1535 GET_CAMERA_PROPERTY(cameraObj, Position, pos)
1536 translation = pos;
1537
1538 GET_CAMERA_PROPERTY(cameraObj, Rotation, rotation)
1539 GET_CAMERA_PROPERTY(cameraObj, Declination, declination)
1540 }
1541 #undef GET_CAMERA_PROPERTY
1542
1543 if (pGameView)
1544 {
1545 pGameView->GetCamera()->m_Orientation.SetXRotation(declination);
1546 pGameView->GetCamera()->m_Orientation.RotateY(rotation);
1547 pGameView->GetCamera()->m_Orientation.Translate(translation);
1548 pGameView->GetCamera()->UpdateFrustum();
1549 }
1550
1551 return 0;
1552 }
1553
~CMapReader()1554 CMapReader::~CMapReader()
1555 {
1556 // Cleaup objects
1557 delete xml_reader;
1558 delete m_MapGen;
1559 }
1560