334 lines
11 KiB
C
334 lines
11 KiB
C
#include "map.h"
|
|
|
|
#include "../core/memory.h"
|
|
#include "../math/vec2i.h"
|
|
|
|
#include <cute_tiled.h>
|
|
#include <string.h>
|
|
|
|
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->data) continue;
|
|
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)) {
|
|
bzFree(layer->data);
|
|
layer->data = NULL;
|
|
layer->dataCount = 0;
|
|
}
|
|
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)) {
|
|
bzFree(objectLayer->objects);
|
|
objectLayer->objects = NULL;
|
|
objectLayer->objectCount = 0;
|
|
}
|
|
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;
|
|
if (layer->data) {
|
|
bzFree(layer->data);
|
|
layer->data = NULL;
|
|
layer->dataCount = 0;
|
|
}
|
|
}
|
|
for (i32 i = 0; i < tilemap->objectLayerCount; i++) {
|
|
BzTileObjectLayer *objectLayer = tilemap->objectLayers + i;
|
|
if (objectLayer->objects) {
|
|
bzFree(objectLayer->objects);
|
|
objectLayer->objects = NULL;
|
|
objectLayer->objectCount = 0;
|
|
}
|
|
}
|
|
|
|
bzFree(tilemap->colliderMap);
|
|
tilemap->collidersCount = 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;
|
|
}
|
|
|
|
}
|
|
|
|
static void drawObjectLayer(BzTileObjectLayer *objectLayer) {
|
|
Color color = ORANGE;
|
|
for (int i = 0; i < objectLayer->objectCount; i++) {
|
|
BzTileShape shape = objectLayer->objects[i].shape;
|
|
switch (shape.type) {
|
|
case BZ_TILE_SHAPE_NONE:
|
|
break;
|
|
case BZ_TILE_SHAPE_POINT:
|
|
DrawCircle(shape.x, shape.y, 2.0f, color);
|
|
break;
|
|
case BZ_TILE_SHAPE_RECT:
|
|
DrawRectangle(shape.x, shape.y, shape.sizeX, shape.sizeY, color);
|
|
break;
|
|
case BZ_TILE_SHAPE_ELLIPSE:
|
|
DrawEllipse(shape.x, shape.y, shape.sizeX, shape.sizeY, color);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void bzTileMapDraw(BzTileMap *map) {
|
|
for (i32 i = 0; i < map->layerCount; i++) {
|
|
BzTileLayer *layer = map->layers + i;
|
|
if (!layer->data) continue;
|
|
BzTileset *tileset = NULL;
|
|
if (layer->tilesetIdx != -1) {
|
|
tileset = map->tilesets + layer->tilesetIdx;
|
|
}
|
|
drawLayer(map->layers + i, tileset);
|
|
}
|
|
|
|
for (i32 i = 0; i < map->objectLayerCount; i++) {
|
|
BzTileObjectLayer *objectLayer = map->objectLayers + i;
|
|
if (!objectLayer->objects) continue;
|
|
drawObjectLayer(objectLayer);
|
|
}
|
|
}
|
|
|
|
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:
|
|
DrawEllipseLines(posX, posY, sizeX, sizeY, color);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
BzTileCollider bzTileMapGetCollider(BzTileMap *map, i32 x, i32 y) {
|
|
i32 idx = y * map->width + x;
|
|
if (idx < 0 || idx >= map->collidersCount) {
|
|
return (BzTileCollider) {{BZ_TILE_SHAPE_NONE}};
|
|
}
|
|
return map->colliderMap[idx];
|
|
}
|