LCOV - code coverage report
Current view: top level - source/tools/atlas/GameInterface/Handlers - TerrainHandlers.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 0 281 0.0 %
Date: 2023-01-19 00:18:29 Functions: 0 81 0.0 %

          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             : 
      22             : #include "../CommandProc.h"
      23             : 
      24             : #include "graphics/Patch.h"
      25             : #include "graphics/TerrainTextureManager.h"
      26             : #include "graphics/TerrainTextureEntry.h"
      27             : #include "graphics/Terrain.h"
      28             : #include "graphics/TextureManager.h"
      29             : #include "ps/Game.h"
      30             : #include "ps/World.h"
      31             : #include "lib/tex/tex.h"
      32             : #include "ps/Filesystem.h"
      33             : #include "simulation2/Simulation2.h"
      34             : #include "simulation2/components/ICmpPathfinder.h"
      35             : #include "simulation2/components/ICmpTerrain.h"
      36             : #include "simulation2/helpers/Grid.h"
      37             : 
      38             : #include "../Brushes.h"
      39             : #include "../DeltaArray.h"
      40             : #include "../View.h"
      41             : 
      42             : #include <queue>
      43             : 
      44             : namespace AtlasMessage
      45             : {
      46             : 
      47             : namespace
      48             : {
      49             : 
      50           0 : sTerrainTexturePreview MakeEmptyTerrainTexturePreview()
      51             : {
      52           0 :     sTerrainTexturePreview preview{};
      53           0 :     preview.name = std::wstring();
      54           0 :     preview.loaded = false;
      55           0 :     preview.imageHeight = 0;
      56           0 :     preview.imageWidth = 0;
      57           0 :     preview.imageData = {};
      58           0 :     return preview;
      59             : }
      60             : 
      61           0 : bool CompareTerrain(const sTerrainTexturePreview& a, const sTerrainTexturePreview& b)
      62             : {
      63           0 :     return (wcscmp(a.name.c_str(), b.name.c_str()) < 0);
      64             : }
      65             : 
      66           0 : sTerrainTexturePreview GetPreview(CTerrainTextureEntry* tex, size_t width, size_t height)
      67             : {
      68           0 :     sTerrainTexturePreview preview;
      69           0 :     preview.name = tex->GetTag().FromUTF8();
      70             : 
      71           0 :     const size_t previewBPP = 3;
      72           0 :     std::vector<u8> buffer(width * height * previewBPP);
      73             : 
      74             :     // It's not good to shrink the entire texture to fit the small preview
      75             :     // window, since it's the fine details in the texture that are
      76             :     // interesting; so just go down one mipmap level, then crop a chunk
      77             :     // out of the middle.
      78             : 
      79           0 :     std::shared_ptr<u8> fileData;
      80             :     size_t fileSize;
      81           0 :     Tex texture;
      82             :     const bool canUsePreview =
      83           0 :         !tex->GetDiffuseTexturePath().empty() &&
      84           0 :         g_VFS->LoadFile(tex->GetDiffuseTexturePath(), fileData, fileSize) == INFO::OK &&
      85           0 :         texture.decode(fileData, fileSize) == INFO::OK &&
      86             :         // Check that we can fit the texture into the preview size before any transform.
      87           0 :         texture.m_Width >= width && texture.m_Height >= height &&
      88             :         // Transform to a single format that we can process.
      89           0 :         texture.transform_to((texture.m_Flags | TEX_MIPMAPS) & ~(TEX_DXT | TEX_GREY | TEX_BGR)) == INFO::OK &&
      90           0 :         (texture.m_Bpp == 24 || texture.m_Bpp == 32);
      91           0 :     if (canUsePreview)
      92             :     {
      93           0 :         size_t level = 0;
      94           0 :         while ((texture.m_Width >> (level + 1)) >= width && (texture.m_Height >> (level + 1)) >= height && level < texture.GetMIPLevels().size())
      95           0 :             ++level;
      96             :         // Extract the middle section (as a representative preview),
      97             :         // and copy into buffer.
      98           0 :         u8* data = texture.GetMIPLevels()[level].data;
      99           0 :         ENSURE(data);
     100           0 :         const size_t levelWidth = texture.m_Width >> level;
     101           0 :         const size_t levelHeight = texture.m_Height >> level;
     102           0 :         const size_t dataShiftX = (levelWidth - width) / 2;
     103           0 :         const size_t dataShiftY = (levelHeight - height) / 2;
     104           0 :         for (size_t y = 0; y < height; ++y)
     105           0 :             for (size_t x = 0; x < width; ++x)
     106             :             {
     107           0 :                 const size_t bufferOffset = (y * width + x) * previewBPP;
     108           0 :                 const size_t dataOffset = ((y + dataShiftY) * levelWidth + x + dataShiftX) * texture.m_Bpp / 8;
     109           0 :                 buffer[bufferOffset + 0] = data[dataOffset + 0];
     110           0 :                 buffer[bufferOffset + 1] = data[dataOffset + 1];
     111           0 :                 buffer[bufferOffset + 2] = data[dataOffset + 2];
     112             :             }
     113           0 :         preview.loaded = true;
     114             :     }
     115             :     else
     116             :     {
     117             :         // Too small to preview. Just use a flat color instead.
     118           0 :         const u32 baseColor = tex->GetBaseColor();
     119           0 :         for (size_t i = 0; i < width * height; ++i)
     120             :         {
     121           0 :             buffer[i * previewBPP + 0] = (baseColor >> 16) & 0xff;
     122           0 :             buffer[i * previewBPP + 1] = (baseColor >> 8) & 0xff;
     123           0 :             buffer[i * previewBPP + 2] = (baseColor >> 0) & 0xff;
     124             :         }
     125           0 :         preview.loaded = tex->GetTexture()->IsLoaded();
     126             :     }
     127             : 
     128           0 :     preview.imageWidth = width;
     129           0 :     preview.imageHeight = height;
     130           0 :     preview.imageData = buffer;
     131             : 
     132           0 :     return preview;
     133             : }
     134             : 
     135             : } // anonymous namespace
     136             : 
     137           0 : QUERYHANDLER(GetTerrainGroups)
     138             : {
     139           0 :     const CTerrainTextureManager::TerrainGroupMap &groups = g_TexMan.GetGroups();
     140           0 :     std::vector<std::wstring> groupNames;
     141           0 :     for (CTerrainTextureManager::TerrainGroupMap::const_iterator it = groups.begin(); it != groups.end(); ++it)
     142           0 :         groupNames.push_back(it->first.FromUTF8());
     143           0 :     msg->groupNames = groupNames;
     144           0 : }
     145             : 
     146           0 : QUERYHANDLER(GetTerrainGroupTextures)
     147             : {
     148           0 :     std::vector<std::wstring> names;
     149             : 
     150           0 :     CTerrainGroup* group = g_TexMan.FindGroup(CStrW(*msg->groupName).ToUTF8());
     151           0 :     if (group)
     152             :     {
     153           0 :         for (std::vector<CTerrainTextureEntry*>::const_iterator it = group->GetTerrains().begin(); it != group->GetTerrains().end(); ++it)
     154           0 :             names.emplace_back((*it)->GetTag().FromUTF8());
     155             :     }
     156           0 :     std::sort(names.begin(), names.end());
     157           0 :     msg->names = names;
     158           0 : }
     159             : 
     160           0 : QUERYHANDLER(GetTerrainGroupPreviews)
     161             : {
     162           0 :     std::vector<sTerrainTexturePreview> previews;
     163             : 
     164           0 :     CTerrainGroup* group = g_TexMan.FindGroup(CStrW(*msg->groupName).ToUTF8());
     165           0 :     for (std::vector<CTerrainTextureEntry*>::const_iterator it = group->GetTerrains().begin(); it != group->GetTerrains().end(); ++it)
     166             :     {
     167           0 :         previews.push_back(GetPreview(*it, msg->imageWidth, msg->imageHeight));
     168             :     }
     169             : 
     170             :     // Sort the list alphabetically by name
     171           0 :     std::sort(previews.begin(), previews.end(), CompareTerrain);
     172           0 :     msg->previews = previews;
     173           0 : }
     174             : 
     175           0 : QUERYHANDLER(GetTerrainPassabilityClasses)
     176             : {
     177           0 :     CmpPtr<ICmpPathfinder> cmpPathfinder(*AtlasView::GetView_Game()->GetSimulation2(), SYSTEM_ENTITY);
     178           0 :     if (cmpPathfinder)
     179             :     {
     180           0 :         std::map<std::string, pass_class_t> nonPathfindingClasses, pathfindingClasses;
     181           0 :         cmpPathfinder->GetPassabilityClasses(nonPathfindingClasses, pathfindingClasses);
     182             : 
     183           0 :         std::vector<std::wstring> classNames;
     184           0 :         for (std::map<std::string, pass_class_t>::iterator it = nonPathfindingClasses.begin(); it != nonPathfindingClasses.end(); ++it)
     185           0 :             classNames.push_back(CStr(it->first).FromUTF8());
     186           0 :         msg->classNames = classNames;
     187             :     }
     188           0 : }
     189             : 
     190           0 : QUERYHANDLER(GetTerrainTexture)
     191             : {
     192             :     ssize_t x, y;
     193           0 :     g_CurrentBrush.m_Centre = msg->pos->GetWorldSpace();
     194           0 :     g_CurrentBrush.GetCentre(x, y);
     195             : 
     196           0 :     CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
     197           0 :     CMiniPatch* tile = terrain->GetTile(x, y);
     198           0 :     if (tile)
     199             :     {
     200           0 :         CTerrainTextureEntry* tex = tile->GetTextureEntry();
     201           0 :         msg->texture = tex->GetTag().FromUTF8();
     202             :     }
     203             :     else
     204             :     {
     205           0 :         msg->texture = std::wstring();
     206             :     }
     207           0 : }
     208             : 
     209           0 : QUERYHANDLER(GetTerrainTexturePreview)
     210             : {
     211           0 :     CTerrainTextureEntry* tex = g_TexMan.FindTexture(CStrW(*msg->name).ToUTF8());
     212           0 :     if (tex)
     213           0 :         msg->preview = GetPreview(tex, msg->imageWidth, msg->imageHeight);
     214             :     else
     215           0 :         msg->preview = MakeEmptyTerrainTexturePreview();
     216           0 : }
     217             : 
     218             : //////////////////////////////////////////////////////////////////////////
     219             : 
     220             : namespace
     221             : {
     222             : 
     223             : struct TerrainTile
     224             : {
     225           0 :     TerrainTile(CTerrainTextureEntry* t, ssize_t p) : tex(t), priority(p) {}
     226             :     CTerrainTextureEntry* tex;
     227             :     ssize_t priority;
     228             : };
     229             : 
     230           0 : class TerrainArray : public DeltaArray2D<TerrainTile>
     231             : {
     232             : public:
     233           0 :     void Init()
     234             :     {
     235           0 :         m_Terrain = g_Game->GetWorld()->GetTerrain();
     236           0 :         m_VertsPerSide = g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide();
     237           0 :     }
     238             : 
     239           0 :     void UpdatePriority(ssize_t x, ssize_t y, CTerrainTextureEntry* tex, ssize_t priorityScale, ssize_t& priority)
     240             :     {
     241           0 :         CMiniPatch* tile = m_Terrain->GetTile(x, y);
     242           0 :         if (!tile)
     243           0 :             return; // tile was out-of-bounds
     244             : 
     245             :         // If this tile matches the current texture, we just want to match its
     246             :         // priority; otherwise we want to exceed its priority
     247           0 :         if (tile->GetTextureEntry() == tex)
     248           0 :             priority = std::max(priority, tile->GetPriority()*priorityScale);
     249             :         else
     250           0 :             priority = std::max(priority, tile->GetPriority()*priorityScale + 1);
     251             :     }
     252             : 
     253           0 :     CTerrainTextureEntry* GetTexEntry(ssize_t x, ssize_t y)
     254             :     {
     255           0 :         if (size_t(x) >= size_t(m_VertsPerSide-1) || size_t(y) >= size_t(m_VertsPerSide-1))
     256           0 :             return NULL;
     257             : 
     258           0 :         return get(x, y).tex;
     259             :     }
     260             : 
     261           0 :     ssize_t GetPriority(ssize_t x, ssize_t y)
     262             :     {
     263           0 :         if (size_t(x) >= size_t(m_VertsPerSide-1) || size_t(y) >= size_t(m_VertsPerSide-1))
     264           0 :             return 0;
     265             : 
     266           0 :         return get(x, y).priority;
     267             :     }
     268             : 
     269           0 :     void PaintTile(ssize_t x, ssize_t y, CTerrainTextureEntry* tex, ssize_t priority)
     270             :     {
     271             :         // Ignore out-of-bounds tiles
     272           0 :         if (size_t(x) >= size_t(m_VertsPerSide-1) || size_t(y) >= size_t(m_VertsPerSide-1))
     273           0 :             return;
     274             : 
     275           0 :         set(x,y, TerrainTile(tex, priority));
     276             :     }
     277             : 
     278           0 :     ssize_t GetTilesPerSide()
     279             :     {
     280           0 :         return m_VertsPerSide-1;
     281             :     }
     282             : 
     283             : protected:
     284           0 :     TerrainTile getOld(ssize_t x, ssize_t y)
     285             :     {
     286           0 :         CMiniPatch* mp = m_Terrain->GetTile(x, y);
     287           0 :         ENSURE(mp);
     288           0 :         return TerrainTile(mp->Tex, mp->Priority);
     289             :     }
     290           0 :     void setNew(ssize_t x, ssize_t y, const TerrainTile& val)
     291             :     {
     292           0 :         CMiniPatch* mp = m_Terrain->GetTile(x, y);
     293           0 :         ENSURE(mp);
     294           0 :         mp->Tex = val.tex;
     295           0 :         mp->Priority = val.priority;
     296           0 :     }
     297             : 
     298             :     CTerrain* m_Terrain;
     299             :     ssize_t m_VertsPerSide;
     300             : };
     301             : 
     302             : }
     303             : 
     304           0 : BEGIN_COMMAND(PaintTerrain)
     305             : {
     306             :     TerrainArray m_TerrainDelta;
     307             :     ssize_t m_i0, m_j0, m_i1, m_j1; // dirtied tiles (inclusive lower bound, exclusive upper)
     308             : 
     309           0 :     cPaintTerrain()
     310           0 :     {
     311           0 :         m_TerrainDelta.Init();
     312           0 :     }
     313             : 
     314           0 :     void MakeDirty()
     315             :     {
     316           0 :         g_Game->GetWorld()->GetTerrain()->MakeDirty(m_i0, m_j0, m_i1, m_j1, RENDERDATA_UPDATE_INDICES);
     317           0 :     }
     318             : 
     319           0 :     void Do()
     320             :     {
     321           0 :         g_CurrentBrush.m_Centre = msg->pos->GetWorldSpace();
     322             : 
     323             :         ssize_t x0, y0;
     324           0 :         g_CurrentBrush.GetBottomLeft(x0, y0);
     325             : 
     326           0 :         CTerrainTextureEntry* texentry = g_TexMan.FindTexture(CStrW(*msg->texture).ToUTF8());
     327           0 :         if (! texentry)
     328             :         {
     329           0 :             debug_warn(L"Can't find texentry"); // TODO: nicer error handling
     330           0 :             return;
     331             :         }
     332             : 
     333             :         // Priority system: If the new tile should have a high priority,
     334             :         // set it to one plus the maximum priority of all surrounding tiles
     335             :         // that aren't included in the brush (so that it's definitely the highest).
     336             :         // Similar for low priority.
     337           0 :         ssize_t priorityScale = (msg->priority == ePaintTerrainPriority::HIGH ? +1 : -1);
     338           0 :         ssize_t priority = 0;
     339             : 
     340           0 :         for (ssize_t dy = -1; dy < g_CurrentBrush.m_H+1; ++dy)
     341             :         {
     342           0 :             for (ssize_t dx = -1; dx < g_CurrentBrush.m_W+1; ++dx)
     343             :             {
     344           0 :                 if (!(g_CurrentBrush.Get(dx, dy) > 0.5f)) // ignore tiles that will be painted over
     345           0 :                     m_TerrainDelta.UpdatePriority(x0+dx, y0+dy, texentry, priorityScale, priority);
     346             :             }
     347             :         }
     348             : 
     349           0 :         for (ssize_t dy = 0; dy < g_CurrentBrush.m_H; ++dy)
     350             :         {
     351           0 :             for (ssize_t dx = 0; dx < g_CurrentBrush.m_W; ++dx)
     352             :             {
     353           0 :                 if (g_CurrentBrush.Get(dx, dy) > 0.5f) // TODO: proper solid brushes
     354           0 :                     m_TerrainDelta.PaintTile(x0+dx, y0+dy, texentry, priority*priorityScale);
     355             :             }
     356             :         }
     357             : 
     358           0 :         m_i0 = x0 - 1;
     359           0 :         m_j0 = y0 - 1;
     360           0 :         m_i1 = x0 + g_CurrentBrush.m_W + 1;
     361           0 :         m_j1 = y0 + g_CurrentBrush.m_H + 1;
     362           0 :         MakeDirty();
     363             :     }
     364             : 
     365           0 :     void Undo()
     366             :     {
     367           0 :         m_TerrainDelta.Undo();
     368           0 :         MakeDirty();
     369           0 :     }
     370             : 
     371           0 :     void Redo()
     372             :     {
     373           0 :         m_TerrainDelta.Redo();
     374           0 :         MakeDirty();
     375           0 :     }
     376             : 
     377           0 :     void MergeIntoPrevious(cPaintTerrain* prev)
     378             :     {
     379           0 :         prev->m_TerrainDelta.OverlayWith(m_TerrainDelta);
     380           0 :         prev->m_i0 = std::min(prev->m_i0, m_i0);
     381           0 :         prev->m_j0 = std::min(prev->m_j0, m_j0);
     382           0 :         prev->m_i1 = std::max(prev->m_i1, m_i1);
     383           0 :         prev->m_j1 = std::max(prev->m_j1, m_j1);
     384           0 :     }
     385             : };
     386           0 : END_COMMAND(PaintTerrain)
     387             : 
     388             : //////////////////////////////////////////////////////////////////////////
     389             : 
     390           0 : BEGIN_COMMAND(ReplaceTerrain)
     391             : {
     392             :     TerrainArray m_TerrainDelta;
     393             :     ssize_t m_i0, m_j0, m_i1, m_j1; // dirtied tiles (inclusive lower bound, exclusive upper)
     394             : 
     395           0 :     cReplaceTerrain()
     396           0 :     {
     397           0 :         m_TerrainDelta.Init();
     398           0 :     }
     399             : 
     400           0 :     void MakeDirty()
     401             :     {
     402           0 :         g_Game->GetWorld()->GetTerrain()->MakeDirty(m_i0, m_j0, m_i1, m_j1, RENDERDATA_UPDATE_INDICES);
     403           0 :     }
     404             : 
     405           0 :     void Do()
     406             :     {
     407           0 :         g_CurrentBrush.m_Centre = msg->pos->GetWorldSpace();
     408             : 
     409             :         ssize_t x0, y0;
     410           0 :         g_CurrentBrush.GetBottomLeft(x0, y0);
     411             : 
     412           0 :         m_i0 = m_i1 = x0;
     413           0 :         m_j0 = m_j1 = y0;
     414             : 
     415           0 :         CTerrainTextureEntry* texentry = g_TexMan.FindTexture(CStrW(*msg->texture).ToUTF8());
     416           0 :         if (! texentry)
     417             :         {
     418           0 :             debug_warn(L"Can't find texentry"); // TODO: nicer error handling
     419           0 :             return;
     420             :         }
     421             : 
     422           0 :         CTerrainTextureEntry* replacedTex = m_TerrainDelta.GetTexEntry(x0, y0);
     423             : 
     424             :         // Don't bother if we're not making a change
     425           0 :         if (texentry == replacedTex)
     426             :         {
     427           0 :             return;
     428             :         }
     429             : 
     430           0 :         ssize_t tiles = m_TerrainDelta.GetTilesPerSide();
     431             : 
     432           0 :         for (ssize_t j = 0; j < tiles; ++j)
     433             :         {
     434           0 :             for (ssize_t i = 0; i < tiles; ++i)
     435             :             {
     436           0 :                 if (m_TerrainDelta.GetTexEntry(i, j) == replacedTex)
     437             :                 {
     438           0 :                     m_i0 = std::min(m_i0, i-1);
     439           0 :                     m_j0 = std::min(m_j0, j-1);
     440           0 :                     m_i1 = std::max(m_i1, i+2);
     441           0 :                     m_j1 = std::max(m_j1, j+2);
     442           0 :                     m_TerrainDelta.PaintTile(i, j, texentry, m_TerrainDelta.GetPriority(i, j));
     443             :                 }
     444             :             }
     445             :         }
     446             : 
     447           0 :         MakeDirty();
     448             :     }
     449             : 
     450           0 :     void Undo()
     451             :     {
     452           0 :         m_TerrainDelta.Undo();
     453           0 :         MakeDirty();
     454           0 :     }
     455             : 
     456           0 :     void Redo()
     457             :     {
     458           0 :         m_TerrainDelta.Redo();
     459           0 :         MakeDirty();
     460           0 :     }
     461             : };
     462           0 : END_COMMAND(ReplaceTerrain)
     463             : 
     464             : //////////////////////////////////////////////////////////////////////////
     465             : 
     466           0 : BEGIN_COMMAND(FillTerrain)
     467             : {
     468             :     TerrainArray m_TerrainDelta;
     469             :     ssize_t m_i0, m_j0, m_i1, m_j1; // dirtied tiles (inclusive lower bound, exclusive upper)
     470             : 
     471           0 :     cFillTerrain()
     472           0 :     {
     473           0 :         m_TerrainDelta.Init();
     474           0 :     }
     475             : 
     476           0 :     void MakeDirty()
     477             :     {
     478           0 :         g_Game->GetWorld()->GetTerrain()->MakeDirty(m_i0, m_j0, m_i1, m_j1, RENDERDATA_UPDATE_INDICES);
     479           0 :     }
     480             : 
     481           0 :     void Do()
     482             :     {
     483           0 :         g_CurrentBrush.m_Centre = msg->pos->GetWorldSpace();
     484             : 
     485             :         ssize_t x0, y0;
     486           0 :         g_CurrentBrush.GetBottomLeft(x0, y0);
     487             : 
     488           0 :         m_i0 = m_i1 = x0;
     489           0 :         m_j0 = m_j1 = y0;
     490             : 
     491           0 :         CTerrainTextureEntry* texentry = g_TexMan.FindTexture(CStrW(*msg->texture).ToUTF8());
     492           0 :         if (! texentry)
     493             :         {
     494           0 :             debug_warn(L"Can't find texentry"); // TODO: nicer error handling
     495           0 :             return;
     496             :         }
     497             : 
     498           0 :         CTerrainTextureEntry* replacedTex = m_TerrainDelta.GetTexEntry(x0, y0);
     499             : 
     500             :         // Don't bother if we're not making a change
     501           0 :         if (texentry == replacedTex)
     502             :         {
     503           0 :             return;
     504             :         }
     505             : 
     506           0 :         ssize_t tiles = m_TerrainDelta.GetTilesPerSide();
     507             : 
     508             :         // Simple 4-way flood fill algorithm using queue and a grid to keep track of visited tiles,
     509             :         //  almost as fast as loop for filling whole map, much faster for small patches
     510           0 :         SparseGrid<bool> visited(tiles, tiles);
     511           0 :         std::queue<std::pair<u16, u16> > queue;
     512             : 
     513             :         // Initial tile
     514           0 :         queue.push(std::make_pair((u16)x0, (u16)y0));
     515           0 :         visited.set(x0, y0, true);
     516             : 
     517           0 :         while(!queue.empty())
     518             :         {
     519             :             // Check front of queue
     520           0 :             std::pair<u16, u16> t = queue.front();
     521           0 :             queue.pop();
     522           0 :             u16 i = t.first;
     523           0 :             u16 j = t.second;
     524             : 
     525           0 :             if (m_TerrainDelta.GetTexEntry(i, j) == replacedTex)
     526             :             {
     527             :                 // Found a tile to replace: adjust bounds and paint it
     528           0 :                 m_i0 = std::min(m_i0, (ssize_t)i-1);
     529           0 :                 m_j0 = std::min(m_j0, (ssize_t)j-1);
     530           0 :                 m_i1 = std::max(m_i1, (ssize_t)i+2);
     531           0 :                 m_j1 = std::max(m_j1, (ssize_t)j+2);
     532           0 :                 m_TerrainDelta.PaintTile(i, j, texentry, m_TerrainDelta.GetPriority(i, j));
     533             : 
     534             :                 // Visit 4 adjacent tiles (could visit 8 if we want to count diagonal adjacency)
     535           0 :                 if (i > 0 && !visited.get(i-1, j))
     536             :                 {
     537           0 :                     visited.set(i-1, j, true);
     538           0 :                     queue.push(std::make_pair(i-1, j));
     539             :                 }
     540           0 :                 if (i < (tiles-1) && !visited.get(i+1, j))
     541             :                 {
     542           0 :                     visited.set(i+1, j, true);
     543           0 :                     queue.push(std::make_pair(i+1, j));
     544             :                 }
     545           0 :                 if (j > 0 && !visited.get(i, j-1))
     546             :                 {
     547           0 :                     visited.set(i, j-1, true);
     548           0 :                     queue.push(std::make_pair(i, j-1));
     549             :                 }
     550           0 :                 if (j < (tiles-1) && !visited.get(i, j+1))
     551             :                 {
     552           0 :                     visited.set(i, j+1, true);
     553           0 :                     queue.push(std::make_pair(i, j+1));
     554             :                 }
     555             :             }
     556             :         }
     557             : 
     558           0 :         MakeDirty();
     559             :     }
     560             : 
     561           0 :     void Undo()
     562             :     {
     563           0 :         m_TerrainDelta.Undo();
     564           0 :         MakeDirty();
     565           0 :     }
     566             : 
     567           0 :     void Redo()
     568             :     {
     569           0 :         m_TerrainDelta.Redo();
     570           0 :         MakeDirty();
     571           0 :     }
     572             : };
     573           0 : END_COMMAND(FillTerrain)
     574             : 
     575             : } // namespace AtlasMessage

Generated by: LCOV version 1.13