#include "map.h" #include "../core/memory.h" #include "../math/vec2i.h" #include #include BzTileMap BZ_TILEMAP_INVALID = {.isValid = false}; int16_t bzTileLayerGetTile(BzTileLayer *layer, i32 x, i32 y) { return layer->data[layer->width * y + x]; } static void handleTileLayer(BzTileLayer *layer, cute_tiled_layer_t *cuteLayer) { layer->id = cuteLayer->id; layer->dataCount = cuteLayer->data_count; layer->data = NULL; if (layer->dataCount > 0) { layer->data = bzAlloc(layer->dataCount * sizeof(*layer->data)); layer->minData = layer->maxData = (i16) cuteLayer->data[0]; for (i32 i = 0; i < layer->dataCount; i++) { layer->data[i] = (i16) cuteLayer->data[i]; if (layer->data[i] < layer->minData) layer->minData = layer->data[i]; else if (layer->data[i] > layer->maxData) layer->maxData = layer->data[i]; } } layer->width = cuteLayer->width; layer->height = cuteLayer->height; layer->offsetX = cuteLayer->offsetx; layer->offsetY = cuteLayer->offsety; layer->opacity = cuteLayer->opacity; } // tileset.c BzTileShape bzCuteObjectToTileShape(cute_tiled_object_t *object); static void handleTileObjectLayer(BzTileObjectLayer *layer, cute_tiled_layer_t *cuteLayer, BzStringHashFunc hashFunc) { // Count objects layer->objectCount = 0; cute_tiled_object_t *object = cuteLayer->objects; while (object) { layer->objectCount++; object = object->next; } layer->objects = bzAlloc(layer->objectCount * sizeof(*layer->objects)); object = cuteLayer->objects; for (i32 i = 0; i < layer->objectCount; i++) { layer->objects[i].id = hashFunc(object->name.ptr); layer->objects[i].shape = bzCuteObjectToTileShape(object); object = object->next; } } static void createColliders(BzTileMap *map) { map->collidersCount = map->width * map->height; map->colliderMap = bzAlloc(map->collidersCount * sizeof(*map->colliderMap)); for (i32 i = 0; i < map->collidersCount; i++) { map->colliderMap[i] = (BzTileCollider) {{BZ_TILE_SHAPE_NONE}}; } // Top-most layer takes priority for (i32 i = map->layerCount - 1; i >= 0; i--) { BzTileLayer *layer = map->layers + i; if (layer->tilesetIdx == -1) continue; BzTileset *tileset = map->tilesets + layer->tilesetIdx; for (i32 y = 0; y < map->height; y++) { for (i32 x = 0; x < map->width; x++) { i32 tile = bzTileLayerGetTile(layer, x, y); BzTileShape tilesetShape = bzTilesetGetTileCollider(tileset, tile); if (tilesetShape.type == BZ_TILE_SHAPE_NONE || tilesetShape.type == BZ_TILE_SHAPE_POINT) continue; tilesetShape.x += layer->offsetX; tilesetShape.y += layer->offsetY; i32 colliderIdx = y * map->width + x; BzTileCollider *collider = map->colliderMap + colliderIdx; for (i32 sIdx = 0; sIdx < BZ_MAP_COLLIDER_DEPTH; sIdx++) { BzTileShape *shape = collider->shapes + sIdx; if (shape->type == BZ_TILE_SHAPE_NONE) { *shape = tilesetShape; break; } } } } } } BzTileMap bzTileMapCreate(const BzTileMapDesc *desc) { BzTileMap map = {}; // Auto detect tileset count. for (i32 i = 0; i < BZ_MAX_MAP_TILESETS; i++) { if (!desc->tilesets[i].isValid) break; map.tilesetCount++; } for (i32 i = 0; i < map.tilesetCount; i++) { map.tilesets[i] = desc->tilesets[i]; } cute_tiled_map_t *cuteMap = cute_tiled_load_map_from_file(desc->path, NULL); map.backgroundColor = GetColor(cuteMap->backgroundcolor); map.width = cuteMap->width; map.height = cuteMap->height; map.tileWidth = cuteMap->tilewidth; map.tileHeight = cuteMap->tileheight; cute_tiled_layer_t *cuteLayer = cuteMap->layers; while (cuteLayer) { BZ_ASSERT(map.layerCount < BZ_MAX_MAP_LAYERS); BZ_ASSERT(map.objectLayerCount < BZ_MAX_MAP_LAYERS); // Find slot i32 slot = -1; for (i32 i = 0; i < BZ_MAX_MAP_LAYERS; i++) { const BzTileLayerDesc *layerDesc = desc->layers + i; const BzTileObjectsDesc *objectsDesc = desc->objectLayers + i; if (layerDesc->name && strcmp(layerDesc->name, cuteLayer->name.ptr) == 0 || objectsDesc->name && strcmp(objectsDesc->name, cuteLayer->name.ptr) == 0) { slot = i; break; } else if (!layerDesc->name && !objectsDesc->name) { break; } } if (slot == -1) { cuteLayer = cuteLayer->next; continue; } if (cuteLayer->data) { BzTileLayer *layer = map.layers + slot; const BzTileLayerDesc *layerDesc = desc->layers + slot; handleTileLayer(layer, cuteLayer); if (layerDesc->handler) layerDesc->handler(layer); map.layerCount++; } else { BzTileObjectLayer *objectLayer = map.objectLayers + slot; const BzTileObjectsDesc *objectLayerDesc = desc->objectLayers + slot; BzStringHashFunc *hashFunc = objectLayerDesc->hashFunc; if (!hashFunc) hashFunc = bzStringDefaultHash; handleTileObjectLayer(objectLayer, cuteLayer, hashFunc); if (objectLayerDesc->handler) objectLayerDesc->handler(objectLayer); map.objectLayerCount++; } cuteLayer = cuteLayer->next; } cute_tiled_tileset_t *cuteTileset = cuteMap->tilesets; int tilesetCount = 0; for (; cuteTileset; cuteTileset = cuteTileset->next) tilesetCount++; BZ_ASSERT(tilesetCount == map.tilesetCount); cuteTileset = cuteMap->tilesets; for (i32 i = 0; i < map.tilesetCount; i++) { BzTileset *tileset = map.tilesets + i; tileset->startID = cuteTileset->firstgid; cuteTileset = cuteTileset->next; } // Assign tilesets to layers for (i32 i = 0; i < map.layerCount; i++) { BzTileLayer *layer = map.layers + i; layer->tilesetIdx = -1; for (i32 j = map.tilesetCount - 1; j >= 0; j--) { BzTileset *tileset = map.tilesets + j; if (tileset->startID >= layer->minData && tileset->startID <= layer->maxData) { layer->tilesetIdx = j; break; } } } cute_tiled_free_map(cuteMap); createColliders(&map); map.isValid = true; return map; } void bzTileMapDestroy(BzTileMap *tilemap) { for (i32 i = 0; i < tilemap->layerCount; i++) { BzTileLayer *layer = tilemap->layers + i; bzFree(layer->data); layer->data = NULL; layer->dataCount = 0; } *tilemap = BZ_TILEMAP_INVALID; } static void drawLayer(BzTileLayer *layer, BzTileset *tileset) { if (!tileset) return; if (layer->minData == layer->maxData) return; Vector2 drawPos = {layer->offsetX, layer->offsetY}; for (i32 y = 0; y < layer->height; y++) { for (i32 x = 0; x < layer->width; x++) { i16 tile = bzTileLayerGetTile(layer, x, y); if (tile - tileset->startID != -1) { Rectangle rec = bzTilesetGetTileRegion(tileset, tile); DrawTextureRec(tileset->tiles, rec, drawPos, WHITE); } drawPos.x += (float) tileset->tileWidth; } drawPos.x = layer->offsetX; drawPos.y += (float) tileset->tileHeight; } } void bzTileMapDraw(BzTileMap *map) { for (i32 i = 0; i < map->layerCount; i++) { BzTileLayer *layer = map->layers + i; BzTileset *tileset = NULL; if (layer->tilesetIdx != -1) { tileset = map->tilesets + layer->tilesetIdx; } drawLayer(map->layers + i, tileset); } } void bzTileMapDrawColliders(BzTileMap *map) { Color color = RED; color.a = 150; for (i32 y = 0; y < map->height; y++) { for (i32 x = 0; x < map->width; x++) { i32 idx = y * map->width + x; BzTileCollider collider = map->colliderMap[idx]; for (i32 i = 0; i < BZ_MAP_COLLIDER_DEPTH; i++) { BzTileShape shape = collider.shapes[i]; if (shape.type == BZ_TILE_SHAPE_NONE) break; i32 posX = x * map->tileWidth + shape.x; i32 posY = y * map->tileHeight + shape.y; f32 sizeX = shape.sizeX; f32 sizeY = shape.sizeY; switch (shape.type) { case BZ_TILE_SHAPE_NONE: default: break; case BZ_TILE_SHAPE_RECT: DrawRectangleLines(posX, posY, sizeX, sizeY, color); break; case BZ_TILE_SHAPE_ELLIPSE: sizeX *= 0.5f; sizeY *= 0.5f; DrawEllipseLines(posX + sizeX, posY + sizeY, sizeX, sizeY, color); break; } } } } } bool bzTileMapCanPlace(BzTileMap *map, i32 tileX, i32 tileY, i32 sizeX, i32 sizeY) { for (i32 y = tileY; y < tileY + sizeY; y++) { for (i32 x = tileX; x < tileX + sizeX; x++) { for (i32 i = 0; i < map->layerCount; i++) { BzTileLayer *layer = map->layers + i; if (layer->tilesetIdx == -1) continue; BzTileset *tileset = map->tilesets + layer->tilesetIdx; i16 tile = bzTileLayerGetTile(layer, x, y); BzTileShape collider = bzTilesetGetTileCollider(tileset, tile); if (collider.type != BZ_TILE_SHAPE_NONE) return false; } } } return true; } BzTileCollider bzTileMapGetCollider(BzTileMap *map, i32 x, i32 y) { i32 idx = y * map->width + x; BZ_ASSERT(idx < 0 && idx < map->collidersCount); return map->colliderMap[idx]; }