Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow adding and removing tilesets to existing maps #27

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 167 additions & 0 deletions Maps/MapData.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "MapData.h"
#include "CellType.h"
#include "../XFile.h"
#include <stdexcept>

unsigned int MapData::GetTileInfoIndex(unsigned int x, unsigned int y) const
{
Expand Down Expand Up @@ -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();
}
}

}
}
}
18 changes: 18 additions & 0 deletions Maps/MapData.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
};
2 changes: 1 addition & 1 deletion Maps/MapReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
}
Expand Down
28 changes: 27 additions & 1 deletion Maps/TerrainType.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -55,7 +81,7 @@ struct TerrainType
TileRange scorchedRange[3];

// UNKNOWN
short unkown[15];
short unknown[15];
};

#pragma pack(pop)
12 changes: 12 additions & 0 deletions Maps/TileData.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
26 changes: 26 additions & 0 deletions Maps/TileSetSource.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#include "TileSetSource.h"
#include "../XFile.h"
#include <stdexcept>

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;
}
10 changes: 9 additions & 1 deletion Maps/TileSetSource.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
1 change: 1 addition & 0 deletions OP2Utility.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@
<ClCompile Include="Maps\MapData.cpp" />
<ClCompile Include="Maps\MapReader.cpp" />
<ClCompile Include="Maps\MapWriter.cpp" />
<ClCompile Include="Maps\TileSetSource.cpp" />
<ClCompile Include="ResourceManager.cpp" />
<ClCompile Include="Archives\AdaptHuffTree.cpp" />
<ClCompile Include="Archives\MemoryMappedFile.cpp" />
Expand Down
2 changes: 2 additions & 0 deletions OP2Utility.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@
<ClCompile Include="Maps\MapReader.cpp">
<Filter>Maps</Filter>
</ClCompile>
<ClCompile Include="Maps\TileSetSource.cpp">
<Filter>Maps</Filter>
<ClCompile Include="StreamWriter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
Expand Down