LCOV - code coverage report
Current view: top level - source/graphics - MapWriter.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 1 236 0.4 %
Date: 2023-01-19 00:18:29 Functions: 2 11 18.2 %

          Line data    Source code
       1             : /* Copyright (C) 2021 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 "Camera.h"
      21             : #include "CinemaManager.h"
      22             : #include "GameView.h"
      23             : #include "LightEnv.h"
      24             : #include "MapReader.h"
      25             : #include "MapWriter.h"
      26             : #include "Patch.h"
      27             : #include "Terrain.h"
      28             : #include "TerrainTextureEntry.h"
      29             : #include "TerrainTextureManager.h"
      30             : 
      31             : #include "maths/MathUtil.h"
      32             : #include "maths/NUSpline.h"
      33             : #include "ps/CLogger.h"
      34             : #include "ps/Loader.h"
      35             : #include "ps/Filesystem.h"
      36             : #include "ps/XML/XMLWriter.h"
      37             : #include "renderer/PostprocManager.h"
      38             : #include "renderer/SkyManager.h"
      39             : #include "renderer/WaterManager.h"
      40             : #include "simulation2/Simulation2.h"
      41             : #include "simulation2/components/ICmpCinemaManager.h"
      42             : #include "simulation2/components/ICmpGarrisonHolder.h"
      43             : #include "simulation2/components/ICmpObstruction.h"
      44             : #include "simulation2/components/ICmpOwnership.h"
      45             : #include "simulation2/components/ICmpPosition.h"
      46             : #include "simulation2/components/ICmpTemplateManager.h"
      47             : #include "simulation2/components/ICmpTurretHolder.h"
      48             : #include "simulation2/components/ICmpVisual.h"
      49             : #include "simulation2/components/ICmpWaterManager.h"
      50             : 
      51             : ///////////////////////////////////////////////////////////////////////////////////////////////////
      52             : // CMapWriter constructor: nothing to do at the minute
      53           0 : CMapWriter::CMapWriter()
      54             : {
      55           0 : }
      56             : 
      57             : ///////////////////////////////////////////////////////////////////////////////////////////////////
      58             : // SaveMap: try to save the current map to the given file
      59           0 : void CMapWriter::SaveMap(const VfsPath& pathname, CTerrain* pTerrain,
      60             :                          WaterManager* pWaterMan, SkyManager* pSkyMan,
      61             :                          CLightEnv* pLightEnv, CCamera* pCamera, CCinemaManager* UNUSED(pCinema),
      62             :                          CPostprocManager* pPostproc,
      63             :                          CSimulation2* pSimulation2)
      64             : {
      65           0 :     CFilePacker packer(FILE_VERSION, "PSMP");
      66             : 
      67             :     // build necessary data
      68           0 :     PackMap(packer, pTerrain);
      69             : 
      70             :     try
      71             :     {
      72             :         // write it out
      73           0 :         packer.Write(pathname);
      74             :     }
      75           0 :     catch (PSERROR_File_WriteFailed&)
      76             :     {
      77           0 :         LOGERROR("Failed to write map '%s'", pathname.string8());
      78           0 :         return;
      79             :     }
      80             : 
      81           0 :     VfsPath pathnameXML = pathname.ChangeExtension(L".xml");
      82           0 :     WriteXML(pathnameXML, pWaterMan, pSkyMan, pLightEnv, pCamera, pPostproc, pSimulation2);
      83             : }
      84             : 
      85             : ///////////////////////////////////////////////////////////////////////////////////////////////////
      86             : // GetHandleIndex: return the index of the given handle in the given list; or 0xFFFF if
      87             : // handle isn't in list
      88           0 : static u16 GetEntryIndex(const CTerrainTextureEntry* entry, const std::vector<CTerrainTextureEntry*>& entries)
      89             : {
      90           0 :     const size_t limit = std::min(entries.size(), size_t(0xFFFEu)); // paranoia
      91           0 :     for (size_t i=0;i<limit;i++) {
      92           0 :         if (entries[i]==entry) {
      93           0 :             return (u16)i;
      94             :         }
      95             :     }
      96             : 
      97           0 :     return 0xFFFF;
      98             : }
      99             : 
     100             : ///////////////////////////////////////////////////////////////////////////////////////////////////
     101             : // EnumTerrainTextures: build lists of textures used by map, and tile descriptions for
     102             : // each tile on the terrain
     103           0 : void CMapWriter::EnumTerrainTextures(CTerrain *pTerrain,
     104             :                                      std::vector<CStr>& textures,
     105             :                                      std::vector<STileDesc>& tiles)
     106             : {
     107             :     // the list of all handles in use
     108           0 :     std::vector<CTerrainTextureEntry*> entries;
     109             : 
     110             :     // resize tile array to required size
     111           0 :     tiles.resize(SQR(pTerrain->GetVerticesPerSide()-1));
     112           0 :     STileDesc* tileptr=&tiles[0];
     113             : 
     114             :     // now iterate through all the tiles
     115           0 :     const ssize_t patchesPerSide=pTerrain->GetPatchesPerSide();
     116           0 :     for (ssize_t j=0;j<patchesPerSide;j++) {
     117           0 :         for (ssize_t i=0;i<patchesPerSide;i++) {
     118           0 :             for (ssize_t m=0;m<PATCH_SIZE;m++) {
     119           0 :                 for (ssize_t k=0;k<PATCH_SIZE;k++) {
     120           0 :                     CMiniPatch& mp=pTerrain->GetPatch(i,j)->m_MiniPatches[m][k];  // can't fail
     121           0 :                     u16 index=u16(GetEntryIndex(mp.GetTextureEntry(),entries));
     122           0 :                     if (index==0xFFFF) {
     123           0 :                         index=(u16)entries.size();
     124           0 :                         entries.push_back(mp.GetTextureEntry());
     125             :                     }
     126             : 
     127           0 :                     tileptr->m_Tex1Index=index;
     128           0 :                     tileptr->m_Tex2Index=0xFFFF;
     129           0 :                     tileptr->m_Priority=mp.GetPriority();
     130           0 :                     tileptr++;
     131             :                 }
     132             :             }
     133             :         }
     134             :     }
     135             : 
     136             :     // now find the texture names for each handle
     137           0 :     for (size_t i=0;i<entries.size();i++) {
     138           0 :         CStr texturename;
     139           0 :         CTerrainTextureEntry* texentry=entries[i];
     140           0 :         if (!texentry) {
     141             :             // uh-oh, this shouldn't happen; set texturename to empty string
     142           0 :             texturename="";
     143             :         } else {
     144           0 :             texturename=texentry->GetTag();
     145             :         }
     146           0 :         textures.push_back(texturename);
     147             :     }
     148           0 : }
     149             : 
     150             : ///////////////////////////////////////////////////////////////////////////////////////////////////
     151             : // PackMap: pack the current world into a raw data stream
     152           0 : void CMapWriter::PackMap(CFilePacker& packer, CTerrain* pTerrain)
     153             : {
     154             :     // now pack everything up
     155           0 :     PackTerrain(packer, pTerrain);
     156           0 : }
     157             : 
     158             : ///////////////////////////////////////////////////////////////////////////////////////////////////
     159             : // PackTerrain: pack the terrain onto the end of the output data stream
     160             : //      - data: map size, heightmap, list of textures used by map, texture tile assignments
     161           0 : void CMapWriter::PackTerrain(CFilePacker& packer, CTerrain* pTerrain)
     162             : {
     163             :     // pack map size
     164           0 :     const ssize_t mapsize = pTerrain->GetPatchesPerSide();
     165           0 :     packer.PackSize(mapsize);
     166             : 
     167             :     // pack heightmap
     168           0 :     packer.PackRaw(pTerrain->GetHeightMap(),sizeof(u16)*SQR(pTerrain->GetVerticesPerSide()));
     169             : 
     170             :     // the list of textures used by map
     171           0 :     std::vector<CStr> terrainTextures;
     172             :     // descriptions of each tile
     173           0 :     std::vector<STileDesc> tiles;
     174             : 
     175             :     // build lists by scanning through the terrain
     176           0 :     EnumTerrainTextures(pTerrain, terrainTextures, tiles);
     177             : 
     178             :     // pack texture names
     179           0 :     const size_t numTextures = terrainTextures.size();
     180           0 :     packer.PackSize(numTextures);
     181           0 :     for (size_t i=0;i<numTextures;i++)
     182           0 :         packer.PackString(terrainTextures[i]);
     183             : 
     184             :     // pack tile data
     185           0 :     packer.PackRaw(&tiles[0],sizeof(STileDesc)*tiles.size());
     186           0 : }
     187             : 
     188           0 : void CMapWriter::WriteXML(const VfsPath& filename,
     189             :                           WaterManager* pWaterMan, SkyManager* pSkyMan,
     190             :                           CLightEnv* pLightEnv, CCamera* pCamera,
     191             :                           CPostprocManager* pPostproc,
     192             :                           CSimulation2* pSimulation2)
     193             : {
     194           0 :     XMLWriter_File xmlMapFile;
     195             :     {
     196           0 :         XMLWriter_Element scenarioTag(xmlMapFile, "Scenario");
     197           0 :         scenarioTag.Attribute("version", static_cast<int>(FILE_VERSION));
     198             : 
     199           0 :         ENSURE(pSimulation2);
     200           0 :         CSimulation2& sim = *pSimulation2;
     201             : 
     202           0 :         if (!sim.GetStartupScript().empty())
     203             :         {
     204           0 :             XMLWriter_Element scriptTag(xmlMapFile, "Script");
     205           0 :             scriptTag.Text(sim.GetStartupScript().c_str(), true);
     206             :         }
     207             : 
     208             :         {
     209           0 :             XMLWriter_Element environmentTag(xmlMapFile, "Environment");
     210           0 :             environmentTag.Setting("SkySet", pSkyMan->GetSkySet());
     211             :             {
     212           0 :                 XMLWriter_Element sunColorTag(xmlMapFile, "SunColor");
     213           0 :                 sunColorTag.Attribute("r", pLightEnv->m_SunColor.X); // yes, it's X/Y/Z...
     214           0 :                 sunColorTag.Attribute("g", pLightEnv->m_SunColor.Y);
     215           0 :                 sunColorTag.Attribute("b", pLightEnv->m_SunColor.Z);
     216             :             }
     217             :             {
     218           0 :                 XMLWriter_Element SunElevationTag(xmlMapFile, "SunElevation");
     219           0 :                 SunElevationTag.Attribute("angle", pLightEnv->m_Elevation);
     220             :             }
     221             :             {
     222           0 :                 XMLWriter_Element sunRotationTag(xmlMapFile, "SunRotation");
     223           0 :                 sunRotationTag.Attribute("angle", pLightEnv->m_Rotation);
     224             :             }
     225             :             {
     226           0 :                 XMLWriter_Element ambientColorTag(xmlMapFile, "AmbientColor");
     227           0 :                 ambientColorTag.Attribute("r", pLightEnv->m_AmbientColor.X);
     228           0 :                 ambientColorTag.Attribute("g", pLightEnv->m_AmbientColor.Y);
     229           0 :                 ambientColorTag.Attribute("b", pLightEnv->m_AmbientColor.Z);
     230             :             }
     231             :             {
     232           0 :                 XMLWriter_Element fogTag(xmlMapFile, "Fog");
     233           0 :                 fogTag.Setting("FogFactor", pLightEnv->m_FogFactor);
     234           0 :                 fogTag.Setting("FogThickness", pLightEnv->m_FogMax);
     235             :                 {
     236           0 :                     XMLWriter_Element fogColorTag(xmlMapFile, "FogColor");
     237           0 :                     fogColorTag.Attribute("r", pLightEnv->m_FogColor.X);
     238           0 :                     fogColorTag.Attribute("g", pLightEnv->m_FogColor.Y);
     239           0 :                     fogColorTag.Attribute("b", pLightEnv->m_FogColor.Z);
     240             :                 }
     241             :             }
     242             : 
     243             :             {
     244           0 :                 XMLWriter_Element waterTag(xmlMapFile, "Water");
     245             :                 {
     246           0 :                     XMLWriter_Element waterBodyTag(xmlMapFile, "WaterBody");
     247           0 :                     CmpPtr<ICmpWaterManager> cmpWaterManager(sim, SYSTEM_ENTITY);
     248           0 :                     ENSURE(cmpWaterManager);
     249           0 :                     waterBodyTag.Setting("Type", pWaterMan->m_WaterType);
     250             :                     {
     251           0 :                         XMLWriter_Element colorTag(xmlMapFile, "Color");
     252           0 :                         colorTag.Attribute("r", pWaterMan->m_WaterColor.r);
     253           0 :                         colorTag.Attribute("g", pWaterMan->m_WaterColor.g);
     254           0 :                         colorTag.Attribute("b", pWaterMan->m_WaterColor.b);
     255             :                     }
     256             :                     {
     257           0 :                         XMLWriter_Element tintTag(xmlMapFile, "Tint");
     258           0 :                         tintTag.Attribute("r", pWaterMan->m_WaterTint.r);
     259           0 :                         tintTag.Attribute("g", pWaterMan->m_WaterTint.g);
     260           0 :                         tintTag.Attribute("b", pWaterMan->m_WaterTint.b);
     261             :                     }
     262           0 :                     waterBodyTag.Setting("Height", cmpWaterManager->GetExactWaterLevel(0, 0));
     263           0 :                     waterBodyTag.Setting("Waviness", pWaterMan->m_Waviness);
     264           0 :                     waterBodyTag.Setting("Murkiness", pWaterMan->m_Murkiness);
     265           0 :                     waterBodyTag.Setting("WindAngle", pWaterMan->m_WindAngle);
     266             :                 }
     267             :             }
     268             : 
     269             :             {
     270           0 :                 XMLWriter_Element postProcTag(xmlMapFile, "Postproc");
     271             :                 {
     272           0 :                     postProcTag.Setting("Brightness", pLightEnv->m_Brightness);
     273           0 :                     postProcTag.Setting("Contrast", pLightEnv->m_Contrast);
     274           0 :                     postProcTag.Setting("Saturation", pLightEnv->m_Saturation);
     275           0 :                     postProcTag.Setting("Bloom", pLightEnv->m_Bloom);
     276           0 :                     postProcTag.Setting("PostEffect", pPostproc->GetPostEffect());
     277             :                 }
     278             :             }
     279             :         }
     280             : 
     281             :         {
     282           0 :             XMLWriter_Element cameraTag(xmlMapFile, "Camera");
     283             :             {
     284           0 :                 XMLWriter_Element positionTag(xmlMapFile, "Position");
     285           0 :                 CVector3D pos = pCamera->GetOrientation().GetTranslation();
     286           0 :                 positionTag.Attribute("x", pos.X);
     287           0 :                 positionTag.Attribute("y", pos.Y);
     288           0 :                 positionTag.Attribute("z", pos.Z);
     289             :             }
     290             : 
     291           0 :             CVector3D in = pCamera->GetOrientation().GetIn();
     292             :             // Convert to spherical coordinates
     293           0 :             float rotation = atan2(in.X, in.Z);
     294           0 :             float declination = atan2(sqrt(in.X*in.X + in.Z*in.Z), in.Y) - static_cast<float>(M_PI / 2);
     295             : 
     296             :             {
     297           0 :                 XMLWriter_Element rotationTag(xmlMapFile, "Rotation");
     298           0 :                 rotationTag.Attribute("angle", rotation);
     299             :             }
     300             :             {
     301           0 :                 XMLWriter_Element declinationTag(xmlMapFile, "Declination");
     302           0 :                 declinationTag.Attribute("angle", declination);
     303             :             }
     304             :         }
     305             : 
     306             :         {
     307           0 :             std::string settings = sim.GetMapSettingsString();
     308           0 :             if (!settings.empty())
     309             :             {
     310           0 :                 XMLWriter_Element scriptSettingsTag(xmlMapFile, "ScriptSettings");
     311           0 :                 scriptSettingsTag.Text(("\n" + settings + "\n").c_str(), true);
     312             :             }
     313             :         }
     314             : 
     315             :         {
     316           0 :             XMLWriter_Element entitiesTag(xmlMapFile, "Entities");
     317           0 :             CmpPtr<ICmpTemplateManager> cmpTemplateManager(sim, SYSTEM_ENTITY);
     318           0 :             ENSURE(cmpTemplateManager);
     319             : 
     320             :             // This will probably need to be changed in the future, but for now we'll
     321             :             // just save all entities that have a position
     322           0 :             CSimulation2::InterfaceList ents = sim.GetEntitiesWithInterface(IID_Position);
     323           0 :             for (CSimulation2::InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it)
     324             :             {
     325           0 :                 entity_id_t ent = it->first;
     326             : 
     327             :                 // Don't save local entities (placement previews etc)
     328           0 :                 if (ENTITY_IS_LOCAL(ent))
     329           0 :                     continue;
     330             : 
     331           0 :                 XMLWriter_Element entityTag(xmlMapFile, "Entity");
     332           0 :                 entityTag.Attribute("uid", ent);
     333             : 
     334           0 :                 entityTag.Setting("Template", cmpTemplateManager->GetCurrentTemplateName(ent));
     335             : 
     336           0 :                 CmpPtr<ICmpOwnership> cmpOwnership(sim, ent);
     337           0 :                 if (cmpOwnership)
     338           0 :                     entityTag.Setting("Player", static_cast<int>(cmpOwnership->GetOwner()));
     339             : 
     340           0 :                 CmpPtr<ICmpGarrisonHolder> cmpGarrisonHolder(sim, ent);
     341           0 :                 if (cmpGarrisonHolder)
     342             :                 {
     343           0 :                     std::vector<entity_id_t> garrison = cmpGarrisonHolder->GetEntities();
     344           0 :                     if (!garrison.empty())
     345             :                     {
     346           0 :                         XMLWriter_Element garrisonTag(xmlMapFile, "Garrison");
     347           0 :                         for (const entity_id_t garr_ent_id : garrison)
     348             :                         {
     349           0 :                             XMLWriter_Element garrisonedEntityTag(xmlMapFile, "GarrisonedEntity");
     350           0 :                             garrisonedEntityTag.Attribute("uid", static_cast<int>(garr_ent_id));
     351             :                         }
     352             :                     }
     353             :                 }
     354             : 
     355           0 :                 CmpPtr<ICmpTurretHolder> cmpTurretHolder(sim, ent);
     356           0 :                 if (cmpTurretHolder)
     357             :                 {
     358           0 :                     std::vector<std::pair<std::string, entity_id_t> > turrets = cmpTurretHolder->GetTurrets();
     359           0 :                     if (!turrets.empty())
     360             :                     {
     361           0 :                         XMLWriter_Element turretTag(xmlMapFile, "Turrets");
     362           0 :                         for (const std::pair<std::string, entity_id_t>& turret : turrets)
     363             :                         {
     364           0 :                             XMLWriter_Element turretedEntityTag(xmlMapFile, "Turret");
     365           0 :                             turretedEntityTag.Attribute("turret", turret.first);
     366           0 :                             turretedEntityTag.Attribute("uid", static_cast<int>(turret.second));
     367             :                         }
     368             :                     }
     369             :                 }
     370             : 
     371           0 :                 CmpPtr<ICmpPosition> cmpPosition(sim, ent);
     372           0 :                 if (cmpPosition)
     373             :                 {
     374           0 :                     CFixedVector3D pos;
     375           0 :                     if (cmpPosition->IsInWorld())
     376           0 :                         pos = cmpPosition->GetPosition();
     377             : 
     378           0 :                     CFixedVector3D rot = cmpPosition->GetRotation();
     379             :                     {
     380           0 :                         XMLWriter_Element positionTag(xmlMapFile, "Position");
     381           0 :                         positionTag.Attribute("x", pos.X);
     382           0 :                         positionTag.Attribute("z", pos.Z);
     383             :                         // TODO: height offset etc
     384             :                     }
     385             :                     {
     386           0 :                         XMLWriter_Element orientationTag(xmlMapFile, "Orientation");
     387           0 :                         orientationTag.Attribute("y", rot.Y);
     388             :                         // TODO: X, Z maybe
     389             :                     }
     390             :                 }
     391             : 
     392           0 :                 CmpPtr<ICmpObstruction> cmpObstruction(sim, ent);
     393           0 :                 if (cmpObstruction)
     394             :                 {
     395             :                     // TODO: Currently only necessary because Atlas
     396             :                     // does not set up control groups for its walls.
     397           0 :                     cmpObstruction->ResolveFoundationCollisions();
     398             : 
     399           0 :                     entity_id_t group = cmpObstruction->GetControlGroup();
     400           0 :                     entity_id_t group2 = cmpObstruction->GetControlGroup2();
     401             : 
     402             :                     // Don't waste space writing the default control groups.
     403           0 :                     if (group != ent || group2 != INVALID_ENTITY)
     404             :                     {
     405           0 :                         XMLWriter_Element obstructionTag(xmlMapFile, "Obstruction");
     406           0 :                         if (group != ent)
     407           0 :                             obstructionTag.Attribute("group", group);
     408           0 :                         if (group2 != INVALID_ENTITY)
     409           0 :                             obstructionTag.Attribute("group2", group2);
     410             :                     }
     411             :                 }
     412             : 
     413           0 :                 CmpPtr<ICmpVisual> cmpVisual(sim, ent);
     414           0 :                 if (cmpVisual)
     415             :                 {
     416           0 :                     entity_id_t seed = static_cast<entity_id_t>(cmpVisual->GetActorSeed());
     417           0 :                     if (seed != ent)
     418             :                     {
     419           0 :                         XMLWriter_Element actorTag(xmlMapFile, "Actor");
     420           0 :                         actorTag.Attribute("seed",seed);
     421             :                     }
     422             :                     // TODO: variation/selection strings
     423             :                 }
     424             :             }
     425             :         }
     426             : 
     427             : 
     428           0 :         CmpPtr<ICmpCinemaManager> cmpCinemaManager(sim, SYSTEM_ENTITY);
     429           0 :         if (cmpCinemaManager)
     430             :         {
     431           0 :             const std::map<CStrW, CCinemaPath>& paths = cmpCinemaManager->GetPaths();
     432           0 :             std::map<CStrW, CCinemaPath>::const_iterator it = paths.begin();
     433           0 :             XMLWriter_Element pathsTag(xmlMapFile, "Paths");
     434             : 
     435           0 :             for ( ; it != paths.end(); ++it )
     436             :             {
     437           0 :                 fixed timescale = it->second.GetTimescale();
     438           0 :                 const std::vector<SplineData>& position_nodes = it->second.GetAllNodes();
     439           0 :                 const std::vector<SplineData>& target_nodes = it->second.GetTargetSpline().GetAllNodes();
     440           0 :                 const CCinemaData* data = it->second.GetData();
     441             : 
     442           0 :                 XMLWriter_Element pathTag(xmlMapFile, "Path");
     443           0 :                 pathTag.Attribute("name", data->m_Name);
     444           0 :                 pathTag.Attribute("timescale", timescale);
     445           0 :                 pathTag.Attribute("orientation", data->m_Orientation);
     446           0 :                 pathTag.Attribute("mode", data->m_Mode);
     447           0 :                 pathTag.Attribute("style", data->m_Style);
     448             : 
     449             :                 struct SEvent
     450             :                 {
     451             :                     fixed time;
     452             :                     const char* type;
     453             :                     CFixedVector3D value;
     454           0 :                     SEvent(fixed time, const char* type, CFixedVector3D value)
     455           0 :                         : time(time), type(type), value(value)
     456           0 :                     {}
     457           0 :                     bool operator<(const SEvent& another) const
     458             :                     {
     459           0 :                         return time < another.time;
     460             :                     }
     461             :                 };
     462             : 
     463             :                 // All events of a manipulating of camera (position/rotation/target)
     464           0 :                 std::vector<SEvent> events;
     465           0 :                 events.reserve(position_nodes.size() + target_nodes.size());
     466             : 
     467           0 :                 fixed last_position = fixed::Zero();
     468           0 :                 for (size_t i = 0; i < position_nodes.size(); ++i)
     469             :                 {
     470           0 :                     fixed distance = i > 0 ? position_nodes[i - 1].Distance : fixed::Zero();
     471           0 :                     last_position += distance;
     472           0 :                     events.emplace_back(last_position, "Position", position_nodes[i].Position);
     473             :                 }
     474             : 
     475           0 :                 fixed last_target = fixed::Zero();
     476           0 :                 for (size_t i = 0; i < target_nodes.size(); ++i)
     477             :                 {
     478           0 :                     fixed distance = i > 0 ? target_nodes[i - 1].Distance : fixed::Zero();
     479           0 :                     last_target += distance;
     480           0 :                     events.emplace_back(last_target, "Target", target_nodes[i].Position);
     481             :                 }
     482             : 
     483           0 :                 std::sort(events.begin(), events.end());
     484           0 :                 for (size_t i = 0; i < events.size();)
     485             :                 {
     486           0 :                     XMLWriter_Element nodeTag(xmlMapFile, "Node");
     487           0 :                     fixed deltatime = i > 0 ? (events[i].time - events[i - 1].time) : fixed::Zero();
     488           0 :                     nodeTag.Attribute("deltatime", deltatime);
     489           0 :                     size_t j = i;
     490           0 :                     for (; j < events.size() && events[j].time == events[i].time; ++j)
     491             :                     {
     492             :                         // Types: Position/Rotation/Target
     493           0 :                         XMLWriter_Element eventTypeTag(xmlMapFile, events[j].type);
     494           0 :                         eventTypeTag.Attribute("x", events[j].value.X);
     495           0 :                         eventTypeTag.Attribute("y", events[j].value.Y);
     496           0 :                         eventTypeTag.Attribute("z", events[j].value.Z);
     497             :                     }
     498           0 :                     i = j;
     499             :                 }
     500             :             }
     501             :         }
     502             :     }
     503           0 :     if (!xmlMapFile.StoreVFS(g_VFS, filename))
     504           0 :         LOGERROR("Failed to write map '%s'", filename.string8());
     505           3 : }

Generated by: LCOV version 1.13