/* Copyright (c) 2023 Alex Diener This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Alex Diener alex@ludobloom.com */ #include "gamemath/Rect4i.h" #include "tileset/TileMapVisual.h" #include "utilities/IOUtilities.h" #include "stem_core.h" #include #define stemobject_implementation TileMapVisual stemobject_vtable_begin(); stemobject_vtable_entry(dispose); stemobject_vtable_end(); TileMapVisual * TileMapVisual_create(TilesetEditData * tileset, ImageCollection * imageCollection) { stemobject_create_implementation(init, tileset, imageCollection) } bool TileMapVisual_init(TileMapVisual * self, TilesetEditData * tileset, ImageCollection * imageCollection) { call_super(init, self); self->tileset = tileset; self->imageCollection = imageCollection; self->drawnTileCount = 0; self->drawnTileAllocatedCount = 0; self->drawnTiles = NULL; self->nextListOrder = 0; return true; } void TileMapVisual_dispose(TileMapVisual * self) { free(self->drawnTiles); call_super_virtual(dispose, self); } TileMapVisual * TileMapVisual_copy(TileMapVisual * self) { TileMapVisual * copy = TileMapVisual_create(self->tileset, self->imageCollection); copy->drawnTileCount = self->drawnTileCount; copy->drawnTileAllocatedCount = self->drawnTileCount; copy->drawnTiles = memdup(self->drawnTiles, self->drawnTileCount * sizeof(*self->drawnTiles)); copy->nextListOrder = self->nextListOrder; return copy; } static Rect4f sliceTextureBounds(Rect4f atlasEntry, Rect4i totalBounds, Rect4i sliceBounds) { Rect4f textureBounds; textureBounds.xMin = atlasEntry.xMin + (atlasEntry.xMax - atlasEntry.xMin) * (sliceBounds.xMin - totalBounds.xMin) / (totalBounds.xMax - totalBounds.xMin); textureBounds.xMax = atlasEntry.xMax - (atlasEntry.xMax - atlasEntry.xMin) * (totalBounds.xMax - sliceBounds.xMax) / (totalBounds.xMax - totalBounds.xMin); textureBounds.yMin = atlasEntry.yMin + (atlasEntry.yMax - atlasEntry.yMin) * (totalBounds.yMax - sliceBounds.yMax) / (totalBounds.yMax - totalBounds.yMin); textureBounds.yMax = atlasEntry.yMax - (atlasEntry.yMax - atlasEntry.yMin) * (sliceBounds.yMin - totalBounds.yMin) / (totalBounds.yMax - totalBounds.yMin); return textureBounds; } static void addDrawnTile(TileMapVisual * self, TileMapVisual_drawnTile drawnTile) { if (self->drawnTileCount >= self->drawnTileAllocatedCount) { self->drawnTileAllocatedCount += self->drawnTileAllocatedCount + (self->drawnTileAllocatedCount == 0) * 64; self->drawnTiles = realloc(self->drawnTiles, self->drawnTileAllocatedCount * sizeof(*self->drawnTiles)); } drawnTile.listOrder = self->nextListOrder++; self->drawnTiles[self->drawnTileCount++] = drawnTile; } static void addTileImage(TileMapVisual * self, TileEditData * tile, ImageCollectionEntry * imageEntry, Vector2i tilePosition) { if (tile == NULL || imageEntry == NULL) { return; } Rect4i tileBounds = Rect4i_fromPositionAndSize(tile->offset, VECTOR2i(imageEntry->size.x, imageEntry->size.y)); TileMapVisual_drawnTile drawnTile = {.tilePosition = tilePosition, .shape = TILE_SHAPE_QUAD}; drawnTile.size.x = tileBounds.xMax - tileBounds.xMin; drawnTile.size.y = tileBounds.yMax - tileBounds.yMin; drawnTile.offset.x = tileBounds.xMin; drawnTile.offset.y = tileBounds.yMin; drawnTile.textureBounds = imageEntry->atlasEntry; drawnTile.tileID = tile->identifier; drawnTile.drawPriority = 0; addDrawnTile(self, drawnTile); } void TileMapVisual_addTileInstanceGrid(TileMapVisual * self, TileInstanceGrid * tileGrid, Vector2i tileOffset) { Vector2u gridSize = tileGrid->size; TileInstance * tileInstances = tileGrid->tileInstances; for (unsigned int rowIndex = 0; rowIndex < gridSize.y; rowIndex++) { for (unsigned int columnIndex = 0; columnIndex < gridSize.x; columnIndex++) { Vector2i tilePosition = {tileOffset.x + columnIndex, tileOffset.y + rowIndex}; TileEditData * tileData = TilesetEditData_getTile(self->tileset, tileInstances[rowIndex * gridSize.x + columnIndex].tileID); ImageCollectionEntry * imageEntry = NULL; if (tileData != NULL) { imageEntry = ImageCollection_getImage(self->imageCollection, tileData->imageID); } addTileImage(self, tileData, imageEntry, tilePosition); } } } static void addTileSlice(TileMapVisual * self, unsigned int relativeOriginX, unsigned int relativeOriginY, TileEditData * tile, ImageCollectionEntry * imageEntry, TileMapVisual_shape shape, Vector2i tilePosition) { if (tile == NULL || imageEntry == NULL) { return; } Vector2u tileSize = self->tileset->tileSize; TileMapVisual_drawnTile drawnTile = {.tilePosition = {tilePosition.x - relativeOriginX, tilePosition.y - relativeOriginY}, .shape = shape}; Rect4i tileImageTotalBounds = Rect4i_fromPositionAndSize(tile->offset, VECTOR2i(imageEntry->size.x, imageEntry->size.y)); Rect4i tileImageSlicedBounds = tileImageTotalBounds; if (relativeOriginX == 1) { if (tileImageSlicedBounds.xMin < (int) (tileSize.x - tileSize.x / 2)) { tileImageSlicedBounds.xMin = tileSize.x - tileSize.x / 2; } } else { if (tileImageSlicedBounds.xMax > (int) (tileSize.x - tileSize.x / 2)) { tileImageSlicedBounds.xMax = tileSize.x - tileSize.x / 2; } } if (relativeOriginY == 1) { if (tileImageSlicedBounds.yMin < (int) (tileSize.y - tileSize.y / 2)) { tileImageSlicedBounds.yMin = tileSize.y - tileSize.y / 2; } } else { if (tileImageSlicedBounds.yMax > (int) (tileSize.y - tileSize.y / 2)) { tileImageSlicedBounds.yMax = tileSize.y - tileSize.y / 2; } } if (tileImageSlicedBounds.xMax <= tileImageSlicedBounds.xMin || tileImageSlicedBounds.yMax <= tileImageSlicedBounds.yMin) { return; } drawnTile.size.x = tileImageSlicedBounds.xMax - tileImageSlicedBounds.xMin; drawnTile.size.y = tileImageSlicedBounds.yMax - tileImageSlicedBounds.yMin; drawnTile.offset.x = tileImageSlicedBounds.xMin; drawnTile.offset.y = tileImageSlicedBounds.yMin; drawnTile.textureBounds = sliceTextureBounds(imageEntry->atlasEntry, tileImageTotalBounds, tileImageSlicedBounds); drawnTile.tileID = tile->identifier; drawnTile.drawPriority = 0; addDrawnTile(self, drawnTile); } static void addTileAdjacencySlice(TileMapVisual * self, unsigned int sliceHalfTileX, unsigned int sliceHalfTileY, unsigned int relativeOriginX, unsigned int relativeOriginY, TileAdjacencyPair * adjacencyPair, ImageCollectionEntry * imageEntry, TileMapVisual_shape shape, Vector2i tilePosition, TileID tileID) { Vector2u tileSize = self->tileset->tileSize; TileMapVisual_drawnTile drawnTile = {.tilePosition = {tilePosition.x - relativeOriginX, tilePosition.y - relativeOriginY}, .shape = shape}; Rect4i sliceTotalBounds = Rect4i_fromPositionAndSize(VECTOR2i_ZERO, VECTOR2i(imageEntry->size.x, imageEntry->size.y)); Rect4i sliceBounds = RECT4i(tileSize.x * sliceHalfTileX / 2, tileSize.x * (sliceHalfTileX + 1) / 2, tileSize.y * sliceHalfTileY / 2, tileSize.y * (sliceHalfTileY + 1) / 2); drawnTile.size.x = sliceBounds.xMax - sliceBounds.xMin; drawnTile.size.y = sliceBounds.yMax - sliceBounds.yMin; drawnTile.offset.x = -drawnTile.size.x * relativeOriginX + tileSize.x * relativeOriginX; drawnTile.offset.y = -drawnTile.size.y * relativeOriginY + tileSize.y * relativeOriginY; drawnTile.textureBounds = sliceTextureBounds(imageEntry->atlasEntry, sliceTotalBounds, sliceBounds); drawnTile.tileID = tileID; drawnTile.drawPriority = 0; addDrawnTile(self, drawnTile); } void TileMapVisual_addTileInstanceGridWithBlendMap(TileMapVisual * self, TileInstanceGrid * tileGrid, TilesetAdjacencyBlendMap * blendMap, Vector2i tileOffset) { Vector2u gridSize = tileGrid->size; TileInstance * tileInstances = tileGrid->tileInstances; unsigned int rowMin = 0, rowMax = gridSize.y, columnMin = 0, columnMax = gridSize.x; for (unsigned int rowIndex = rowMin; rowIndex <= rowMax; rowIndex++) { TileID tileIDs[4] = {TILE_ID_NONE, TILE_ID_NONE, TILE_ID_NONE, TILE_ID_NONE}; TileEditData * tiles[4] = {NULL, NULL, NULL, NULL}; ImageCollectionEntry * tileImageEntries[4] = {NULL, NULL, NULL, NULL}; for (unsigned int columnIndex = columnMin; columnIndex <= columnMax; columnIndex++) { Vector2i tilePosition = {tileOffset.x + columnIndex, tileOffset.y + rowIndex}; tileIDs[0] = tileIDs[1]; tileIDs[2] = tileIDs[3]; tiles[0] = tiles[1]; tiles[2] = tiles[3]; tileImageEntries[0] = tileImageEntries[1]; tileImageEntries[2] = tileImageEntries[3]; if (columnIndex < gridSize.x) { if (rowIndex > 0) { tileIDs[1] = tileInstances[(rowIndex - 1) * gridSize.x + columnIndex].tileID; } if (rowIndex < gridSize.y) { tileIDs[3] = tileInstances[rowIndex * gridSize.x + columnIndex].tileID; } tiles[1] = TilesetEditData_getTile(self->tileset, tileIDs[1]); tiles[3] = TilesetEditData_getTile(self->tileset, tileIDs[3]); if (tiles[1] == NULL) { tileImageEntries[1] = NULL; } else { tileImageEntries[1] = ImageCollection_getImage(self->imageCollection, tiles[1]->imageID); } if (tiles[3] == NULL) { tileImageEntries[3] = NULL; } else { tileImageEntries[3] = ImageCollection_getImage(self->imageCollection, tiles[3]->imageID); } } else { tileIDs[1] = tileIDs[3] = TILE_ID_NONE; tiles[1] = tiles[3] = NULL; tileImageEntries[1] = tileImageEntries[3] = NULL; } TileAdjacencyPair * adjacencies[6] = {NULL, NULL, NULL, NULL, NULL, NULL}; if (blendMap != NULL) { if (rowIndex > 0 && columnIndex > 0 && columnIndex < gridSize.x) { adjacencies[0] = TilesetAdjacencyBlendMap_getTileAdjacencyPair(blendMap, tileIDs[0], tileIDs[1], NULL); } if (rowIndex > 0 && rowIndex < gridSize.y) { if (columnIndex > 0) { adjacencies[1] = TilesetAdjacencyBlendMap_getTileAdjacencyPair(blendMap, tileIDs[0], tileIDs[2], NULL); if (columnIndex < gridSize.x) { adjacencies[2] = TilesetAdjacencyBlendMap_getTileAdjacencyPair(blendMap, tileIDs[0], tileIDs[3], NULL); adjacencies[3] = TilesetAdjacencyBlendMap_getTileAdjacencyPair(blendMap, tileIDs[1], tileIDs[2], NULL); } } if (columnIndex < gridSize.x) { adjacencies[4] = TilesetAdjacencyBlendMap_getTileAdjacencyPair(blendMap, tileIDs[1], tileIDs[3], NULL); } } if (rowIndex < gridSize.y && columnIndex > 0 && columnIndex < gridSize.x) { adjacencies[5] = TilesetAdjacencyBlendMap_getTileAdjacencyPair(blendMap, tileIDs[2], tileIDs[3], NULL); } } ImageCollectionEntry * adjacencyImageEntries[6] = {NULL, NULL, NULL, NULL, NULL, NULL}; for (unsigned int adjacencyIndex = 0; adjacencyIndex < 6; adjacencyIndex++) { if (adjacencies[adjacencyIndex] != NULL) { adjacencyImageEntries[adjacencyIndex] = ImageCollection_getImage(self->imageCollection, adjacencies[adjacencyIndex]->sliceImageID); if (adjacencyImageEntries[adjacencyIndex] == NULL) { adjacencies[adjacencyIndex] = NULL; } } } if (columnIndex > columnMin && rowIndex > rowMin) { // Bottom right corner of above left tile if (adjacencies[0] != NULL && adjacencies[1] == adjacencies[0] && adjacencies[2] == adjacencies[0]) { if (adjacencies[0]->tileID1 == tileIDs[0]) { // Ab // bb addTileAdjacencySlice(self, 0, 2, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]); } else { // Ba // aa addTileAdjacencySlice(self, 6, 4, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]); } } else if (adjacencies[2] != NULL && adjacencies[2] == adjacencies[4] && adjacencies[2] == adjacencies[5]) { if (adjacencies[2]->tileID1 == tileIDs[0]) { // Aa // ab addTileAdjacencySlice(self, 4, 2, 1, 1, adjacencies[2], adjacencyImageEntries[2], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]); } else { // Bb // ba addTileAdjacencySlice(self, 2, 4, 1, 1, adjacencies[2], adjacencyImageEntries[2], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]); } } else if (adjacencies[0] != NULL && adjacencies[3] == adjacencies[0] && adjacencies[4] == adjacencies[0]) { if (adjacencies[0]->tileID1 == tileIDs[0]) { // Ab // aa addTileAdjacencySlice(self, 4, 4, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]); } else { // Ba // bb addTileAdjacencySlice(self, 2, 2, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]); } } else if (adjacencies[1] != NULL && adjacencies[3] == adjacencies[1] && adjacencies[5] == adjacencies[1]) { if (adjacencies[1]->tileID1 == tileIDs[0]) { // Aa // ba addTileAdjacencySlice(self, 6, 2, 1, 1, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]); } else { // Bb // ab addTileAdjacencySlice(self, 0, 4, 1, 1, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]); } } else if (adjacencies[0] != NULL && adjacencies[1] != NULL) { if (adjacencies[1] != adjacencies[0]) { // Ab // ca if (adjacencies[0]->tileID1 == tileIDs[0]) { addTileAdjacencySlice(self, 2, 6, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition, tileIDs[0]); } else { addTileAdjacencySlice(self, 4, 6, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition, tileIDs[0]); } if (adjacencies[1]->tileID1 == tileIDs[0]) { addTileAdjacencySlice(self, 2, 6, 1, 1, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition, tileIDs[0]); } else { addTileAdjacencySlice(self, 4, 6, 1, 1, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition, tileIDs[0]); } } else { if (adjacencies[0]->tileID1 == tileIDs[0]) { // Ab // ba if (tileIDs[0] == tileIDs[3]) { addTileAdjacencySlice(self, 2, 6, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]); } else { addTileAdjacencySlice(self, 6, 6, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]); } } else { // Ba // ab if (adjacencies[0] != adjacencies[1] || tileIDs[0] == adjacencies[0]->tileID1) { addTileAdjacencySlice(self, 4, 6, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]); } else { addTileAdjacencySlice(self, 0, 6, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]); } } } } else if (adjacencies[0] != NULL) { if (adjacencies[1] != NULL) { if (adjacencies[0]->tileID1 == tileIDs[0]) { // Ab // c? addTileAdjacencySlice(self, 0, 0, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition, tileIDs[0]); } else { // Ba // c? addTileAdjacencySlice(self, 2, 0, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition, tileIDs[0]); } if (adjacencies[1]->tileID1 == tileIDs[0]) { // Ab // c? addTileAdjacencySlice(self, 6, 0, 1, 1, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition, tileIDs[0]); } else { // Ba // c? addTileAdjacencySlice(self, 4, 0, 1, 1, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition, tileIDs[0]); } } else { if (tileIDs[3] == tileIDs[0]) { if (adjacencies[0]->tileID1 == tileIDs[0]) { // Ab // ?a addTileAdjacencySlice(self, 2, 6, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition, tileIDs[0]); addTileSlice(self, 1, 1, tiles[0], tileImageEntries[0], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition); } else { // Ba // ?b addTileAdjacencySlice(self, 4, 6, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition, tileIDs[0]); addTileSlice(self, 1, 1, tiles[0], tileImageEntries[0], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition); } } else { if (adjacencies[0]->tileID1 == tileIDs[0]) { // Ab // ab addTileAdjacencySlice(self, 0, 0, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]); } else { // Ba // ba addTileAdjacencySlice(self, 2, 0, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]); } } } } else if (adjacencies[1] != NULL) { if (tileIDs[3] == tileIDs[0]) { if (adjacencies[1]->tileID1 == tileIDs[0]) { // A? // ba addTileSlice(self, 1, 1, tiles[0], tileImageEntries[0], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition); addTileAdjacencySlice(self, 2, 6, 1, 1, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition, tileIDs[0]); } else { // B? // ab addTileSlice(self, 1, 1, tiles[0], tileImageEntries[0], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition); addTileAdjacencySlice(self, 4, 6, 1, 1, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition, tileIDs[0]); } } else { if (adjacencies[1]->tileID1 == tileIDs[0]) { // Aa // bb addTileAdjacencySlice(self, 6, 0, 1, 1, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]); } else { // Bb // aa addTileAdjacencySlice(self, 4, 0, 1, 1, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]); } } } else if (tileImageEntries[0] != NULL) { addTileSlice(self, 1, 1, tiles[0], tileImageEntries[0], TILE_SHAPE_QUAD, tilePosition); } } if (columnIndex < columnMax && rowIndex > rowMin) { // Bottom left corner of above right tile if (adjacencies[0] != NULL && adjacencies[4] == adjacencies[0] && adjacencies[3] == adjacencies[0]) { if (adjacencies[0]->tileID1 == tileIDs[1]) { // bA // bb addTileAdjacencySlice(self, 3, 2, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]); } else { // aB // aa addTileAdjacencySlice(self, 5, 4, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]); } } else if (adjacencies[3] != NULL && adjacencies[3] == adjacencies[1] && adjacencies[3] == adjacencies[5]) { if (adjacencies[3]->tileID1 == tileIDs[1]) { // aA // ba addTileAdjacencySlice(self, 7, 2, 0, 1, adjacencies[3], adjacencyImageEntries[3], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]); } else { // bB // ab addTileAdjacencySlice(self, 1, 4, 0, 1, adjacencies[3], adjacencyImageEntries[3], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]); } } else if (adjacencies[0] != NULL && adjacencies[2] == adjacencies[0] && adjacencies[1] == adjacencies[0]) { if (adjacencies[0]->tileID1 == tileIDs[1]) { // bA // aa addTileAdjacencySlice(self, 7, 4, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]); } else { // aB // bb addTileAdjacencySlice(self, 1, 2, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]); } } else if (adjacencies[4] != NULL && adjacencies[2] == adjacencies[4] && adjacencies[5] == adjacencies[4]) { if (adjacencies[4]->tileID1 == tileIDs[1]) { // aA // ab addTileAdjacencySlice(self, 5, 2, 0, 1, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]); } else { // bB // ba addTileAdjacencySlice(self, 3, 4, 0, 1, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]); } } else if (adjacencies[0] != NULL && adjacencies[4] != NULL) { if (adjacencies[4] != adjacencies[0]) { // bA // ac if (adjacencies[0]->tileID1 == tileIDs[1]) { addTileAdjacencySlice(self, 1, 6, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition, tileIDs[1]); } else { addTileAdjacencySlice(self, 7, 6, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition, tileIDs[1]); } if (adjacencies[4]->tileID1 == tileIDs[1]) { addTileAdjacencySlice(self, 1, 6, 0, 1, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition, tileIDs[1]); } else { addTileAdjacencySlice(self, 7, 6, 0, 1, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition, tileIDs[1]); } } else { if (adjacencies[0]->tileID1 == tileIDs[1]) { // bA // ab if (tileIDs[1] == tileIDs[2]) { addTileAdjacencySlice(self, 1, 6, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]); } else { addTileAdjacencySlice(self, 5, 6, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]); } } else { // aB // ba if (adjacencies[0] != adjacencies[4] || tileIDs[1] == adjacencies[0]->tileID1) { addTileAdjacencySlice(self, 7, 6, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]); } else { addTileAdjacencySlice(self, 3, 6, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]); } } } } else if (adjacencies[0] != NULL) { if (adjacencies[4] != NULL) { if (adjacencies[0]->tileID1 == tileIDs[1]) { // bA // ?c addTileAdjacencySlice(self, 3, 0, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition, tileIDs[1]); } else { // aB // ?c addTileAdjacencySlice(self, 0, 0, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition, tileIDs[1]); } if (adjacencies[4]->tileID1 == tileIDs[1]) { // bA // ?c addTileAdjacencySlice(self, 7, 0, 0, 1, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition, tileIDs[1]); } else { // aB // ?c addTileAdjacencySlice(self, 5, 0, 0, 1, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition, tileIDs[1]); } } else { if (tileIDs[2] == tileIDs[1]) { if (adjacencies[0]->tileID1 == tileIDs[1]) { // bA // a? addTileAdjacencySlice(self, 1, 6, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition, tileIDs[1]); addTileSlice(self, 0, 1, tiles[1], tileImageEntries[1], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition); } else { // aB // b? addTileAdjacencySlice(self, 7, 6, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition, tileIDs[1]); addTileSlice(self, 0, 1, tiles[1], tileImageEntries[1], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition); } } else { if (adjacencies[0]->tileID1 == tileIDs[1]) { // bA // ba addTileAdjacencySlice(self, 3, 0, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]); } else { // aB // ab addTileAdjacencySlice(self, 1, 0, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]); } } } } else if (adjacencies[4] != NULL) { if (tileIDs[2] == tileIDs[1]) { if (adjacencies[4]->tileID1 == tileIDs[1]) { // ?A // ab addTileSlice(self, 0, 1, tiles[1], tileImageEntries[1], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition); addTileAdjacencySlice(self, 1, 6, 0, 1, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition, tileIDs[1]); } else { // ?B // ba addTileSlice(self, 0, 1, tiles[1], tileImageEntries[1], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition); addTileAdjacencySlice(self, 7, 6, 0, 1, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition, tileIDs[1]); } } else { if (adjacencies[4]->tileID1 == tileIDs[1]) { // aA // bb addTileAdjacencySlice(self, 7, 0, 0, 1, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]); } else { // Bb // aa addTileAdjacencySlice(self, 5, 0, 0, 1, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]); } } } else if (tileImageEntries[1] != NULL) { addTileSlice(self, 0, 1, tiles[1], tileImageEntries[1], TILE_SHAPE_QUAD, tilePosition); } } if (columnIndex > columnMin && rowIndex < rowMax) { // Top right corner of below left tile if (adjacencies[5] != NULL && adjacencies[1] == adjacencies[5] && adjacencies[3] == adjacencies[5]) { if (adjacencies[5]->tileID1 == tileIDs[2]) { // bb // Ab addTileAdjacencySlice(self, 0, 5, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]); } else { // aa // Ba addTileAdjacencySlice(self, 6, 3, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]); } } else if (adjacencies[3] != NULL && adjacencies[3] == adjacencies[4] && adjacencies[3] == adjacencies[0]) { if (adjacencies[3]->tileID1 == tileIDs[3]) { // ab // Aa addTileAdjacencySlice(self, 4, 5, 1, 0, adjacencies[3], adjacencyImageEntries[3], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]); } else { // ba // Bb addTileAdjacencySlice(self, 2, 3, 1, 0, adjacencies[3], adjacencyImageEntries[3], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]); } } else if (adjacencies[5] != NULL && adjacencies[2] == adjacencies[5] && adjacencies[4] == adjacencies[5]) { if (adjacencies[5]->tileID1 == tileIDs[2]) { // aa // Ab addTileAdjacencySlice(self, 4, 3, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]); } else { // bb // Ba addTileAdjacencySlice(self, 2, 5, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]); } } else if (adjacencies[1] != NULL && adjacencies[2] == adjacencies[1] && adjacencies[0] == adjacencies[1]) { if (adjacencies[1]->tileID1 == tileIDs[2]) { // ba // Aa addTileAdjacencySlice(self, 6, 5, 1, 0, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]); } else { // ab // Bb addTileAdjacencySlice(self, 0, 3, 1, 0, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]); } } else if (adjacencies[5] != NULL && adjacencies[1] != NULL) { if (adjacencies[1] != adjacencies[5]) { // ca // Ab if (adjacencies[1]->tileID1 == tileIDs[2]) { addTileAdjacencySlice(self, 0, 7, 1, 0, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition, tileIDs[2]); } else { addTileAdjacencySlice(self, 6, 7, 1, 0, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition, tileIDs[2]); } if (adjacencies[5]->tileID1 == tileIDs[2]) { addTileAdjacencySlice(self, 0, 7, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition, tileIDs[2]); } else { addTileAdjacencySlice(self, 6, 7, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition, tileIDs[2]); } } else { if (adjacencies[5]->tileID1 == tileIDs[2]) { // ba // Ab if (tileIDs[2] == tileIDs[1]) { addTileAdjacencySlice(self, 0, 7, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]); } else { addTileAdjacencySlice(self, 4, 7, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]); } } else { // ab // Ba if (adjacencies[5] != adjacencies[1] || tileIDs[2] == adjacencies[5]->tileID1) { addTileAdjacencySlice(self, 6, 7, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]); } else { addTileAdjacencySlice(self, 2, 7, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]); } } } } else if (adjacencies[5] != NULL) { if (adjacencies[1] != NULL) { if (adjacencies[1]->tileID1 == tileIDs[2]) { // c? // Ab addTileAdjacencySlice(self, 4, 1, 1, 0, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition, tileIDs[2]); } else { // c? // Ba addTileAdjacencySlice(self, 6, 1, 1, 0, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition, tileIDs[2]); } if (adjacencies[5]->tileID1 == tileIDs[2]) { // c? // Ab addTileAdjacencySlice(self, 0, 1, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition, tileIDs[2]); } else { // c? // Ba addTileAdjacencySlice(self, 2, 1, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition, tileIDs[2]); } } else { if (tileIDs[1] == tileIDs[2]) { if (adjacencies[5]->tileID1 == tileIDs[2]) { // ?a // Ab addTileSlice(self, 1, 0, tiles[2], tileImageEntries[2], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition); addTileAdjacencySlice(self, 0, 7, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition, tileIDs[2]); } else { // ?b // Ba addTileSlice(self, 1, 0, tiles[2], tileImageEntries[2], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition); addTileAdjacencySlice(self, 6, 7, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition, tileIDs[2]); } } else { if (adjacencies[5]->tileID1 == tileIDs[2]) { // ab // Ab addTileAdjacencySlice(self, 0, 1, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]); } else { // ba // Ba addTileAdjacencySlice(self, 2, 1, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]); } } } } else if (adjacencies[1] != NULL) { if (tileIDs[1] == tileIDs[2]) { if (adjacencies[1]->tileID1 == tileIDs[2]) { // A? // ba addTileAdjacencySlice(self, 0, 7, 1, 0, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition, tileIDs[2]); addTileSlice(self, 1, 0, tiles[2], tileImageEntries[2], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition); } else { // B? // ab addTileAdjacencySlice(self, 6, 7, 1, 0, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition, tileIDs[2]); addTileSlice(self, 1, 0, tiles[2], tileImageEntries[2], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition); } } else { if (adjacencies[1]->tileID1 == tileIDs[2]) { // bb // Aa addTileAdjacencySlice(self, 4, 1, 1, 0, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]); } else { // Bb // aa addTileAdjacencySlice(self, 6, 1, 1, 0, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]); } } } else if (tileImageEntries[2] != NULL) { addTileSlice(self, 1, 0, tiles[2], tileImageEntries[2], TILE_SHAPE_QUAD, tilePosition); } } if (columnIndex < columnMax && rowIndex < rowMax) { // Top left corner of below right tile if (adjacencies[5] != NULL && adjacencies[4] == adjacencies[5] && adjacencies[2] == adjacencies[5]) { if (adjacencies[5]->tileID1 == tileIDs[3]) { // bb // bA addTileAdjacencySlice(self, 3, 5, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]); } else { // aa // aB addTileAdjacencySlice(self, 5, 3, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]); } } else if (adjacencies[2] != NULL && adjacencies[2] == adjacencies[1] && adjacencies[2] == adjacencies[0]) { if (adjacencies[2]->tileID1 == tileIDs[3]) { // ba // aA addTileAdjacencySlice(self, 7, 5, 0, 0, adjacencies[2], adjacencyImageEntries[2], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]); } else { // ab // bB addTileAdjacencySlice(self, 1, 3, 0, 0, adjacencies[2], adjacencyImageEntries[2], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]); } } else if (adjacencies[5] != NULL && adjacencies[3] == adjacencies[5] && adjacencies[1] == adjacencies[5]) { if (adjacencies[5]->tileID1 == tileIDs[3]) { // aa // bA addTileAdjacencySlice(self, 7, 3, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]); } else { // bb // aB addTileAdjacencySlice(self, 1, 5, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]); } } else if (adjacencies[4] != NULL && adjacencies[3] == adjacencies[4] && adjacencies[0] == adjacencies[4]) { if (adjacencies[4]->tileID1 == tileIDs[3]) { // ab // aA addTileAdjacencySlice(self, 5, 5, 0, 0, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]); } else { // ba // bB addTileAdjacencySlice(self, 3, 3, 0, 0, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]); } } else if (adjacencies[5] != NULL && adjacencies[4] != NULL) { if (adjacencies[4] != adjacencies[5]) { // ac // bA if (adjacencies[4]->tileID1 == tileIDs[3]) { addTileAdjacencySlice(self, 3, 7, 0, 0, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition, tileIDs[3]); } else { addTileAdjacencySlice(self, 5, 7, 0, 0, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition, tileIDs[3]); } if (adjacencies[5]->tileID1 == tileIDs[3]) { addTileAdjacencySlice(self, 3, 7, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition, tileIDs[3]); } else { addTileAdjacencySlice(self, 5, 7, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition, tileIDs[3]); } } else { if (adjacencies[5]->tileID1 == tileIDs[3]) { // ab // bA if (tileIDs[3] == tileIDs[0]) { addTileAdjacencySlice(self, 3, 7, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]); } else { addTileAdjacencySlice(self, 7, 7, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]); } } else { // ba // aB if (adjacencies[5] != adjacencies[4] || tileIDs[3] == adjacencies[5]->tileID1) { addTileAdjacencySlice(self, 5, 7, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]); } else { addTileAdjacencySlice(self, 1, 7, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]); } } } } else if (adjacencies[5] != NULL) { if (adjacencies[4] != NULL) { if (adjacencies[4]->tileID1 == tileIDs[3]) { // ?c // bA addTileAdjacencySlice(self, 5, 1, 0, 0, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition, tileIDs[3]); } else { // ?c // aB addTileAdjacencySlice(self, 7, 1, 0, 0, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition, tileIDs[3]); } if (adjacencies[5]->tileID1 == tileIDs[3]) { // ?c // bA addTileAdjacencySlice(self, 3, 1, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition, tileIDs[3]); } else { // ?c // aB addTileAdjacencySlice(self, 1, 1, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition, tileIDs[3]); } } else { if (tileIDs[0] == tileIDs[3]) { if (adjacencies[5]->tileID1 == tileIDs[3]) { // a? // bA addTileSlice(self, 0, 0, tiles[3], tileImageEntries[3], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition); addTileAdjacencySlice(self, 3, 7, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition, tileIDs[3]); } else { // b? // aB addTileSlice(self, 0, 0, tiles[3], tileImageEntries[3], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition); addTileAdjacencySlice(self, 5, 7, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition, tileIDs[3]); } } else { if (adjacencies[5]->tileID1 == tileIDs[3]) { // ba // bA addTileAdjacencySlice(self, 3, 1, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]); } else { // ab // aB addTileAdjacencySlice(self, 1, 1, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]); } } } } else if (adjacencies[4] != NULL) { if (tileIDs[0] == tileIDs[3]) { if (adjacencies[4]->tileID1 == tileIDs[3]) { // ab // ?A addTileAdjacencySlice(self, 3, 7, 0, 0, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition, tileIDs[3]); addTileSlice(self, 0, 0, tiles[3], tileImageEntries[3], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition); } else { // ba // ?B addTileAdjacencySlice(self, 5, 7, 0, 0, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition, tileIDs[3]); addTileSlice(self, 0, 0, tiles[3], tileImageEntries[3], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition); } } else { if (adjacencies[4]->tileID1 == tileIDs[3]) { // bb // aA addTileAdjacencySlice(self, 5, 1, 0, 0, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]); } else { // Bb // aa addTileAdjacencySlice(self, 7, 1, 0, 0, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]); } } } else if (tileImageEntries[3] != NULL) { addTileSlice(self, 0, 0, tiles[3], tileImageEntries[3], TILE_SHAPE_QUAD, tilePosition); } } } } } static void addTileImageWithAdjacencyBehaviors(TileMapVisual * self, TileID tileID, TileEditData * tile, ImageCollectionEntry * imageEntry, Vector2i tilePosition, unsigned int behaviorCount, TileAdjacencyBehavior ** behaviors) { if (tile != NULL && imageEntry != NULL) { Rect4i tileBounds = Rect4i_fromPositionAndSize(tile->offset, VECTOR2i(imageEntry->size.x, imageEntry->size.y)); Rect4i sliceInsets = behaviors[0]->sliceInsets; for (unsigned int behaviorIndex = 1; behaviorIndex < behaviorCount; behaviorIndex++) { sliceInsets.xMin = fmaxf(sliceInsets.xMin, behaviors[behaviorIndex]->sliceInsets.xMin); sliceInsets.xMax = fmaxf(sliceInsets.xMax, behaviors[behaviorIndex]->sliceInsets.xMax); sliceInsets.yMin = fmaxf(sliceInsets.yMin, behaviors[behaviorIndex]->sliceInsets.yMin); sliceInsets.yMax = fmaxf(sliceInsets.yMax, behaviors[behaviorIndex]->sliceInsets.yMax); } Rect4i tileSlicedBounds = Rect4i_insetMargins(tileBounds, sliceInsets); if (tileSlicedBounds.xMax > tileSlicedBounds.xMin && tileSlicedBounds.yMax > tileSlicedBounds.yMin) { TileMapVisual_drawnTile drawnTile = {.tilePosition = tilePosition, .shape = TILE_SHAPE_QUAD}; drawnTile.size.x = tileSlicedBounds.xMax - tileSlicedBounds.xMin; drawnTile.size.y = tileSlicedBounds.yMax - tileSlicedBounds.yMin; drawnTile.offset.x = tileSlicedBounds.xMin; drawnTile.offset.y = tileSlicedBounds.yMin; drawnTile.textureBounds = sliceTextureBounds(imageEntry->atlasEntry, tileBounds, tileSlicedBounds); drawnTile.tileID = tileID; drawnTile.drawPriority = 0; addDrawnTile(self, drawnTile); } } for (unsigned int behaviorIndex = 0; behaviorIndex < behaviorCount; behaviorIndex++) { for (unsigned int overlayIndex = 0; overlayIndex < behaviors[behaviorIndex]->overlayCount; overlayIndex++) { TileAdjacencyOverlay overlay = behaviors[behaviorIndex]->overlays[overlayIndex]; ImageCollectionEntry * overlayImageEntry = ImageCollection_getImage(self->imageCollection, overlay.imageID); if (overlayImageEntry != NULL) { Rect4i overlayBounds = Rect4i_fromPositionAndSize(overlay.offset, VECTOR2i(overlayImageEntry->size.x, overlayImageEntry->size.y)); Rect4i overlaySlicedBounds = Rect4i_insetMargins(overlayBounds, overlay.sliceInsets); if (overlaySlicedBounds.xMax > overlaySlicedBounds.xMin && overlaySlicedBounds.yMax > overlaySlicedBounds.yMin) { TileMapVisual_drawnTile drawnTile = {.tilePosition = tilePosition, .shape = TILE_SHAPE_QUAD}; drawnTile.size.x = overlaySlicedBounds.xMax - overlaySlicedBounds.xMin; drawnTile.size.y = overlaySlicedBounds.yMax - overlaySlicedBounds.yMin; drawnTile.offset.x = overlaySlicedBounds.xMin; drawnTile.offset.y = overlaySlicedBounds.yMin; drawnTile.textureBounds = sliceTextureBounds(overlayImageEntry->atlasEntry, overlayBounds, overlaySlicedBounds); drawnTile.tileID = tileID; drawnTile.drawPriority = overlay.drawPriority; addDrawnTile(self, drawnTile); } } } } } void TileMapVisual_addTileInstanceGridWithBehaviorSet(TileMapVisual * self, TileAdjacencyBehaviorSet * behaviorSet, Rect4i tileRect, TileQueryCallback queryCallback, void * callbackContext) { for (int rowIndex = tileRect.yMin; rowIndex < tileRect.yMax; rowIndex++) { for (int columnIndex = tileRect.xMin; columnIndex < tileRect.xMax; columnIndex++) { Vector2i tilePosition = {columnIndex, rowIndex}; TileID tileID = queryCallback(tilePosition, callbackContext); TileEditData * tileData = TilesetEditData_getTile(self->tileset, tileID); ImageCollectionEntry * imageEntry = NULL; if (tileData != NULL) { imageEntry = ImageCollection_getImage(self->imageCollection, tileData->imageID); } TileAdjacencyBehavior * behaviors[64]; unsigned int behaviorCount = TileAdjacencyBehaviorSet_getApplicableBehaviors(behaviorSet, tilePosition, tileID, queryCallback, callbackContext, sizeof_count(behaviors), behaviors); if (behaviorCount == 0) { addTileImage(self, tileData, imageEntry, tilePosition); } else { addTileImageWithAdjacencyBehaviors(self, tileID, tileData, imageEntry, tilePosition, behaviorCount, behaviors); } } } } void TileMapVisual_clear(TileMapVisual * self) { self->drawnTileCount = 0; self->nextListOrder = 0; } Rect4i TileMapVisual_calculateExtendedBounds(TileMapVisual * self) { Rect4i result = RECT4i_EMPTY; Vector2u tileSize = self->tileset->tileSize; for (unsigned int drawnTileIndex = 0; drawnTileIndex < self->drawnTileCount; drawnTileIndex++) { TileMapVisual_drawnTile drawnTile = self->drawnTiles[drawnTileIndex]; Rect4i tileRect; tileRect.xMin = drawnTile.tilePosition.x * tileSize.x + drawnTile.offset.x; tileRect.yMin = drawnTile.tilePosition.y * tileSize.y + drawnTile.offset.y; tileRect.xMax = tileRect.xMin + drawnTile.size.x; tileRect.yMax = tileRect.yMin + drawnTile.size.y; result = Rect4i_union(tileRect, result); } return result; } static int compareDrawnTiles(const void * lhsUntyped, const void * rhsUntyped) { const TileMapVisual_drawnTile * lhs = lhsUntyped, * rhs = rhsUntyped; if (lhs->drawPriority != rhs->drawPriority) { return (lhs->drawPriority > rhs->drawPriority) * 2 - 1; } if (lhs->tilePosition.y != rhs->tilePosition.y) { return (lhs->tilePosition.y > rhs->tilePosition.y) * 2 - 1; } if (lhs->tilePosition.x != rhs->tilePosition.x) { return (lhs->tilePosition.x > rhs->tilePosition.x) * 2 - 1; } return (lhs->listOrder > rhs->listOrder) * 2 - 1; } void TileMapVisual_sortByDrawPriority(TileMapVisual * self) { qsort(self->drawnTiles, self->drawnTileCount, sizeof(self->drawnTiles[0]), compareDrawnTiles); }