diff --git a/Maps/MapData.cpp b/Maps/MapData.cpp index 9321bfff..3d4c1f54 100644 --- a/Maps/MapData.cpp +++ b/Maps/MapData.cpp @@ -1,5 +1,7 @@ #include "MapData.h" #include "CellType.h" +#include "../XFile.h" +#include unsigned int MapData::GetTileInfoIndex(unsigned int x, unsigned int y) const { @@ -35,3 +37,168 @@ size_t MapData::GetCellIndex(unsigned int x, unsigned int y) const unsigned int upperX = x >> 5; // ... 1110 0000 return (upperX * header.mapTileHeight + y) * 32 + lowerX; } + +void MapData::AddTileset(std::string filename, int tileCount) +{ + AddTilesetSource(filename, tileCount); + + for (int i = 0; i < tileCount; ++i) + { + tileInfos.push_back(TileInfo()); + } +} + +void MapData::AddTilesetSource(std::string filename, int tileCount) +{ + for (TilesetSource& tilesetSource : tilesetSources) + { + if (tilesetSource.IsSet()) { + continue; + } + + tilesetSource.Set(filename, tileCount); + return; + } + + throw std::runtime_error("Map already contains 512 tilesets."); +} + +void MapData::RemoveTileset(std::string filename) +{ + rsize_t tilesetIndex = GetTilesetIndex(filename); + + if (tilesetIndex == -1) { + return; //tilesetSource was not found. + } + + size_t firstTileInfoIndexRemoved = 0; + for (size_t i = 0; i < tilesetIndex; ++i) { + firstTileInfoIndexRemoved += tilesetSources[i].tileCount; + } + + TileRange range(firstTileInfoIndexRemoved, firstTileInfoIndexRemoved + tilesetSources[tilesetIndex].tileCount); + + RemoveTilesetSource(tilesetIndex); + RemoveTileInfos(tilesetIndex); + UpdateTilesAfterTilesetRemoval(range); + UpdateTileGroupsAfterTilesetRemoval(range); + UpdateTerrainTypesAfterTilesetRemoval(range, filename); +} + +// returns the index of the tilesetSource. +// If the tilesetSource is not located, -1 is returned. +size_t MapData::GetTilesetIndex(std::string filename) +{ + filename = XFile::ChangeFileExtension(filename, ""); + + for (size_t i = 0; i < tilesetSources.size(); ++i) + { + // return if all currently set tilesetSources are reviewed. + if (!tilesetSources[i].IsSet()) { + return -1; + } + + if (XFile::PathsAreEqual(filename, tilesetSources[i].tilesetFilename)) { + return i; + } + } + + // Return -1 if all tilesetSources are currently set but none contain the given filename. + return -1; +} + +void MapData::RemoveTilesetSource(size_t tilesetIndex) +{ + tilesetSources.erase(tilesetSources.begin() + tilesetIndex); + tilesetSources.push_back(TilesetSource()); // Add an empty tileset to the end of the vector. +} + +void MapData::RemoveTileInfos(size_t tilesetIndex) +{ + for (int i = tileInfos.size() - 1; i >= 0; --i) + { + if (tileInfos[i].tilesetIndex == tilesetIndex) + { + tileInfos.erase(tileInfos.begin() + i); + } + else if (tileInfos[i].tilesetIndex > tilesetIndex) + { + tileInfos[i].tilesetIndex--; + } + } +} + +void MapData::UpdateTilesAfterTilesetRemoval(const TileRange& tileRange) +{ + for (TileData& tileData : tiles) + { + // Tile was removed completely + if (tileRange.Includes(tileData.tileIndex)) + { + tileData.Reset(); + } + // Tile's index must be reduced by range of tileset removed. + else if (tileData.tileIndex > tileRange.end) + { + tileData.tileIndex -= tileRange.Range(); + } + } +} + +void MapData::UpdateTileGroupsAfterTilesetRemoval(const TileRange& tileRange) +{ + for (TileGroup& tileGroup : tileGroups) + { + for (size_t mappingIndex : tileGroup.mappingIndices) + { + // Tile was removed completely + if (tileRange.Includes(mappingIndex)) + { + mappingIndex = 0; + } + // Tile's index must be reduced by range of tileset removed. + else if (mappingIndex > tileRange.end) + { + mappingIndex -= tileRange.Range(); + } + } + } +} + +void MapData::UpdateTerrainTypesAfterTilesetRemoval(const TileRange& rangeRemoved, std::string tilesetName) +{ + for (TerrainType& terrainType : terrainTypes) + { + if (terrainType.tileRange.Includes(rangeRemoved)) + { + throw std::runtime_error("Unable to remove the tileset " + tilesetName + " because it belongs to a TerrainType."); + } + + if (terrainType.tileRange.start > rangeRemoved.start) + { + terrainType.bulldozedTileIndex -= rangeRemoved.Range(); + terrainType.lavaTileIndex -= rangeRemoved.Range(); + terrainType.rubbleTileIndex -= rangeRemoved.Range(); + terrainType.scorchedTileIndex -= rangeRemoved.Range(); + + for (int i = 0; i < 16; ++i) + { + terrainType.tube[i] -= rangeRemoved.Range(); + } + + for (int i = 0; i < 6; ++i) + { + terrainType.tubeTiles[i] -= rangeRemoved.Range(); + } + + for (int i = 0; i < 5; ++i) + { + for (int j = 0; j < 16; ++j) + { + terrainType.wall[i][j] -= rangeRemoved.Range(); + } + } + + } + } +} diff --git a/Maps/MapData.h b/Maps/MapData.h index 62b86a59..2cbcd8cc 100644 --- a/Maps/MapData.h +++ b/Maps/MapData.h @@ -46,6 +46,24 @@ struct MapData short GetTilesetIndex(unsigned int x, unsigned int y) const; short GetImageIndex(unsigned int x, unsigned int y) const; + // Fills tileset's associated TileInfos with default values. + void AddTileset(std::string filename, int tileCount); + + // If tileset is not found, function exits without throwing an error. + // Only removes the tileset if it does not belog to a TerrainType. + // After removingtileset: + // 1. Sets all tiles on the map that were associated with the tileset to index 0 (typically blue) + // 2. Sets all tiles on TileGroups that were associated with the tileset to index 0 + void RemoveTileset(std::string filename); + private: size_t GetCellIndex(unsigned int x, unsigned int y) const; + + void AddTilesetSource(std::string filename, int tileCount); + size_t GetTilesetIndex(std::string filename); + void RemoveTilesetSource(size_t tilesetIndex); + void RemoveTileInfos(size_t tilesetIndex); + void UpdateTilesAfterTilesetRemoval(const TileRange& tileRange); + void UpdateTileGroupsAfterTilesetRemoval(const TileRange& tileRange); + void UpdateTerrainTypesAfterTilesetRemoval(const TileRange& rangeRemoved, std::string tilesetName); }; diff --git a/Maps/MapReader.cpp b/Maps/MapReader.cpp index 8d8b86cd..483ccf80 100644 --- a/Maps/MapReader.cpp +++ b/Maps/MapReader.cpp @@ -79,7 +79,7 @@ void MapReader::ReadTilesetSources(MapData& mapData) } if (mapData.tilesetSources[i].tilesetFilename.size() > 0) { - streamReader->Read((char*)&mapData.tilesetSources[i].numTiles, sizeof(int)); + streamReader->Read((char*)&mapData.tilesetSources[i].tileCount, sizeof(int)); } } } diff --git a/Maps/TerrainType.h b/Maps/TerrainType.h index 9c1c6d8b..edd1fb9c 100644 --- a/Maps/TerrainType.h +++ b/Maps/TerrainType.h @@ -4,11 +4,37 @@ struct TileRange { + TileRange() {} + + TileRange(short start, short end) + { + this->start = start; + this->end = end; + } + // First tile index in range. short start; // Last tile index in range. short end; + + bool Includes(short index) const + { + return index > start && index <= end; + } + + // Returns true if any portion of the range intersects. + bool Includes(const TileRange& tileRange) const + { + return Includes(tileRange.start) || + Includes(tileRange.end) || + (tileRange.start < start && tileRange.end > end); + } + + short const Range() const + { + return end - start; + } }; // The properties associated with a range of tiles. @@ -55,7 +81,7 @@ struct TerrainType TileRange scorchedRange[3]; // UNKNOWN - short unkown[15]; + short unknown[15]; }; #pragma pack(pop) diff --git a/Maps/TileData.h b/Maps/TileData.h index 87b61b20..566f2f04 100644 --- a/Maps/TileData.h +++ b/Maps/TileData.h @@ -32,6 +32,18 @@ struct TileData // True if a wall or a building has been built on the tile. int bWallOrBuilding : 1; + + void Reset() + { + cellType = 0; + tileIndex = 0; + unitIndex = 0; + bLava = false; + bLavaPossible = false; + bExpansion = false; + bMicrobe = false; + bWallOrBuilding = false; + } }; #pragma pack(pop) diff --git a/Maps/TileSetSource.cpp b/Maps/TileSetSource.cpp new file mode 100644 index 00000000..b118ca91 --- /dev/null +++ b/Maps/TileSetSource.cpp @@ -0,0 +1,26 @@ +#include "TileSetSource.h" +#include "../XFile.h" +#include + +void TilesetSource::Set(std::string filename, int tileCount) +{ + if (tileCount < 0) { + std::runtime_error("A tileset's tileCount cannot be negative"); + } + + // filenames stored in TileSet do not include the file's extension. + filename = XFile::ChangeFileExtension(filename, ""); + + if (filename.size() < 1 || filename.size() > 8) { + std::runtime_error("A tileset's filename cannot be empty and cannot be greater than 8 characters, not including the file extension"); + } + + tilesetFilename = filename; + this->tileCount = tileCount; +} + +void TilesetSource::Remove() +{ + tilesetFilename.clear(); + tileCount = 0; +} \ No newline at end of file diff --git a/Maps/TileSetSource.h b/Maps/TileSetSource.h index b509580b..e982a72b 100644 --- a/Maps/TileSetSource.h +++ b/Maps/TileSetSource.h @@ -13,7 +13,15 @@ struct TilesetSource std::string tilesetFilename; // Number of Tiles in set (represented on BMP). - int numTiles; + int tileCount; + + void Set(std::string filename, int tileCount); + void Remove(); + + bool IsSet() + { + return tileCount > 0; + } }; #pragma pack(pop) diff --git a/OP2Utility.vcxproj b/OP2Utility.vcxproj index 1c3b7b92..05549763 100644 --- a/OP2Utility.vcxproj +++ b/OP2Utility.vcxproj @@ -142,6 +142,7 @@ + diff --git a/OP2Utility.vcxproj.filters b/OP2Utility.vcxproj.filters index 61bd0194..eaf0bdb0 100644 --- a/OP2Utility.vcxproj.filters +++ b/OP2Utility.vcxproj.filters @@ -149,6 +149,8 @@ Maps + + Maps Source Files