LCOV - code coverage report
Current view: top level - source/tools/atlas/GameInterface/Handlers - MapHandlers.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 1 310 0.3 %
Date: 2023-01-19 00:18:29 Functions: 2 78 2.6 %

          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 "MessageHandler.h"
      21             : #include "../CommandProc.h"
      22             : #include "../GameLoop.h"
      23             : #include "../MessagePasser.h"
      24             : 
      25             : #include "graphics/GameView.h"
      26             : #include "graphics/LOSTexture.h"
      27             : #include "graphics/MapIO.h"
      28             : #include "graphics/MapWriter.h"
      29             : #include "graphics/MiniMapTexture.h"
      30             : #include "graphics/Patch.h"
      31             : #include "graphics/Terrain.h"
      32             : #include "graphics/TerrainTextureEntry.h"
      33             : #include "graphics/TerrainTextureManager.h"
      34             : #include "lib/bits.h"
      35             : #include "lib/file/vfs/vfs_path.h"
      36             : #include "lib/status.h"
      37             : #include "maths/MathUtil.h"
      38             : #include "ps/CLogger.h"
      39             : #include "ps/Filesystem.h"
      40             : #include "ps/Game.h"
      41             : #include "ps/GameSetup/GameSetup.h"
      42             : #include "ps/Loader.h"
      43             : #include "ps/World.h"
      44             : #include "renderer/Renderer.h"
      45             : #include "renderer/SceneRenderer.h"
      46             : #include "renderer/WaterManager.h"
      47             : #include "scriptinterface/Object.h"
      48             : #include "scriptinterface/JSON.h"
      49             : #include "simulation2/Simulation2.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/ICmpRangeManager.h"
      55             : #include "simulation2/components/ICmpTemplateManager.h"
      56             : #include "simulation2/components/ICmpTerrain.h"
      57             : #include "simulation2/components/ICmpVisual.h"
      58             : #include "simulation2/system/ParamNode.h"
      59             : 
      60             : 
      61             : #ifdef _MSC_VER
      62             : # pragma warning(disable: 4458) // Declaration hides class member.
      63             : #endif
      64             : 
      65             : namespace
      66             : {
      67           0 :     void InitGame()
      68             :     {
      69           0 :         if (g_Game)
      70             :         {
      71           0 :             delete g_Game;
      72           0 :             g_Game = NULL;
      73             :         }
      74             : 
      75           0 :         g_Game = new CGame(false);
      76             : 
      77             :         // Default to player 1 for playtesting
      78           0 :         g_Game->SetPlayerID(1);
      79           0 :     }
      80             : 
      81           0 :     void StartGame(JS::MutableHandleValue attrs)
      82             :     {
      83           0 :         g_Game->StartGame(attrs, "");
      84             : 
      85             :         // TODO: Non progressive load can fail - need a decent way to handle this
      86           0 :         LDR_NonprogressiveLoad();
      87             : 
      88             :         // Disable fog-of-war - this must be done before starting the game,
      89             :         // as visual actors cache their visibility state on first render.
      90           0 :         CmpPtr<ICmpRangeManager> cmpRangeManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
      91           0 :         if (cmpRangeManager)
      92           0 :             cmpRangeManager->SetLosRevealAll(-1, true);
      93             : 
      94           0 :         PSRETURN ret = g_Game->ReallyStartGame();
      95           0 :         ENSURE(ret == PSRETURN_OK);
      96           0 :     }
      97             : }
      98             : 
      99             : namespace AtlasMessage {
     100             : 
     101           0 : QUERYHANDLER(GenerateMap)
     102             : {
     103             :     try
     104             :     {
     105           0 :         InitGame();
     106             : 
     107             :         // Random map
     108           0 :         const ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface();
     109           0 :         ScriptRequest rq(scriptInterface);
     110             : 
     111           0 :         JS::RootedValue settings(rq.cx);
     112           0 :         Script::ParseJSON(rq, *msg->settings, &settings);
     113           0 :         Script::SetProperty(rq, settings, "mapType", "random");
     114             : 
     115           0 :         JS::RootedValue attrs(rq.cx);
     116           0 :         Script::CreateObject(
     117             :             rq,
     118             :             &attrs,
     119             :             "mapType", "random",
     120           0 :             "script", *msg->filename,
     121             :             "settings", settings);
     122             : 
     123           0 :         StartGame(&attrs);
     124             : 
     125           0 :         msg->status = 0;
     126             :     }
     127           0 :     catch (PSERROR_Game_World_MapLoadFailed&)
     128             :     {
     129             :         // Cancel loading
     130           0 :         LDR_Cancel();
     131             : 
     132             :         // Since map generation failed and we don't know why, use the blank map as a fallback
     133             : 
     134           0 :         InitGame();
     135             : 
     136           0 :         const ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface();
     137           0 :         ScriptRequest rq(scriptInterface);
     138             : 
     139             :         // Set up 8-element array of empty objects to satisfy init
     140           0 :         JS::RootedValue playerData(rq.cx);
     141           0 :         Script::CreateArray(rq, &playerData);
     142             : 
     143           0 :         for (int i = 0; i < 8; ++i)
     144             :         {
     145           0 :             JS::RootedValue player(rq.cx);
     146           0 :             Script::CreateObject(rq, &player);
     147           0 :             Script::SetPropertyInt(rq, playerData, i, player);
     148             :         }
     149             : 
     150           0 :         JS::RootedValue settings(rq.cx);
     151           0 :         Script::CreateObject(
     152             :             rq,
     153             :             &settings,
     154             :             "mapType", "scenario",
     155             :             "PlayerData", playerData);
     156             : 
     157           0 :         JS::RootedValue attrs(rq.cx);
     158           0 :         Script::CreateObject(
     159             :             rq,
     160             :             &attrs,
     161             :             "mapType", "scenario",
     162             :             "map", "maps/scenarios/_default",
     163             :             "settings", settings);
     164             : 
     165           0 :         StartGame(&attrs);
     166             : 
     167           0 :         msg->status = -1;
     168             :     }
     169           0 : }
     170             : 
     171           0 : MESSAGEHANDLER(LoadMap)
     172             : {
     173           0 :     InitGame();
     174             : 
     175           0 :     const ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface();
     176           0 :     ScriptRequest rq(scriptInterface);
     177             : 
     178             :     // Scenario
     179           0 :     CStrW map = *msg->filename;
     180           0 :     CStrW mapBase = map.BeforeLast(L".pmp"); // strip the file extension, if any
     181             : 
     182           0 :     JS::RootedValue attrs(rq.cx);
     183             : 
     184           0 :     Script::CreateObject(
     185             :         rq,
     186             :         &attrs,
     187             :         "mapType", "scenario",
     188             :         "map", mapBase);
     189             : 
     190           0 :     StartGame(&attrs);
     191           0 : }
     192             : 
     193           0 : MESSAGEHANDLER(ImportHeightmap)
     194             : {
     195           0 :     std::vector<u16> heightmap_source;
     196           0 :     if (LoadHeightmapImageOs(*msg->filename, heightmap_source) != INFO::OK)
     197             :     {
     198           0 :         LOGERROR("Failed to decode heightmap.");
     199           0 :         return;
     200             :     }
     201             : 
     202             :     // resize terrain to heightmap size
     203             :     // Notice that the number of tiles/pixels per side of the heightmap image is
     204             :     // one less than the number of vertices per side of the heightmap.
     205           0 :     CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
     206           0 :     const ssize_t newSize = (sqrt(heightmap_source.size()) - 1) / PATCH_SIZE;
     207           0 :     const ssize_t offset = (newSize - terrain->GetPatchesPerSide()) / 2;
     208           0 :     terrain->ResizeAndOffset(newSize, offset, offset);
     209             : 
     210             :     // copy heightmap data into map
     211           0 :     u16* heightmap = g_Game->GetWorld()->GetTerrain()->GetHeightMap();
     212           0 :     ENSURE(heightmap_source.size() == (std::size_t) SQR(g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide()));
     213           0 :     std::copy(heightmap_source.begin(), heightmap_source.end(), heightmap);
     214             : 
     215             :     // update simulation
     216           0 :     CmpPtr<ICmpTerrain> cmpTerrain(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
     217           0 :     if (cmpTerrain)
     218           0 :         cmpTerrain->ReloadTerrain();
     219             : 
     220           0 :     g_Game->GetView()->GetLOSTexture().MakeDirty();
     221             : }
     222             : 
     223           0 : MESSAGEHANDLER(SaveMap)
     224             : {
     225           0 :     CMapWriter writer;
     226           0 :     VfsPath pathname = VfsPath(*msg->filename).ChangeExtension(L".pmp");
     227           0 :     writer.SaveMap(pathname,
     228             :         g_Game->GetWorld()->GetTerrain(),
     229           0 :         &g_Renderer.GetSceneRenderer().GetWaterManager(), &g_Renderer.GetSceneRenderer().GetSkyManager(),
     230             :         &g_LightEnv, g_Game->GetView()->GetCamera(), g_Game->GetView()->GetCinema(),
     231           0 :         &g_Renderer.GetPostprocManager(),
     232             :         g_Game->GetSimulation2());
     233           0 : }
     234             : 
     235           0 : QUERYHANDLER(GetMapSettings)
     236             : {
     237           0 :     msg->settings = g_Game->GetSimulation2()->GetMapSettingsString();
     238           0 : }
     239             : 
     240           0 : BEGIN_COMMAND(SetMapSettings)
     241             : {
     242             :     std::string m_OldSettings, m_NewSettings;
     243             : 
     244           0 :     void SetSettings(const std::string& settings)
     245             :     {
     246           0 :         g_Game->GetSimulation2()->SetMapSettings(settings);
     247           0 :     }
     248             : 
     249           0 :     void Do()
     250             :     {
     251           0 :         m_OldSettings = g_Game->GetSimulation2()->GetMapSettingsString();
     252           0 :         m_NewSettings = *msg->settings;
     253             : 
     254           0 :         SetSettings(m_NewSettings);
     255           0 :     }
     256             : 
     257             :     // TODO: we need some way to notify the Atlas UI when the settings are changed
     258             :     //  externally, otherwise this will have no visible effect
     259           0 :     void Undo()
     260             :     {
     261             :         // SetSettings(m_OldSettings);
     262           0 :     }
     263             : 
     264           0 :     void Redo()
     265             :     {
     266             :         // SetSettings(m_NewSettings);
     267           0 :     }
     268             : 
     269           0 :     void MergeIntoPrevious(cSetMapSettings* prev)
     270             :     {
     271           0 :         prev->m_NewSettings = m_NewSettings;
     272           0 :     }
     273             : };
     274           0 : END_COMMAND(SetMapSettings)
     275             : 
     276           0 : MESSAGEHANDLER(LoadPlayerSettings)
     277             : {
     278           0 :     g_Game->GetSimulation2()->LoadPlayerSettings(msg->newplayers);
     279           0 : }
     280             : 
     281           0 : QUERYHANDLER(GetMapSizes)
     282             : {
     283           0 :     msg->sizes = g_Game->GetSimulation2()->GetMapSizes();
     284           0 : }
     285             : 
     286           0 : QUERYHANDLER(RasterizeMinimap)
     287             : {
     288             :     // TODO: remove the code duplication of the rasterization algorithm, using
     289             :     // CMinimap version.
     290           0 :     const CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
     291           0 :     const ssize_t dimension = terrain->GetVerticesPerSide() - 1;
     292           0 :     const ssize_t bpp = 24;
     293           0 :     const ssize_t imageDataSize = dimension * dimension * (bpp / 8);
     294             : 
     295           0 :     std::vector<u8> imageBytes(imageDataSize);
     296             : 
     297           0 :     float shallowPassageHeight = CMiniMapTexture::GetShallowPassageHeight();
     298             : 
     299           0 :     ssize_t w = dimension;
     300           0 :     ssize_t h = dimension;
     301           0 :     const float waterHeight = g_Renderer.GetSceneRenderer().GetWaterManager().m_WaterHeight;
     302             : 
     303           0 :     for (ssize_t j = 0; j < h; ++j)
     304             :     {
     305             :         // Work backwards to vertically flip the image.
     306           0 :         ssize_t position = 3 * (h - j - 1) * dimension;
     307           0 :         for (ssize_t i = 0; i < w; ++i)
     308             :         {
     309           0 :             float avgHeight = (terrain->GetVertexGroundLevel(i, j)
     310           0 :                 + terrain->GetVertexGroundLevel(i + 1, j)
     311           0 :                 + terrain->GetVertexGroundLevel(i, j + 1)
     312           0 :                 + terrain->GetVertexGroundLevel(i + 1, j + 1)
     313           0 :                 ) / 4.0f;
     314             : 
     315           0 :             if (avgHeight < waterHeight && avgHeight > waterHeight - shallowPassageHeight)
     316             :             {
     317             :                 // shallow water
     318           0 :                 imageBytes[position++] = 0x70;
     319           0 :                 imageBytes[position++] = 0x98;
     320           0 :                 imageBytes[position++] = 0xc0;
     321             :             }
     322           0 :             else if (avgHeight < waterHeight)
     323             :             {
     324             :                 // Set water as constant color for consistency on different maps
     325           0 :                 imageBytes[position++] = 0x50;
     326           0 :                 imageBytes[position++] = 0x78;
     327           0 :                 imageBytes[position++] = 0xa0;
     328             :             }
     329             :             else
     330             :             {
     331           0 :                 u32 color = std::numeric_limits<u32>::max();
     332           0 :                 u32 hmap = static_cast<u32>(terrain->GetHeightMap()[j * dimension + i]) >> 8;
     333           0 :                 float scale = hmap / 3.0f + 170.0f / 255.0f;
     334             : 
     335           0 :                 CMiniPatch* mp = terrain->GetTile(i, j);
     336           0 :                 if (mp)
     337             :                 {
     338           0 :                     CTerrainTextureEntry* tex = mp->GetTextureEntry();
     339           0 :                     if (tex)
     340           0 :                         color = tex->GetBaseColor();
     341             :                 }
     342             : 
     343             :                 // Convert
     344           0 :                 imageBytes[position++] = static_cast<u8>(static_cast<float>(color & 0xff) * scale);
     345           0 :                 imageBytes[position++] = static_cast<u8>(static_cast<float>((color >> 8) & 0xff) * scale);
     346           0 :                 imageBytes[position++] = static_cast<u8>(static_cast<float>((color >> 16) & 0xff) * scale);
     347             :             }
     348             :         }
     349             :     }
     350             : 
     351           0 :     msg->imageBytes = std::move(imageBytes);
     352           0 :     msg->dimension = dimension;
     353           0 : }
     354             : 
     355           0 : QUERYHANDLER(GetRMSData)
     356             : {
     357           0 :     msg->data = g_Game->GetSimulation2()->GetRMSData();
     358           0 : }
     359             : 
     360           0 : QUERYHANDLER(GetCurrentMapSize)
     361             : {
     362           0 :     msg->size = g_Game->GetWorld()->GetTerrain()->GetTilesPerSide();
     363           0 : }
     364             : 
     365           0 : BEGIN_COMMAND(ResizeMap)
     366             : {
     367           0 :     bool Within(const CFixedVector3D& pos, const int centerX, const int centerZ, const int radius)
     368             :     {
     369           0 :         int dx = abs(pos.X.ToInt_RoundToZero() - centerX);
     370           0 :         if (dx > radius)
     371           0 :             return false;
     372           0 :         int dz = abs(pos.Z.ToInt_RoundToZero() - centerZ);
     373           0 :         if (dz > radius)
     374           0 :             return false;
     375           0 :         if (dx + dz <= radius)
     376           0 :             return true;
     377           0 :         return dx * dx + dz * dz <= radius * radius;
     378             :     }
     379             : 
     380           0 :     struct DeletedObject
     381             :     {
     382             :         entity_id_t entityId;
     383             :         CStr templateName;
     384             :         player_id_t owner;
     385             :         CFixedVector3D pos;
     386             :         CFixedVector3D rot;
     387             :         u32 actorSeed;
     388             :     };
     389             : 
     390             :     ssize_t m_OldPatches, m_NewPatches;
     391             :     int m_OffsetX, m_OffsetY;
     392             : 
     393             :     u16* m_Heightmap;
     394             :     CPatch* m_Patches;
     395             : 
     396             :     std::vector<DeletedObject> m_DeletedObjects;
     397             :     std::vector<std::pair<entity_id_t, CFixedVector3D>> m_OldPositions;
     398             :     std::vector<std::pair<entity_id_t, CFixedVector3D>> m_NewPositions;
     399             : 
     400           0 :     cResizeMap() : m_Heightmap(nullptr), m_Patches(nullptr)
     401             :     {
     402           0 :     }
     403             : 
     404           0 :     ~cResizeMap()
     405           0 :     {
     406           0 :         delete[] m_Heightmap;
     407           0 :         delete[] m_Patches;
     408           0 :     }
     409             : 
     410           0 :     void MakeDirty()
     411             :     {
     412           0 :         CmpPtr<ICmpTerrain> cmpTerrain(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
     413           0 :         if (cmpTerrain)
     414           0 :             cmpTerrain->ReloadTerrain();
     415             : 
     416             :         // The LOS texture won't normally get updated when running Atlas
     417             :         // (since there's no simulation updates), so explicitly dirty it
     418           0 :         g_Game->GetView()->GetLOSTexture().MakeDirty();
     419           0 :     }
     420             : 
     421           0 :     void ResizeTerrain(ssize_t patches, int offsetX, int offsetY)
     422             :     {
     423           0 :         CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
     424           0 :         terrain->ResizeAndOffset(patches, -offsetX, -offsetY);
     425           0 :     }
     426             : 
     427           0 :     void DeleteObjects(const std::vector<DeletedObject>& deletedObjects)
     428             :     {
     429           0 :         for (const DeletedObject& deleted : deletedObjects)
     430           0 :             g_Game->GetSimulation2()->DestroyEntity(deleted.entityId);
     431             : 
     432           0 :         g_Game->GetSimulation2()->FlushDestroyedEntities();
     433           0 :     }
     434             : 
     435           0 :     void RestoreObjects(const std::vector<DeletedObject>& deletedObjects)
     436             :     {
     437           0 :         CSimulation2& sim = *g_Game->GetSimulation2();
     438             : 
     439           0 :         for (const DeletedObject& deleted : deletedObjects)
     440             :         {
     441           0 :             entity_id_t ent = sim.AddEntity(deleted.templateName.FromUTF8(), deleted.entityId);
     442           0 :             if (ent == INVALID_ENTITY)
     443             :             {
     444           0 :                 LOGERROR("Failed to load entity template '%s'", deleted.templateName.c_str());
     445             :             }
     446             :             else
     447             :             {
     448           0 :                 CmpPtr<ICmpPosition> cmpPosition(sim, deleted.entityId);
     449           0 :                 if (cmpPosition)
     450             :                 {
     451           0 :                     cmpPosition->JumpTo(deleted.pos.X, deleted.pos.Z);
     452           0 :                     cmpPosition->SetXZRotation(deleted.rot.X, deleted.rot.Z);
     453           0 :                     cmpPosition->SetYRotation(deleted.rot.Y);
     454             :                 }
     455             : 
     456           0 :                 CmpPtr<ICmpOwnership> cmpOwnership(sim, deleted.entityId);
     457           0 :                 if (cmpOwnership)
     458           0 :                     cmpOwnership->SetOwner(deleted.owner);
     459             : 
     460           0 :                 CmpPtr<ICmpVisual> cmpVisual(sim, deleted.entityId);
     461           0 :                 if (cmpVisual)
     462           0 :                     cmpVisual->SetActorSeed(deleted.actorSeed);
     463             :             }
     464             :         }
     465           0 :     }
     466             : 
     467           0 :     void SetMovedEntitiesPosition(const std::vector<std::pair<entity_id_t, CFixedVector3D>>& movedObjects)
     468             :     {
     469           0 :         for (const std::pair<entity_id_t, CFixedVector3D>& obj : movedObjects)
     470             :         {
     471           0 :             const entity_id_t id = obj.first;
     472           0 :             const CFixedVector3D position = obj.second;
     473           0 :             CmpPtr<ICmpPosition> cmpPosition(*g_Game->GetSimulation2(), id);
     474           0 :             ENSURE(cmpPosition);
     475           0 :             cmpPosition->JumpTo(position.X, position.Z);
     476             :         }
     477           0 :     }
     478             : 
     479           0 :     void Do()
     480             :     {
     481           0 :         CSimulation2& sim = *g_Game->GetSimulation2();
     482           0 :         CmpPtr<ICmpTemplateManager> cmpTemplateManager(sim, SYSTEM_ENTITY);
     483           0 :         ENSURE(cmpTemplateManager);
     484             : 
     485           0 :         CmpPtr<ICmpTerrain> cmpTerrain(sim, SYSTEM_ENTITY);
     486           0 :         if (!cmpTerrain)
     487             :         {
     488           0 :             m_OldPatches = m_NewPatches = 0;
     489           0 :             m_OffsetX = m_OffsetY = 0;
     490             :         }
     491             :         else
     492             :         {
     493           0 :             m_OldPatches = static_cast<ssize_t>(cmpTerrain->GetTilesPerSide() / PATCH_SIZE);
     494           0 :             m_NewPatches = msg->tiles / PATCH_SIZE;
     495           0 :             m_OffsetX = msg->offsetX / PATCH_SIZE;
     496             :             // Need to flip direction of vertical offset, due to screen mapping order.
     497           0 :             m_OffsetY = -(msg->offsetY / PATCH_SIZE);
     498             : 
     499           0 :             CTerrain* terrain = cmpTerrain->GetCTerrain();
     500           0 :             m_Heightmap = new u16[(m_OldPatches * PATCH_SIZE + 1) * (m_OldPatches * PATCH_SIZE + 1)];
     501           0 :             std::copy_n(terrain->GetHeightMap(), (m_OldPatches * PATCH_SIZE + 1) * (m_OldPatches * PATCH_SIZE + 1), m_Heightmap);
     502           0 :             m_Patches = new CPatch[m_OldPatches * m_OldPatches];
     503           0 :             for (ssize_t j = 0; j < m_OldPatches; ++j)
     504           0 :                 for (ssize_t i = 0; i < m_OldPatches; ++i)
     505             :                 {
     506           0 :                     CPatch& src = *(terrain->GetPatch(i, j));
     507           0 :                     CPatch& dst = m_Patches[j * m_OldPatches + i];
     508           0 :                     std::copy_n(&src.m_MiniPatches[0][0], PATCH_SIZE * PATCH_SIZE, &dst.m_MiniPatches[0][0]);
     509             :                 }
     510             :         }
     511             : 
     512           0 :         const int radiusInTerrainUnits = m_NewPatches * PATCH_SIZE * TERRAIN_TILE_SIZE / 2 * (1.f - 1e-6f);
     513             :         // Opposite direction offset, as we move the destination onto the source, not the source into the destination.
     514           0 :         const int mapCenterX = (m_OldPatches / 2 - m_OffsetX) * PATCH_SIZE * TERRAIN_TILE_SIZE;
     515           0 :         const int mapCenterZ = (m_OldPatches / 2 - m_OffsetY) * PATCH_SIZE * TERRAIN_TILE_SIZE;
     516             :         // The offset to move units by is opposite the direction the map is moved, and from the corner.
     517           0 :         const int offsetX = ((m_NewPatches - m_OldPatches) / 2 + m_OffsetX) * PATCH_SIZE * TERRAIN_TILE_SIZE;
     518           0 :         const int offsetZ = ((m_NewPatches - m_OldPatches) / 2 + m_OffsetY) * PATCH_SIZE * TERRAIN_TILE_SIZE;
     519           0 :         const CFixedVector3D offset = CFixedVector3D(fixed::FromInt(offsetX), fixed::FromInt(0), fixed::FromInt(offsetZ));
     520             : 
     521           0 :         const CSimulation2::InterfaceListUnordered& ents = sim.GetEntitiesWithInterfaceUnordered(IID_Selectable);
     522           0 :         for (const std::pair<const entity_id_t, IComponent*>& ent : ents)
     523             :         {
     524           0 :             const entity_id_t entityId = ent.first;
     525           0 :             CmpPtr<ICmpPosition> cmpPosition(sim, entityId);
     526             : 
     527           0 :             if (cmpPosition && cmpPosition->IsInWorld() && Within(cmpPosition->GetPosition(), mapCenterX, mapCenterZ, radiusInTerrainUnits))
     528             :             {
     529           0 :                 CFixedVector3D position = cmpPosition->GetPosition();
     530             : 
     531           0 :                 m_NewPositions.emplace_back(entityId, position + offset);
     532           0 :                 m_OldPositions.emplace_back(entityId, position);
     533             :             }
     534             :             else
     535             :             {
     536           0 :                 DeletedObject deleted;
     537           0 :                 deleted.entityId = entityId;
     538           0 :                 deleted.templateName = cmpTemplateManager->GetCurrentTemplateName(entityId);
     539             : 
     540             :                 // If the entity has a position, but the ending position is not valid;
     541           0 :                 if (cmpPosition)
     542             :                 {
     543           0 :                     deleted.pos = cmpPosition->GetPosition();
     544           0 :                     deleted.rot = cmpPosition->GetRotation();
     545             :                 }
     546             : 
     547           0 :                 CmpPtr<ICmpOwnership> cmpOwnership(sim, entityId);
     548           0 :                 if (cmpOwnership)
     549           0 :                     deleted.owner = cmpOwnership->GetOwner();
     550             : 
     551           0 :                 CmpPtr<ICmpVisual> cmpVisual(sim, deleted.entityId);
     552           0 :                 if (cmpVisual)
     553           0 :                     deleted.actorSeed = cmpVisual->GetActorSeed();
     554             : 
     555           0 :                 m_DeletedObjects.push_back(deleted);
     556             :             }
     557             :         }
     558             : 
     559           0 :         DeleteObjects(m_DeletedObjects);
     560           0 :         ResizeTerrain(m_NewPatches, m_OffsetX, m_OffsetY);
     561           0 :         SetMovedEntitiesPosition(m_NewPositions);
     562           0 :         MakeDirty();
     563           0 :     }
     564             : 
     565           0 :     void Undo()
     566             :     {
     567           0 :         if (m_Heightmap == nullptr || m_Patches == nullptr)
     568             :         {
     569             :             // If there previously was no data, just resize to old (probably not originally valid).
     570           0 :             ResizeTerrain(m_OldPatches, -m_OffsetX, -m_OffsetY);
     571             :         }
     572             :         else
     573             :         {
     574           0 :             CSimulation2& sim = *g_Game->GetSimulation2();
     575           0 :             CmpPtr<ICmpTerrain> cmpTerrain(sim, SYSTEM_ENTITY);
     576           0 :             CTerrain* terrain = cmpTerrain->GetCTerrain();
     577             : 
     578           0 :             terrain->Initialize(m_OldPatches, m_Heightmap);
     579             :             // Copy terrain data back.
     580           0 :             for (ssize_t j = 0; j < m_OldPatches; ++j)
     581           0 :                 for (ssize_t i = 0; i < m_OldPatches; ++i)
     582             :                 {
     583           0 :                     CPatch& src = m_Patches[j * m_OldPatches + i];
     584           0 :                     CPatch& dst = *(terrain->GetPatch(i, j));
     585           0 :                     std::copy_n(&src.m_MiniPatches[0][0], PATCH_SIZE * PATCH_SIZE, &dst.m_MiniPatches[0][0]);
     586             :                 }
     587             :         }
     588           0 :         RestoreObjects(m_DeletedObjects);
     589           0 :         SetMovedEntitiesPosition(m_OldPositions);
     590           0 :         MakeDirty();
     591           0 :     }
     592             : 
     593           0 :     void Redo()
     594             :     {
     595           0 :         DeleteObjects(m_DeletedObjects);
     596           0 :         ResizeTerrain(m_NewPatches, m_OffsetX, m_OffsetY);
     597           0 :         SetMovedEntitiesPosition(m_NewPositions);
     598           0 :         MakeDirty();
     599           0 :     }
     600             : };
     601           0 : END_COMMAND(ResizeMap)
     602             : 
     603           0 : QUERYHANDLER(VFSFileExists)
     604             : {
     605           0 :     msg->exists = VfsFileExists(*msg->path);
     606           0 : }
     607             : 
     608           0 : QUERYHANDLER(VFSFileRealPath)
     609             : {
     610           0 :     VfsPath pathname(*msg->path);
     611           0 :     if (pathname.empty())
     612           0 :         return;
     613           0 :     OsPath realPathname;
     614           0 :     if (g_VFS->GetRealPath(pathname, realPathname) == INFO::OK)
     615           0 :         msg->realPath = realPathname.string();
     616             : }
     617             : 
     618           0 : static Status AddToFilenames(const VfsPath& pathname, const CFileInfo& UNUSED(fileInfo), const uintptr_t cbData)
     619             : {
     620           0 :     std::vector<std::wstring>& filenames = *(std::vector<std::wstring>*)cbData;
     621           0 :     filenames.push_back(pathname.string().c_str());
     622           0 :     return INFO::OK;
     623             : }
     624             : 
     625           0 : QUERYHANDLER(GetMapList)
     626             : {
     627             : #define GET_FILE_LIST(path, list) \
     628             :     std::vector<std::wstring> list; \
     629             :     vfs::ForEachFile(g_VFS, path, AddToFilenames, (uintptr_t)&list, L"*.xml", vfs::DIR_RECURSIVE); \
     630             :     msg->list = list;
     631             : 
     632           0 :     GET_FILE_LIST(L"maps/scenarios/", scenarioFilenames);
     633           0 :     GET_FILE_LIST(L"maps/skirmishes/", skirmishFilenames);
     634           0 :     GET_FILE_LIST(L"maps/tutorials/", tutorialFilenames);
     635             : #undef GET_FILE_LIST
     636           0 : }
     637             : 
     638           0 : QUERYHANDLER(GetVictoryConditionData)
     639             : {
     640           0 :     msg->data = g_Game->GetSimulation2()->GetVictoryConditiondData();
     641           0 : }
     642             : 
     643           3 : }

Generated by: LCOV version 1.13