LCOV - code coverage report
Current view: top level - source/graphics - MapReader.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 1 755 0.1 %
Date: 2023-01-19 00:18:29 Functions: 2 35 5.7 %

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

Generated by: LCOV version 1.13