1 /* Copyright (C) 2023 Wildfire Games.
2 * This file is part of 0 A.D.
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.
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.
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/>.
18 #include "precompiled.h"
20 #include "MapReader.h"
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 "maths/MathUtil.h"
33 #include "ps/CLogger.h"
34 #include "ps/Loader.h"
36 #include "ps/XML/Xeromyces.h"
37 #include "renderer/PostprocManager.h"
38 #include "renderer/SkyManager.h"
39 #include "renderer/WaterManager.h"
40 #include "scriptinterface/Object.h"
41 #include "scriptinterface/ScriptContext.h"
42 #include "scriptinterface/ScriptRequest.h"
43 #include "scriptinterface/JSON.h"
44 #include "simulation2/Simulation2.h"
45 #include "simulation2/components/ICmpCinemaManager.h"
46 #include "simulation2/components/ICmpGarrisonHolder.h"
47 #include "simulation2/components/ICmpObstruction.h"
48 #include "simulation2/components/ICmpOwnership.h"
49 #include "simulation2/components/ICmpPlayer.h"
50 #include "simulation2/components/ICmpPlayerManager.h"
51 #include "simulation2/components/ICmpPosition.h"
52 #include "simulation2/components/ICmpTerrain.h"
53 #include "simulation2/components/ICmpTurretHolder.h"
54 #include "simulation2/components/ICmpVisual.h"
55 #include "simulation2/components/ICmpWaterManager.h"
57 #include <boost/algorithm/string/predicate.hpp>
59 #if defined(_MSC_VER) && _MSC_VER > 1900
60 #pragma warning(disable: 4456) // Declaration hides previous local declaration.
61 #pragma warning(disable: 4458) // Declaration hides class member.
64 CMapReader::CMapReader()
65 : xml_reader(0), m_PatchesPerSide(0), m_MapGen(0)
67 cur_terrain_tex
= 0; // important - resets generator state
70 // LoadMap: try to load the map from given file; reinitialise the scene to new data if successful
71 void CMapReader::LoadMap(const VfsPath
& pathname
, const ScriptContext
& cx
, JS::HandleValue settings
, CTerrain
*pTerrain_
,
72 WaterManager
* pWaterMan_
, SkyManager
* pSkyMan_
,
73 CLightEnv
*pLightEnv_
, CGameView
*pGameView_
, CCinemaManager
* pCinema_
, CTriggerManager
* pTrigMan_
, CPostprocManager
* pPostproc_
,
74 CSimulation2
*pSimulation2_
, const CSimContext
* pSimContext_
, int playerID_
, bool skipEntities
)
77 pLightEnv
= pLightEnv_
;
78 pGameView
= pGameView_
;
79 pWaterMan
= pWaterMan_
;
83 pPostproc
= pPostproc_
;
84 pSimulation2
= pSimulation2_
;
85 pSimContext
= pSimContext_
;
86 m_PlayerID
= playerID_
;
87 m_SkipEntities
= skipEntities
;
88 m_StartingCameraTarget
= INVALID_ENTITY
;
89 m_ScriptSettings
.init(cx
.GetGeneralJSContext(), settings
);
91 filename_xml
= pathname
.ChangeExtension(L
".xml");
93 // In some cases (particularly tests) we don't want to bother storing a large
94 // mostly-empty .pmp file, so we let the XML file specify basic terrain instead.
95 // If there's an .xml file and no .pmp, then we're probably in this XML-only mode
97 if (!VfsFileExists(pathname
) && VfsFileExists(filename_xml
))
102 file_format_version
= CMapIO::FILE_VERSION
; // default if there's no .pmp
107 unpacker
.Read(pathname
, "PSMP");
108 file_format_version
= unpacker
.GetVersion();
111 // check oldest supported version
112 if (file_format_version
< FILE_READ_VERSION
)
113 throw PSERROR_Game_World_MapLoadFailed("Could not load terrain file - too old version!");
115 // delete all existing entities
117 pSimulation2
->ResetState();
119 // reset post effects
121 pPostproc
->SetPostEffect(L
"default");
123 // load map or script settings script
124 if (settings
.isUndefined())
125 LDR_Register([this](const double)
127 return LoadScriptSettings();
128 }, L
"CMapReader::LoadScriptSettings", 50);
130 LDR_Register([this](const double)
132 return LoadRMSettings();
133 }, L
"CMapReader::LoadRMSettings", 50);
135 // load player settings script (must be done before reading map)
136 LDR_Register([this](const double)
138 return LoadPlayerSettings();
139 }, L
"CMapReader::LoadPlayerSettings", 50);
143 LDR_Register([this](const double)
145 return UnpackTerrain();
146 }, L
"CMapReader::UnpackMap", 1200);
148 // read the corresponding XML file
149 LDR_Register([this](const double)
152 }, L
"CMapReader::ReadXML", 50);
154 // apply terrain data to the world
155 LDR_Register([this](const double)
157 return ApplyTerrainData();
158 }, L
"CMapReader::ApplyTerrainData", 5);
161 LDR_Register([this](const double)
163 return ReadXMLEntities();
164 }, L
"CMapReader::ReadXMLEntities", 5800);
166 // apply misc data to the world
167 LDR_Register([this](const double)
170 }, L
"CMapReader::ApplyData", 5);
172 // load map settings script (must be done after reading map)
173 LDR_Register([this](const double)
175 return LoadMapSettings();
176 }, L
"CMapReader::LoadMapSettings", 5);
179 // LoadRandomMap: try to load the map data; reinitialise the scene to new data if successful
180 void CMapReader::LoadRandomMap(const CStrW
& scriptFile
, const ScriptContext
& cx
, JS::HandleValue settings
, CTerrain
*pTerrain_
,
181 WaterManager
* pWaterMan_
, SkyManager
* pSkyMan_
,
182 CLightEnv
*pLightEnv_
, CGameView
*pGameView_
, CCinemaManager
* pCinema_
, CTriggerManager
* pTrigMan_
, CPostprocManager
* pPostproc_
,
183 CSimulation2
*pSimulation2_
, int playerID_
)
185 pSimulation2
= pSimulation2_
;
186 pSimContext
= pSimulation2
? &pSimulation2
->GetSimContext() : NULL
;
187 m_ScriptSettings
.init(cx
.GetGeneralJSContext(), settings
);
188 pTerrain
= pTerrain_
;
189 pLightEnv
= pLightEnv_
;
190 pGameView
= pGameView_
;
191 pWaterMan
= pWaterMan_
;
194 pTrigMan
= pTrigMan_
;
195 pPostproc
= pPostproc_
;
196 m_PlayerID
= playerID_
;
197 m_SkipEntities
= false;
198 m_StartingCameraTarget
= INVALID_ENTITY
;
200 // delete all existing entities
202 pSimulation2
->ResetState();
206 // copy random map settings (before entity creation)
207 LDR_Register([this](const double)
209 return LoadRMSettings();
210 }, L
"CMapReader::LoadRMSettings", 50);
212 // load player settings script (must be done before reading map)
213 LDR_Register([this](const double)
215 return LoadPlayerSettings();
216 }, L
"CMapReader::LoadPlayerSettings", 50);
218 // load map generator with random map script
219 LDR_Register([this, scriptFile
](const double)
221 return GenerateMap(scriptFile
);
222 }, L
"CMapReader::GenerateMap", 20000);
224 // parse RMS results into terrain structure
225 LDR_Register([this](const double)
227 return ParseTerrain();
228 }, L
"CMapReader::ParseTerrain", 500);
230 // parse RMS results into environment settings
231 LDR_Register([this](const double)
233 return ParseEnvironment();
234 }, L
"CMapReader::ParseEnvironment", 5);
236 // parse RMS results into camera settings
237 LDR_Register([this](const double)
239 return ParseCamera();
240 }, L
"CMapReader::ParseCamera", 5);
242 // apply terrain data to the world
243 LDR_Register([this](const double)
245 return ApplyTerrainData();
246 }, L
"CMapReader::ApplyTerrainData", 5);
248 // parse RMS results into entities
249 LDR_Register([this](const double)
251 return ParseEntities();
252 }, L
"CMapReader::ParseEntities", 1000);
254 // apply misc data to the world
255 LDR_Register([this](const double)
258 }, L
"CMapReader::ApplyData", 5);
260 // load map settings script (must be done after reading map)
261 LDR_Register([this](const double)
263 return LoadMapSettings();
264 }, L
"CMapReader::LoadMapSettings", 5);
267 // UnpackTerrain: unpack the terrain from the end of the input data stream
268 // - data: map size, heightmap, list of textures used by map, texture tile assignments
269 int CMapReader::UnpackTerrain()
271 // yield after this time is reached. balances increased progress bar
272 // smoothness vs. slowing down loading.
273 const double end_time
= timer_Time() + 200e-3;
275 // first call to generator (this is skipped after first call,
276 // i.e. when the loop below was interrupted)
277 if (cur_terrain_tex
== 0)
279 m_PatchesPerSide
= (ssize_t
)unpacker
.UnpackSize();
281 // unpack heightmap [600us]
282 size_t verticesPerSide
= m_PatchesPerSide
*PATCH_SIZE
+1;
283 m_Heightmap
.resize(SQR(verticesPerSide
));
284 unpacker
.UnpackRaw(&m_Heightmap
[0], SQR(verticesPerSide
)*sizeof(u16
));
287 num_terrain_tex
= unpacker
.UnpackSize();
288 m_TerrainTextures
.reserve(num_terrain_tex
);
291 // unpack texture names; find handle for each texture.
293 while (cur_terrain_tex
< num_terrain_tex
)
296 unpacker
.UnpackString(texturename
);
298 if (CTerrainTextureManager::IsInitialised())
300 CTerrainTextureEntry
* texentry
= g_TexMan
.FindTexture(texturename
);
301 m_TerrainTextures
.push_back(texentry
);
305 LDR_CHECK_TIMEOUT(cur_terrain_tex
, num_terrain_tex
);
308 // unpack tile data [3ms]
309 ssize_t tilesPerSide
= m_PatchesPerSide
*PATCH_SIZE
;
310 m_Tiles
.resize(size_t(SQR(tilesPerSide
)));
311 unpacker
.UnpackRaw(&m_Tiles
[0], sizeof(STileDesc
)*m_Tiles
.size());
313 // reset generator state.
319 int CMapReader::ApplyTerrainData()
321 if (m_PatchesPerSide
== 0)
323 // we'll probably crash when trying to use this map later
324 throw PSERROR_Game_World_MapLoadFailed("Error loading map: no terrain data.\nCheck application log for details.");
329 // initialise the terrain
330 pTerrain
->Initialize(m_PatchesPerSide
, &m_Heightmap
[0]);
332 if (CTerrainTextureManager::IsInitialised())
334 // setup the textures on the minipatches
335 STileDesc
* tileptr
= &m_Tiles
[0];
336 for (ssize_t j
=0; j
<m_PatchesPerSide
; j
++) {
337 for (ssize_t i
=0; i
<m_PatchesPerSide
; i
++) {
338 for (ssize_t m
=0; m
<PATCH_SIZE
; m
++) {
339 for (ssize_t k
=0; k
<PATCH_SIZE
; k
++) {
340 CMiniPatch
& mp
= pTerrain
->GetPatch(i
,j
)->m_MiniPatches
[m
][k
]; // can't fail
342 mp
.Tex
= m_TerrainTextures
[tileptr
->m_Tex1Index
];
343 mp
.Priority
= tileptr
->m_Priority
;
353 CmpPtr
<ICmpTerrain
> cmpTerrain(*pSimContext
, SYSTEM_ENTITY
);
355 cmpTerrain
->ReloadTerrain();
360 // ApplyData: take all the input data, and rebuild the scene from it
361 int CMapReader::ApplyData()
363 // copy over the lighting parameters
365 *pLightEnv
= m_LightEnv
;
367 CmpPtr
<ICmpPlayerManager
> cmpPlayerManager(*pSimContext
, SYSTEM_ENTITY
);
369 if (pGameView
&& cmpPlayerManager
)
371 // Default to global camera (with constraints)
372 pGameView
->ResetCameraTarget(pGameView
->GetCamera()->GetFocus());
374 // TODO: Starting rotation?
375 CmpPtr
<ICmpPlayer
> cmpPlayer(*pSimContext
, cmpPlayerManager
->GetPlayerByID(m_PlayerID
));
376 if (cmpPlayer
&& cmpPlayer
->HasStartingCamera())
378 // Use player starting camera
379 CFixedVector3D pos
= cmpPlayer
->GetStartingCameraPos();
380 pGameView
->ResetCameraTarget(CVector3D(pos
.X
.ToFloat(), pos
.Y
.ToFloat(), pos
.Z
.ToFloat()));
382 else if (m_StartingCameraTarget
!= INVALID_ENTITY
)
384 // Point camera at entity
385 CmpPtr
<ICmpPosition
> cmpPosition(*pSimContext
, m_StartingCameraTarget
);
388 CFixedVector3D pos
= cmpPosition
->GetPosition();
389 pGameView
->ResetCameraTarget(CVector3D(pos
.X
.ToFloat(), pos
.Y
.ToFloat(), pos
.Z
.ToFloat()));
397 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
399 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
402 PSRETURN
CMapSummaryReader::LoadMap(const VfsPath
& pathname
)
404 VfsPath filename_xml
= pathname
.ChangeExtension(L
".xml");
407 if (xmb_file
.Load(g_VFS
, filename_xml
, "scenario") != PSRETURN_OK
)
408 return PSRETURN_File_ReadFailed
;
410 // Define all the relevant elements used in the XML file
411 #define EL(x) int el_##x = xmb_file.GetElementID(#x)
412 #define AT(x) int at_##x = xmb_file.GetAttributeID(#x)
418 XMBElement root
= xmb_file
.GetRoot();
419 ENSURE(root
.GetNodeName() == el_scenario
);
421 XERO_ITER_EL(root
, child
)
423 int child_name
= child
.GetNodeName();
424 if (child_name
== el_scriptsettings
)
426 m_ScriptSettings
= child
.GetText();
433 void CMapSummaryReader::GetMapSettings(const ScriptInterface
& scriptInterface
, JS::MutableHandleValue ret
)
435 ScriptRequest
rq(scriptInterface
);
437 Script::CreateObject(rq
, ret
);
439 if (m_ScriptSettings
.empty())
442 JS::RootedValue
scriptSettingsVal(rq
.cx
);
443 Script::ParseJSON(rq
, m_ScriptSettings
, &scriptSettingsVal
);
444 Script::SetProperty(rq
, ret
, "settings", scriptSettingsVal
, false);
447 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
449 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
452 // Holds various state data while reading maps, so that loading can be
453 // interrupted (e.g. to update the progress display) then later resumed.
456 NONCOPYABLE(CXMLReader
);
458 CXMLReader(const VfsPath
& xml_filename
, CMapReader
& mapReader
)
459 : m_MapReader(mapReader
), nodes(NULL
, 0, NULL
)
464 CStr
ReadScriptSettings();
466 // read everything except for entities
469 // return semantics: see Loader.cpp!LoadFunc.
470 int ProgressiveReadEntities();
475 CMapReader
& m_MapReader
;
479 int el_template
, el_player
;
480 int el_position
, el_orientation
, el_obstruction
;
487 int at_group
, at_group2
;
493 XMBElementList nodes
; // children of root
499 // # entities+nonentities processed and total (for progress calc)
500 int completed_jobs
, total_jobs
;
502 // maximum used entity ID, so we can safely allocate new ones
505 void Init(const VfsPath
& xml_filename
);
507 void ReadTerrain(XMBElement parent
);
508 void ReadEnvironment(XMBElement parent
);
509 void ReadCamera(XMBElement parent
);
510 void ReadPaths(XMBElement parent
);
511 void ReadTriggers(XMBElement parent
);
512 int ReadEntities(XMBElement parent
, double end_time
);
516 void CXMLReader::Init(const VfsPath
& xml_filename
)
518 // must only assign once, so do it here
519 node_idx
= entity_idx
= 0;
521 if (xmb_file
.Load(g_VFS
, xml_filename
, "scenario") != PSRETURN_OK
)
522 throw PSERROR_Game_World_MapLoadFailed("Could not read map XML file!");
524 // define the elements and attributes that are frequently used in the XML file,
525 // so we don't need to do lots of string construction and comparison when
527 // (Needs to be synchronised with the list in CXMLReader - ugh)
528 #define EL(x) el_##x = xmb_file.GetElementID(#x)
529 #define AT(x) at_##x = xmb_file.GetAttributeID(#x)
541 AT(group
); AT(group2
);
549 XMBElement root
= xmb_file
.GetRoot();
550 ENSURE(xmb_file
.GetElementStringView(root
.GetNodeName()) == "Scenario");
551 nodes
= root
.GetChildNodes();
553 // find out total number of entities+nonentities
554 // (used when calculating progress)
557 for (XMBElement node
: nodes
)
558 total_jobs
+= node
.GetChildNodes().size();
560 // Find the maximum entity ID, so we can safely allocate new IDs without conflicts
562 max_uid
= SYSTEM_ENTITY
;
564 XMBElement ents
= nodes
.GetFirstNamedItem(xmb_file
.GetElementID("Entities"));
565 XERO_ITER_EL(ents
, ent
)
567 CStr uid
= ent
.GetAttributes().GetNamedItem(at_uid
);
568 max_uid
= std::max(max_uid
, (entity_id_t
)uid
.ToUInt());
573 CStr
CXMLReader::ReadScriptSettings()
575 XMBElement root
= xmb_file
.GetRoot();
576 ENSURE(xmb_file
.GetElementStringView(root
.GetNodeName()) == "Scenario");
577 nodes
= root
.GetChildNodes();
579 XMBElement settings
= nodes
.GetFirstNamedItem(xmb_file
.GetElementID("ScriptSettings"));
581 return settings
.GetText();
585 void CXMLReader::ReadTerrain(XMBElement parent
)
587 #define AT(x) int at_##x = xmb_file.GetAttributeID(#x)
595 CStr texture
= "grass1_spring";
599 XERO_ITER_ATTR(parent
, attr
)
601 if (attr
.Name
== at_patches
)
602 patches
= attr
.Value
.ToInt();
603 else if (attr
.Name
== at_texture
)
604 texture
= attr
.Value
;
605 else if (attr
.Name
== at_priority
)
606 priority
= attr
.Value
.ToInt();
607 else if (attr
.Name
== at_height
)
608 height
= (u16
)attr
.Value
.ToInt();
611 m_MapReader
.m_PatchesPerSide
= patches
;
614 CTerrainTextureEntry
* texentry
= nullptr;
615 if (CTerrainTextureManager::IsInitialised())
616 texentry
= g_TexMan
.FindTexture(texture
);
618 m_MapReader
.pTerrain
->Initialize(patches
, NULL
);
620 // Fill the heightmap
621 u16
* heightmap
= m_MapReader
.pTerrain
->GetHeightMap();
622 ssize_t verticesPerSide
= m_MapReader
.pTerrain
->GetVerticesPerSide();
623 for (ssize_t i
= 0; i
< SQR(verticesPerSide
); ++i
)
624 heightmap
[i
] = height
;
626 // Fill the texture map
627 for (ssize_t pz
= 0; pz
< patches
; ++pz
)
629 for (ssize_t px
= 0; px
< patches
; ++px
)
631 CPatch
* patch
= m_MapReader
.pTerrain
->GetPatch(px
, pz
); // can't fail
633 for (ssize_t z
= 0; z
< PATCH_SIZE
; ++z
)
635 for (ssize_t x
= 0; x
< PATCH_SIZE
; ++x
)
637 patch
->m_MiniPatches
[z
][x
].Tex
= texentry
;
638 patch
->m_MiniPatches
[z
][x
].Priority
= priority
;
645 void CXMLReader::ReadEnvironment(XMBElement parent
)
647 #define EL(x) int el_##x = xmb_file.GetElementID(#x)
648 #define AT(x) int at_##x = xmb_file.GetAttributeID(#x)
677 XERO_ITER_EL(parent
, element
)
679 int element_name
= element
.GetNodeName();
681 XMBAttributeList attrs
= element
.GetAttributes();
683 if (element_name
== el_skyset
)
685 if (m_MapReader
.pSkyMan
)
686 m_MapReader
.pSkyMan
->SetSkySet(element
.GetText().FromUTF8());
688 else if (element_name
== el_suncolor
)
690 m_MapReader
.m_LightEnv
.m_SunColor
= RGBColor(
691 attrs
.GetNamedItem(at_r
).ToFloat(),
692 attrs
.GetNamedItem(at_g
).ToFloat(),
693 attrs
.GetNamedItem(at_b
).ToFloat());
695 else if (element_name
== el_sunelevation
)
697 m_MapReader
.m_LightEnv
.m_Elevation
= attrs
.GetNamedItem(at_angle
).ToFloat();
699 else if (element_name
== el_sunrotation
)
701 m_MapReader
.m_LightEnv
.m_Rotation
= attrs
.GetNamedItem(at_angle
).ToFloat();
703 else if (element_name
== el_ambientcolor
)
705 m_MapReader
.m_LightEnv
.m_AmbientColor
= RGBColor(
706 attrs
.GetNamedItem(at_r
).ToFloat(),
707 attrs
.GetNamedItem(at_g
).ToFloat(),
708 attrs
.GetNamedItem(at_b
).ToFloat());
710 else if (element_name
== el_fog
)
712 XERO_ITER_EL(element
, fog
)
714 int fog_element_name
= fog
.GetNodeName();
715 if (fog_element_name
== el_fogcolor
)
717 XMBAttributeList fogAttributes
= fog
.GetAttributes();
718 m_MapReader
.m_LightEnv
.m_FogColor
= RGBColor(
719 fogAttributes
.GetNamedItem(at_r
).ToFloat(),
720 fogAttributes
.GetNamedItem(at_g
).ToFloat(),
721 fogAttributes
.GetNamedItem(at_b
).ToFloat());
723 else if (fog_element_name
== el_fogfactor
)
725 m_MapReader
.m_LightEnv
.m_FogFactor
= fog
.GetText().ToFloat();
727 else if (fog_element_name
== el_fogthickness
)
729 m_MapReader
.m_LightEnv
.m_FogMax
= fog
.GetText().ToFloat();
733 else if (element_name
== el_postproc
)
735 XERO_ITER_EL(element
, postproc
)
737 int post_element_name
= postproc
.GetNodeName();
738 if (post_element_name
== el_brightness
)
740 m_MapReader
.m_LightEnv
.m_Brightness
= postproc
.GetText().ToFloat();
742 else if (post_element_name
== el_contrast
)
744 m_MapReader
.m_LightEnv
.m_Contrast
= postproc
.GetText().ToFloat();
746 else if (post_element_name
== el_saturation
)
748 m_MapReader
.m_LightEnv
.m_Saturation
= postproc
.GetText().ToFloat();
750 else if (post_element_name
== el_bloom
)
752 m_MapReader
.m_LightEnv
.m_Bloom
= postproc
.GetText().ToFloat();
754 else if (post_element_name
== el_posteffect
)
756 if (m_MapReader
.pPostproc
)
757 m_MapReader
.pPostproc
->SetPostEffect(postproc
.GetText().FromUTF8());
761 else if (element_name
== el_water
)
763 XERO_ITER_EL(element
, waterbody
)
765 ENSURE(waterbody
.GetNodeName() == el_waterbody
);
766 XERO_ITER_EL(waterbody
, waterelement
)
768 int water_element_name
= waterelement
.GetNodeName();
769 if (water_element_name
== el_height
)
771 CmpPtr
<ICmpWaterManager
> cmpWaterManager(*m_MapReader
.pSimContext
, SYSTEM_ENTITY
);
772 ENSURE(cmpWaterManager
);
773 cmpWaterManager
->SetWaterLevel(entity_pos_t::FromString(waterelement
.GetText()));
777 // The rest are purely graphical effects, and should be ignored if
778 // graphics are disabled
779 if (!m_MapReader
.pWaterMan
)
782 if (water_element_name
== el_type
)
784 if (waterelement
.GetText() == "default")
785 m_MapReader
.pWaterMan
->m_WaterType
= L
"ocean";
787 m_MapReader
.pWaterMan
->m_WaterType
= waterelement
.GetText().FromUTF8();
789 #define READ_COLOR(el, out) \
790 else if (water_element_name == el) \
792 XMBAttributeList colorAttrs = waterelement.GetAttributes(); \
794 colorAttrs.GetNamedItem(at_r).ToFloat(), \
795 colorAttrs.GetNamedItem(at_g).ToFloat(), \
796 colorAttrs.GetNamedItem(at_b).ToFloat(), \
800 #define READ_FLOAT(el, out) \
801 else if (water_element_name == el) \
803 out = waterelement.GetText().ToFloat(); \
806 READ_COLOR(el_color, m_MapReader.pWaterMan->m_WaterColor)
807 READ_COLOR(el_tint
, m_MapReader
.pWaterMan
->m_WaterTint
)
808 READ_FLOAT(el_waviness
, m_MapReader
.pWaterMan
->m_Waviness
)
809 READ_FLOAT(el_murkiness
, m_MapReader
.pWaterMan
->m_Murkiness
)
810 READ_FLOAT(el_windangle
, m_MapReader
.pWaterMan
->m_WindAngle
)
816 debug_warn(L
"Invalid map XML data");
822 debug_warn(L
"Invalid map XML data");
825 m_MapReader
.m_LightEnv
.CalculateSunDirection();
828 void CXMLReader::ReadCamera(XMBElement parent
)
830 // defaults if we don't find player starting camera
831 #define EL(x) int el_##x = xmb_file.GetElementID(#x)
832 #define AT(x) int at_##x = xmb_file.GetAttributeID(#x)
841 float declination
= DEGTORAD(30.f
), rotation
= DEGTORAD(-45.f
);
842 CVector3D translation
= CVector3D(100, 150, -100);
844 XERO_ITER_EL(parent
, element
)
846 int element_name
= element
.GetNodeName();
848 XMBAttributeList attrs
= element
.GetAttributes();
849 if (element_name
== el_declination
)
851 declination
= attrs
.GetNamedItem(at_angle
).ToFloat();
853 else if (element_name
== el_rotation
)
855 rotation
= attrs
.GetNamedItem(at_angle
).ToFloat();
857 else if (element_name
== el_position
)
859 translation
= CVector3D(
860 attrs
.GetNamedItem(at_x
).ToFloat(),
861 attrs
.GetNamedItem(at_y
).ToFloat(),
862 attrs
.GetNamedItem(at_z
).ToFloat());
865 debug_warn(L
"Invalid map XML data");
868 if (m_MapReader
.pGameView
)
870 m_MapReader
.pGameView
->GetCamera()->m_Orientation
.SetXRotation(declination
);
871 m_MapReader
.pGameView
->GetCamera()->m_Orientation
.RotateY(rotation
);
872 m_MapReader
.pGameView
->GetCamera()->m_Orientation
.Translate(translation
);
873 m_MapReader
.pGameView
->GetCamera()->UpdateFrustum();
877 void CXMLReader::ReadPaths(XMBElement parent
)
879 #define EL(x) int el_##x = xmb_file.GetElementID(#x)
880 #define AT(x) int at_##x = xmb_file.GetAttributeID(#x)
900 CmpPtr
<ICmpCinemaManager
> cmpCinemaManager(*m_MapReader
.pSimContext
, SYSTEM_ENTITY
);
901 XERO_ITER_EL(parent
, element
)
903 int elementName
= element
.GetNodeName();
905 if (elementName
== el_path
)
907 CCinemaData pathData
;
908 XMBAttributeList attrs
= element
.GetAttributes();
909 CStrW
pathName(attrs
.GetNamedItem(at_name
).FromUTF8());
910 pathData
.m_Name
= pathName
;
911 pathData
.m_Timescale
= fixed::FromString(attrs
.GetNamedItem(at_timescale
));
912 pathData
.m_Orientation
= attrs
.GetNamedItem(at_orientation
).FromUTF8();
913 pathData
.m_Mode
= attrs
.GetNamedItem(at_mode
).FromUTF8();
914 pathData
.m_Style
= attrs
.GetNamedItem(at_style
).FromUTF8();
916 TNSpline positionSpline
, targetSpline
;
917 fixed lastPositionTime
= fixed::Zero();
918 fixed lastTargetTime
= fixed::Zero();
920 XERO_ITER_EL(element
, pathChild
)
922 elementName
= pathChild
.GetNodeName();
923 attrs
= pathChild
.GetAttributes();
925 // Load node data used for spline
926 if (elementName
== el_node
)
928 lastPositionTime
+= fixed::FromString(attrs
.GetNamedItem(at_deltatime
));
929 lastTargetTime
+= fixed::FromString(attrs
.GetNamedItem(at_deltatime
));
930 XERO_ITER_EL(pathChild
, nodeChild
)
932 elementName
= nodeChild
.GetNodeName();
933 attrs
= nodeChild
.GetAttributes();
935 if (elementName
== el_position
)
937 CFixedVector3D
position(fixed::FromString(attrs
.GetNamedItem(at_x
)),
938 fixed::FromString(attrs
.GetNamedItem(at_y
)),
939 fixed::FromString(attrs
.GetNamedItem(at_z
)));
941 positionSpline
.AddNode(position
, CFixedVector3D(), lastPositionTime
);
942 lastPositionTime
= fixed::Zero();
944 else if (elementName
== el_rotation
)
946 // TODO: Implement rotation slerp/spline as another object
948 else if (elementName
== el_target
)
950 CFixedVector3D
targetPosition(fixed::FromString(attrs
.GetNamedItem(at_x
)),
951 fixed::FromString(attrs
.GetNamedItem(at_y
)),
952 fixed::FromString(attrs
.GetNamedItem(at_z
)));
954 targetSpline
.AddNode(targetPosition
, CFixedVector3D(), lastTargetTime
);
955 lastTargetTime
= fixed::Zero();
958 LOGWARNING("Invalid cinematic element for node child");
962 LOGWARNING("Invalid cinematic element for path child");
965 // Construct cinema path with data gathered
966 CCinemaPath
path(pathData
, positionSpline
, targetSpline
);
969 LOGWARNING("Path with name '%s' is empty", pathName
.ToUTF8());
973 if (!cmpCinemaManager
)
975 if (!cmpCinemaManager
->HasPath(pathName
))
976 cmpCinemaManager
->AddPath(path
);
978 LOGWARNING("Path with name '%s' already exists", pathName
.ToUTF8());
981 LOGWARNING("Invalid path child with name '%s'", element
.GetText());
985 void CXMLReader::ReadTriggers(XMBElement
UNUSED(parent
))
989 int CXMLReader::ReadEntities(XMBElement parent
, double end_time
)
991 XMBElementList entities
= parent
.GetChildNodes();
993 ENSURE(m_MapReader
.pSimulation2
);
994 CSimulation2
& sim
= *m_MapReader
.pSimulation2
;
995 CmpPtr
<ICmpPlayerManager
> cmpPlayerManager(sim
, SYSTEM_ENTITY
);
997 while (entity_idx
< entities
.size())
999 // all new state at this scope and below doesn't need to be
1000 // wrapped, since we only yield after a complete iteration.
1002 XMBElement entity
= entities
[entity_idx
++];
1003 ENSURE(entity
.GetNodeName() == el_entity
);
1005 XMBAttributeList attrs
= entity
.GetAttributes();
1006 CStr uid
= attrs
.GetNamedItem(at_uid
);
1007 ENSURE(!uid
.empty());
1008 int EntityUid
= uid
.ToInt();
1012 std::vector
<entity_id_t
> Garrison
;
1013 std::vector
<std::pair
<std::string
, entity_id_t
>> Turrets
;
1014 CFixedVector3D Position
;
1015 CFixedVector3D Orientation
;
1018 // Obstruction control groups.
1019 entity_id_t ControlGroup
= INVALID_ENTITY
;
1020 entity_id_t ControlGroup2
= INVALID_ENTITY
;
1022 XERO_ITER_EL(entity
, setting
)
1024 int element_name
= setting
.GetNodeName();
1027 if (element_name
== el_template
)
1029 TemplateName
= setting
.GetText().FromUTF8();
1032 else if (element_name
== el_player
)
1034 PlayerID
= setting
.GetText().ToInt();
1037 else if (element_name
== el_position
)
1039 XMBAttributeList positionAttrs
= setting
.GetAttributes();
1040 Position
= CFixedVector3D(
1041 fixed::FromString(positionAttrs
.GetNamedItem(at_x
)),
1042 fixed::FromString(positionAttrs
.GetNamedItem(at_y
)),
1043 fixed::FromString(positionAttrs
.GetNamedItem(at_z
)));
1046 else if (element_name
== el_orientation
)
1048 XMBAttributeList orientationAttrs
= setting
.GetAttributes();
1049 Orientation
= CFixedVector3D(
1050 fixed::FromString(orientationAttrs
.GetNamedItem(at_x
)),
1051 fixed::FromString(orientationAttrs
.GetNamedItem(at_y
)),
1052 fixed::FromString(orientationAttrs
.GetNamedItem(at_z
)));
1053 // TODO: what happens if some attributes are missing?
1056 else if (element_name
== el_obstruction
)
1058 XMBAttributeList obstructionAttrs
= setting
.GetAttributes();
1059 ControlGroup
= obstructionAttrs
.GetNamedItem(at_group
).ToInt();
1060 ControlGroup2
= obstructionAttrs
.GetNamedItem(at_group2
).ToInt();
1063 else if (element_name
== el_garrison
)
1065 XMBElementList garrison
= setting
.GetChildNodes();
1066 Garrison
.reserve(garrison
.size());
1067 for (const XMBElement
& garr_ent
: garrison
)
1069 XMBAttributeList garrisonAttrs
= garr_ent
.GetAttributes();
1070 Garrison
.push_back(garrisonAttrs
.GetNamedItem(at_uid
).ToInt());
1074 else if (element_name
== el_turrets
)
1076 XMBElementList turrets
= setting
.GetChildNodes();
1077 Turrets
.reserve(turrets
.size());
1078 for (const XMBElement
& turretPoint
: turrets
)
1080 XMBAttributeList turretAttrs
= turretPoint
.GetAttributes();
1081 Turrets
.emplace_back(
1082 turretAttrs
.GetNamedItem(at_turret
),
1083 turretAttrs
.GetNamedItem(at_uid
).ToInt()
1088 else if (element_name
== el_actor
)
1090 XMBAttributeList attrs
= setting
.GetAttributes();
1091 CStr seedStr
= attrs
.GetNamedItem(at_seed
);
1092 if (!seedStr
.empty())
1094 Seed
= seedStr
.ToLong();
1099 debug_warn(L
"Invalid map XML data");
1102 entity_id_t ent
= sim
.AddEntity(TemplateName
, EntityUid
);
1103 entity_id_t player
= cmpPlayerManager
->GetPlayerByID(PlayerID
);
1104 if (ent
== INVALID_ENTITY
|| player
== INVALID_ENTITY
)
1105 { // Don't add entities with invalid player IDs
1106 LOGERROR("Failed to load entity template '%s'", utf8_from_wstring(TemplateName
));
1110 CmpPtr
<ICmpPosition
> cmpPosition(sim
, ent
);
1113 cmpPosition
->JumpTo(Position
.X
, Position
.Z
);
1114 cmpPosition
->SetYRotation(Orientation
.Y
);
1115 // TODO: other parts of the position
1118 if (!Garrison
.empty())
1120 CmpPtr
<ICmpGarrisonHolder
> cmpGarrisonHolder(sim
, ent
);
1121 if (cmpGarrisonHolder
)
1122 cmpGarrisonHolder
->SetInitEntities(std::move(Garrison
));
1124 LOGERROR("CXMLMapReader::ReadEntities() entity '%d' of player '%d' has no GarrisonHolder component and thus cannot garrison units.", ent
, PlayerID
);
1127 // Needs to be before ownership changes to prevent initialising
1128 // subunits too soon.
1129 if (!Turrets
.empty())
1131 CmpPtr
<ICmpTurretHolder
> cmpTurretHolder(sim
, ent
);
1132 if (cmpTurretHolder
)
1133 cmpTurretHolder
->SetInitEntities(std::move(Turrets
));
1135 LOGERROR("CXMLMapReader::ReadEntities() entity '%d' of player '%d' has no TurretHolder component and thus cannot use turrets.", ent
, PlayerID
);
1138 CmpPtr
<ICmpOwnership
> cmpOwnership(sim
, ent
);
1140 cmpOwnership
->SetOwner(PlayerID
);
1142 CmpPtr
<ICmpObstruction
> cmpObstruction(sim
, ent
);
1145 if (ControlGroup
!= INVALID_ENTITY
)
1146 cmpObstruction
->SetControlGroup(ControlGroup
);
1147 if (ControlGroup2
!= INVALID_ENTITY
)
1148 cmpObstruction
->SetControlGroup2(ControlGroup2
);
1150 cmpObstruction
->ResolveFoundationCollisions();
1153 CmpPtr
<ICmpVisual
> cmpVisual(sim
, ent
);
1157 cmpVisual
->SetActorSeed((u32
)Seed
);
1158 // TODO: variation/selection strings
1161 if (PlayerID
== m_MapReader
.m_PlayerID
&& (boost::algorithm::ends_with(TemplateName
, L
"civil_centre") || m_MapReader
.m_StartingCameraTarget
== INVALID_ENTITY
))
1163 // Focus on civil centre or first entity owned by player
1164 m_MapReader
.m_StartingCameraTarget
= ent
;
1169 LDR_CHECK_TIMEOUT(completed_jobs
, total_jobs
);
1175 void CXMLReader::ReadXML()
1177 for (XMBElement node
: nodes
)
1179 CStr name
= xmb_file
.GetElementString(node
.GetNodeName());
1180 if (name
== "Terrain")
1184 else if (name
== "Environment")
1186 ReadEnvironment(node
);
1188 else if (name
== "Camera")
1192 else if (name
== "ScriptSettings")
1194 // Already loaded - this is to prevent an assertion
1196 else if (name
== "Entities")
1198 // Handled by ProgressiveReadEntities instead
1200 else if (name
== "Paths")
1204 else if (name
== "Triggers")
1208 else if (name
== "Script")
1210 if (m_MapReader
.pSimulation2
)
1211 m_MapReader
.pSimulation2
->SetStartupScript(node
.GetText());
1215 debug_printf("Invalid XML element in map file: %s\n", name
.c_str());
1216 debug_warn(L
"Invalid map XML data");
1221 int CXMLReader::ProgressiveReadEntities()
1223 // yield after this time is reached. balances increased progress bar
1224 // smoothness vs. slowing down loading.
1225 const double end_time
= timer_Time() + 200e-3;
1229 while (node_idx
< nodes
.size())
1231 XMBElement node
= nodes
[node_idx
];
1232 CStr name
= xmb_file
.GetElementString(node
.GetNodeName());
1233 if (name
== "Entities")
1235 if (!m_MapReader
.m_SkipEntities
)
1237 ret
= ReadEntities(node
, end_time
);
1238 if (ret
!= 0) // error or timed out
1249 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1251 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1254 // load script settings from map
1255 int CMapReader::LoadScriptSettings()
1258 xml_reader
= new CXMLReader(filename_xml
, *this);
1260 // parse the script settings
1262 pSimulation2
->SetMapSettings(xml_reader
->ReadScriptSettings());
1267 // load player settings script
1268 int CMapReader::LoadPlayerSettings()
1271 pSimulation2
->LoadPlayerSettings(true);
1275 // load map settings script
1276 int CMapReader::LoadMapSettings()
1279 pSimulation2
->LoadMapSettings();
1283 int CMapReader::ReadXML()
1286 xml_reader
= new CXMLReader(filename_xml
, *this);
1288 xml_reader
->ReadXML();
1294 int CMapReader::ReadXMLEntities()
1297 xml_reader
= new CXMLReader(filename_xml
, *this);
1299 int ret
= xml_reader
->ProgressiveReadEntities();
1300 // finished or failed
1303 SAFE_DELETE(xml_reader
);
1309 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1311 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1314 int CMapReader::LoadRMSettings()
1316 // copy random map settings over to sim
1317 ENSURE(pSimulation2
);
1318 pSimulation2
->SetMapSettings(m_ScriptSettings
);
1323 int CMapReader::GenerateMap(const CStrW
& scriptFile
)
1325 ScriptRequest
rq(pSimulation2
->GetScriptInterface());
1329 // Initialize map generator
1330 m_MapGen
= new CMapGenerator();
1334 if (scriptFile
.length())
1335 scriptPath
= L
"maps/random/" + scriptFile
;
1337 // Stringify settings to pass across threads
1338 std::string scriptSettings
= Script::StringifyJSON(rq
, &m_ScriptSettings
);
1340 // Try to generate map
1341 m_MapGen
->GenerateMap(scriptPath
, scriptSettings
);
1345 int progress
= m_MapGen
->GetProgress();
1348 // RMS failed - return to main menu
1349 throw PSERROR_Game_World_MapLoadFailed("Error generating random map.\nCheck application log for details.");
1351 else if (progress
== 0)
1353 // Finished, get results as StructuredClone object, which must be read to obtain the JS::Value
1354 Script::StructuredClone results
= m_MapGen
->GetResults();
1356 // Parse data into simulation context
1357 JS::RootedValue
data(rq
.cx
);
1358 Script::ReadStructuredClone(rq
, results
, &data
);
1360 if (data
.isUndefined())
1362 // RMS failed - return to main menu
1363 throw PSERROR_Game_World_MapLoadFailed("Error generating random map.\nCheck application log for details.");
1367 m_MapData
.init(rq
.cx
, data
);
1376 int CMapReader::ParseTerrain()
1378 TIMER(L
"ParseTerrain");
1379 ScriptRequest
rq(pSimulation2
->GetScriptInterface());
1381 // parse terrain from map data
1382 // an error here should stop the loading process
1383 #define GET_TERRAIN_PROPERTY(val, prop, out)\
1384 if (!Script::GetProperty(rq, val, #prop, out))\
1385 { LOGERROR("CMapReader::ParseTerrain() failed to get '%s' property", #prop);\
1386 throw PSERROR_Game_World_MapLoadFailed("Error parsing terrain data.\nCheck application log for details"); }
1389 GET_TERRAIN_PROPERTY(m_MapData
, size
, size
)
1391 m_PatchesPerSide
= size
/ PATCH_SIZE
;
1393 // flat heightmap of u16 data
1394 GET_TERRAIN_PROPERTY(m_MapData
, height
, m_Heightmap
)
1397 std::vector
<std::string
> textureNames
;
1398 GET_TERRAIN_PROPERTY(m_MapData
, textureNames
, textureNames
)
1399 num_terrain_tex
= textureNames
.size();
1401 while (cur_terrain_tex
< num_terrain_tex
)
1403 if (CTerrainTextureManager::IsInitialised())
1405 CTerrainTextureEntry
* texentry
= g_TexMan
.FindTexture(textureNames
[cur_terrain_tex
]);
1406 m_TerrainTextures
.push_back(texentry
);
1413 m_Tiles
.resize(SQR(size
));
1415 JS::RootedValue
tileData(rq
.cx
);
1416 GET_TERRAIN_PROPERTY(m_MapData
, tileData
, &tileData
)
1418 // parse tile data object into flat arrays
1419 std::vector
<u16
> tileIndex
;
1420 std::vector
<u16
> tilePriority
;
1421 GET_TERRAIN_PROPERTY(tileData
, index
, tileIndex
);
1422 GET_TERRAIN_PROPERTY(tileData
, priority
, tilePriority
);
1424 ENSURE(SQR(size
) == tileIndex
.size() && SQR(size
) == tilePriority
.size());
1426 // reorder by patches and store
1427 for (size_t x
= 0; x
< size
; ++x
)
1429 size_t patchX
= x
/ PATCH_SIZE
;
1430 size_t offX
= x
% PATCH_SIZE
;
1431 for (size_t y
= 0; y
< size
; ++y
)
1433 size_t patchY
= y
/ PATCH_SIZE
;
1434 size_t offY
= y
% PATCH_SIZE
;
1437 tile
.m_Tex1Index
= tileIndex
[y
*size
+ x
];
1438 tile
.m_Tex2Index
= 0xFFFF;
1439 tile
.m_Priority
= tilePriority
[y
*size
+ x
];
1441 m_Tiles
[(patchY
* m_PatchesPerSide
+ patchX
) * SQR(PATCH_SIZE
) + (offY
* PATCH_SIZE
+ offX
)] = tile
;
1445 // reset generator state
1446 cur_terrain_tex
= 0;
1448 #undef GET_TERRAIN_PROPERTY
1453 int CMapReader::ParseEntities()
1455 TIMER(L
"ParseEntities");
1456 ScriptRequest
rq(pSimulation2
->GetScriptInterface());
1458 // parse entities from map data
1459 std::vector
<Entity
> entities
;
1461 if (!Script::GetProperty(rq
, m_MapData
, "entities", entities
))
1462 LOGWARNING("CMapReader::ParseEntities() failed to get 'entities' property");
1464 CSimulation2
& sim
= *pSimulation2
;
1465 CmpPtr
<ICmpPlayerManager
> cmpPlayerManager(sim
, SYSTEM_ENTITY
);
1467 size_t entity_idx
= 0;
1468 size_t num_entities
= entities
.size();
1472 while (entity_idx
< num_entities
)
1474 // Get current entity struct
1475 currEnt
= entities
[entity_idx
];
1477 entity_id_t ent
= pSimulation2
->AddEntity(currEnt
.templateName
, currEnt
.entityID
);
1478 entity_id_t player
= cmpPlayerManager
->GetPlayerByID(currEnt
.playerID
);
1479 if (ent
== INVALID_ENTITY
|| player
== INVALID_ENTITY
)
1480 { // Don't add entities with invalid player IDs
1481 LOGERROR("Failed to load entity template '%s'", utf8_from_wstring(currEnt
.templateName
));
1485 CmpPtr
<ICmpPosition
> cmpPosition(sim
, ent
);
1488 cmpPosition
->JumpTo(currEnt
.position
.X
* (int)TERRAIN_TILE_SIZE
, currEnt
.position
.Z
* (int)TERRAIN_TILE_SIZE
);
1489 cmpPosition
->SetYRotation(currEnt
.rotation
.Y
);
1490 // TODO: other parts of the position
1493 CmpPtr
<ICmpOwnership
> cmpOwnership(sim
, ent
);
1495 cmpOwnership
->SetOwner(currEnt
.playerID
);
1497 // Detect and fix collisions between foundation-blocking entities.
1498 // This presently serves to copy wall tower control groups to wall
1499 // segments, allowing players to expand RMS-generated walls.
1500 CmpPtr
<ICmpObstruction
> cmpObstruction(sim
, ent
);
1502 cmpObstruction
->ResolveFoundationCollisions();
1504 if (currEnt
.playerID
== m_PlayerID
&& (boost::algorithm::ends_with(currEnt
.templateName
, L
"civil_centre") || m_StartingCameraTarget
== INVALID_ENTITY
))
1506 // Focus on civil centre or first entity owned by player
1507 m_StartingCameraTarget
= currEnt
.entityID
;
1517 int CMapReader::ParseEnvironment()
1519 // parse environment settings from map data
1520 ScriptRequest
rq(pSimulation2
->GetScriptInterface());
1522 #define GET_ENVIRONMENT_PROPERTY(val, prop, out)\
1523 if (!Script::GetProperty(rq, val, #prop, out))\
1524 LOGWARNING("CMapReader::ParseEnvironment() failed to get '%s' property", #prop);
1526 JS::RootedValue
envObj(rq
.cx
);
1527 GET_ENVIRONMENT_PROPERTY(m_MapData
, Environment
, &envObj
)
1529 if (envObj
.isUndefined())
1531 LOGWARNING("CMapReader::ParseEnvironment(): Environment settings not found");
1536 pPostproc
->SetPostEffect(L
"default");
1538 std::wstring skySet
;
1539 GET_ENVIRONMENT_PROPERTY(envObj
, SkySet
, skySet
)
1541 pSkyMan
->SetSkySet(skySet
);
1544 GET_ENVIRONMENT_PROPERTY(envObj
, SunColor
, sunColor
)
1545 m_LightEnv
.m_SunColor
= RGBColor(sunColor
.r
, sunColor
.g
, sunColor
.b
);
1547 GET_ENVIRONMENT_PROPERTY(envObj
, SunElevation
, m_LightEnv
.m_Elevation
)
1548 GET_ENVIRONMENT_PROPERTY(envObj
, SunRotation
, m_LightEnv
.m_Rotation
)
1550 CColor ambientColor
;
1551 GET_ENVIRONMENT_PROPERTY(envObj
, AmbientColor
, ambientColor
)
1552 m_LightEnv
.m_AmbientColor
= RGBColor(ambientColor
.r
, ambientColor
.g
, ambientColor
.b
);
1555 JS::RootedValue
waterObj(rq
.cx
);
1556 GET_ENVIRONMENT_PROPERTY(envObj
, Water
, &waterObj
)
1558 JS::RootedValue
waterBodyObj(rq
.cx
);
1559 GET_ENVIRONMENT_PROPERTY(waterObj
, WaterBody
, &waterBodyObj
)
1561 // Water level - necessary
1563 GET_ENVIRONMENT_PROPERTY(waterBodyObj
, Height
, waterHeight
)
1565 CmpPtr
<ICmpWaterManager
> cmpWaterManager(*pSimulation2
, SYSTEM_ENTITY
);
1566 ENSURE(cmpWaterManager
);
1567 cmpWaterManager
->SetWaterLevel(entity_pos_t::FromFloat(waterHeight
));
1569 // If we have graphics, get rest of settings
1572 GET_ENVIRONMENT_PROPERTY(waterBodyObj
, Type
, pWaterMan
->m_WaterType
)
1573 if (pWaterMan
->m_WaterType
== L
"default")
1574 pWaterMan
->m_WaterType
= L
"ocean";
1575 GET_ENVIRONMENT_PROPERTY(waterBodyObj
, Color
, pWaterMan
->m_WaterColor
)
1576 GET_ENVIRONMENT_PROPERTY(waterBodyObj
, Tint
, pWaterMan
->m_WaterTint
)
1577 GET_ENVIRONMENT_PROPERTY(waterBodyObj
, Waviness
, pWaterMan
->m_Waviness
)
1578 GET_ENVIRONMENT_PROPERTY(waterBodyObj
, Murkiness
, pWaterMan
->m_Murkiness
)
1579 GET_ENVIRONMENT_PROPERTY(waterBodyObj
, WindAngle
, pWaterMan
->m_WindAngle
)
1582 JS::RootedValue
fogObject(rq
.cx
);
1583 GET_ENVIRONMENT_PROPERTY(envObj
, Fog
, &fogObject
);
1585 GET_ENVIRONMENT_PROPERTY(fogObject
, FogFactor
, m_LightEnv
.m_FogFactor
);
1586 GET_ENVIRONMENT_PROPERTY(fogObject
, FogThickness
, m_LightEnv
.m_FogMax
);
1589 GET_ENVIRONMENT_PROPERTY(fogObject
, FogColor
, fogColor
);
1590 m_LightEnv
.m_FogColor
= RGBColor(fogColor
.r
, fogColor
.g
, fogColor
.b
);
1592 JS::RootedValue
postprocObject(rq
.cx
);
1593 GET_ENVIRONMENT_PROPERTY(envObj
, Postproc
, &postprocObject
);
1595 std::wstring postProcEffect
;
1596 GET_ENVIRONMENT_PROPERTY(postprocObject
, PostprocEffect
, postProcEffect
);
1599 pPostproc
->SetPostEffect(postProcEffect
);
1601 GET_ENVIRONMENT_PROPERTY(postprocObject
, Brightness
, m_LightEnv
.m_Brightness
);
1602 GET_ENVIRONMENT_PROPERTY(postprocObject
, Contrast
, m_LightEnv
.m_Contrast
);
1603 GET_ENVIRONMENT_PROPERTY(postprocObject
, Saturation
, m_LightEnv
.m_Saturation
);
1604 GET_ENVIRONMENT_PROPERTY(postprocObject
, Bloom
, m_LightEnv
.m_Bloom
);
1606 m_LightEnv
.CalculateSunDirection();
1608 #undef GET_ENVIRONMENT_PROPERTY
1613 int CMapReader::ParseCamera()
1615 ScriptRequest
rq(pSimulation2
->GetScriptInterface());
1617 // parse camera settings from map data
1618 // defaults if we don't find player starting camera
1619 float declination
= DEGTORAD(30.f
), rotation
= DEGTORAD(-45.f
);
1620 CVector3D translation
= CVector3D(100, 150, -100);
1622 #define GET_CAMERA_PROPERTY(val, prop, out)\
1623 if (!Script::GetProperty(rq, val, #prop, out))\
1624 LOGWARNING("CMapReader::ParseCamera() failed to get '%s' property", #prop);
1626 JS::RootedValue
cameraObj(rq
.cx
);
1627 GET_CAMERA_PROPERTY(m_MapData
, Camera
, &cameraObj
)
1629 if (!cameraObj
.isUndefined())
1630 { // If camera property exists, read values
1632 GET_CAMERA_PROPERTY(cameraObj
, Position
, pos
)
1635 GET_CAMERA_PROPERTY(cameraObj
, Rotation
, rotation
)
1636 GET_CAMERA_PROPERTY(cameraObj
, Declination
, declination
)
1638 #undef GET_CAMERA_PROPERTY
1642 pGameView
->GetCamera()->m_Orientation
.SetXRotation(declination
);
1643 pGameView
->GetCamera()->m_Orientation
.RotateY(rotation
);
1644 pGameView
->GetCamera()->m_Orientation
.Translate(translation
);
1645 pGameView
->GetCamera()->UpdateFrustum();
1651 CMapReader::~CMapReader()