#include #include #include #include #include "loader.h" #include "main.h" #include "tilemap.h" #include "collision.h" struct tilemap *tilemap = NULL, *next_tilemap = NULL; #define PACKED __attribute__((__packed__)) uint8_t const MAGIC[4] = "TMv1"; struct PACKED sets { uint8_t magic[4]; uint32_t tilesets_count; uint32_t wang_count; }; struct PACKED map { struct { uint8_t r, g, b, a; } backdrop; uint32_t width; uint32_t height; uint32_t layers; uint32_t middleground; uint8_t tiles[]; }; void tilemap_background(struct tilemap *tilemap, int x, int y, int w, int h) { SDL_SetRenderDrawColor(renderer, tilemap->backdrop.r, tilemap->backdrop.g, tilemap->backdrop.b, tilemap->backdrop.a); SDL_RenderFillRect(renderer, &(SDL_Rect) {0, 0, w, h}); for (int i = 0; i < tilemap->middleground; i++) { SDL_RenderCopy(renderer, tilemap->tilemaps[i], &(SDL_Rect) {x * tilemap->parallax[i].x, y * tilemap->parallax[i].y, w, h}, &(SDL_Rect) {0, 0, w, h}); } } void tilemap_foreground(struct tilemap *tilemap, int x, int y, int w, int h) { for (int i = tilemap->middleground; i < tilemap->layers; i++) { SDL_RenderCopy(renderer, tilemap->tilemaps[i], &(SDL_Rect) {x * tilemap->parallax[i].x, y * tilemap->parallax[i].y, w, h}, &(SDL_Rect) {0, 0, w, h}); } } void tilemap_free(struct tilemap *tilemap) { SDL_DestroyTexture(tilemap->tileset); SDL_DestroyTexture(tilemap->wang_tileset); free(tilemap->parallax); free(tilemap->collision); for (int i = 0; i < tilemap->layers; i++) { SDL_DestroyTexture(tilemap->tilemaps[i]); } free(tilemap->tilemaps); free(tilemap); } struct tilemap *tilemap_load(void *data, size_t size) { size_t const insize = size; struct sets *const sets = data; data = sets + 1; size -= sizeof (struct sets); if (memcmp(sets->magic, MAGIC, sizeof (MAGIC)) != 0) { fprintf(stderr, "error: unsupported level format (got '%.4s' expected '%.4s')\n", sets->magic, MAGIC); return NULL; } struct tilemap *tilemap = malloc(sizeof (struct tilemap)); if (tilemap == NULL) { return NULL; } tilemap->tileset = SDL_CreateTexture(renderer, SDL_PIXELTYPE_UNKNOWN, SDL_TEXTUREACCESS_TARGET, 128, 128); tilemap->wang_tileset = SDL_CreateTexture(renderer, SDL_PIXELTYPE_UNKNOWN, SDL_TEXTUREACCESS_TARGET, 128, 128); SDL_SetTextureBlendMode(tilemap->wang_tileset, SDL_BLENDMODE_BLEND); tilemap->layers = 0; tilemap->parallax = NULL; tilemap->collision = NULL; tilemap->tilemaps = NULL; char *str = data; int y = 0; SDL_SetRenderTarget(renderer, tilemap->tileset); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); SDL_RenderClear(renderer); collision_T collision[256] = {0}; for (uint32_t count = sets->tilesets_count; count > 0; count--) { int height = *(uint32_t *) str; str += sizeof (uint32_t); size_t len = strnlen(str, size); if (len == size) { goto fail; } void *tex = res_get_texture(str).data; struct blob col = res_get_collision(str); SDL_RenderCopy(renderer, tex, &(SDL_Rect) {0, 0, 128, height}, &(SDL_Rect) {0, y, 128, height}); if (col.size + y * 2 > 0xf0) { fprintf(stderr, "warn: '%s' overflows tile properties\n", str); col.size = 0xf0 - y * 2; } memcpy(collision + y * 2, col.data, col.size * sizeof (collision_T)); y += height; str += len + 1; size -= len + 4 + 1; } SDL_SetRenderTarget(renderer, tilemap->wang_tileset); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); SDL_RenderClear(renderer); int wangs = 0; if (sets->wang_count) { int height = *(uint32_t *) str; wangs = height / 8; str += sizeof (uint32_t); size_t len = strnlen(str, size); if (len == size) { tilemap_free(tilemap); return NULL; } void *tex = res_get_texture(str).data; struct blob col = res_get_collision(str); SDL_RenderCopy(renderer, tex, &(SDL_Rect) {0, 0, 128, height}, &(SDL_Rect) {0, 0, 128, height}); if (col.size > 16) { fprintf(stderr, "warn: '%s' overflows tile properties\n", str); col.size = 16; } memcpy(collision + 0xf0, col.data, col.size * sizeof (collision_T)); str += len + 1; size -= len + 4 + 1; } struct map const *const map = (void *) str; if (map->width * map->height * map->layers > size - sizeof (struct map)) { goto fail; } tilemap->backdrop.r = map->backdrop.r; tilemap->backdrop.g = map->backdrop.g; tilemap->backdrop.b = map->backdrop.b; tilemap->backdrop.a = map->backdrop.a; tilemap->width = map->width; tilemap->height = map->height; tilemap->layers = map->layers; tilemap->middleground = map->middleground; tilemap->tilemaps = malloc(sizeof (void *) * tilemap->layers); if (tilemap->middleground > tilemap->layers) { goto fail; } else if (tilemap->middleground == 0) { fputs("warn: map has no middle ground\n", stderr); tilemap->collision = calloc(tilemap->width, tilemap->height * sizeof (collision_T)); } else { tilemap->collision = malloc(tilemap->width * tilemap->height * sizeof (collision_T)); size_t const ll = (tilemap->middleground - 1) * map->width * map->height; for (size_t i = 0; i < tilemap->width * tilemap->height; i++) { tilemap->collision[i] = collision[map->tiles[i + ll]]; } } for (int l = 0; l < tilemap->layers; l++) { SDL_Texture *layer = SDL_CreateTexture(renderer, SDL_PIXELTYPE_UNKNOWN, SDL_TEXTUREACCESS_TARGET, map->width * 8, map->height * 8); SDL_SetTextureBlendMode(layer, SDL_BLENDMODE_BLEND); SDL_SetRenderTarget(renderer, layer); size_t const ll = l * map->width * map->height; for (int y = 0; y < map->height; y++) { size_t const yy = y * map->width; for (int x = 0; x < map->width; x++) { unsigned tile = map->tiles[x + yy + ll]; int tileX = tile & 0x0f; int tileY = tile >> 4; SDL_RenderCopy(renderer, tilemap->tileset, &(SDL_Rect) {tileX * 8, tileY * 8, 8, 8}, &(SDL_Rect) {x * 8, y * 8, 8, 8}); } } for (int tile = 0xf0; tile < 0xf0 + wangs; tile++) { for (int y = 0; y < map->height - 1; y++) { size_t const yy = y * map->width; for (int x = 0; x < map->width - 1; x++) { int tl = map->tiles[x + yy + ll] == tile; int tr = map->tiles[x + 1 + yy + ll] == tile; int bl = map->tiles[x + yy + map->width + ll] == tile; int br = map->tiles[x + 1 + yy + map->width + ll] == tile; int tileX = tl << 0 | tr << 1 | bl << 2 | br << 3; int tileY = tile & 0x0f; SDL_RenderCopy(renderer, tilemap->wang_tileset, &(SDL_Rect) {tileX * 8, tileY * 8, 8, 8}, &(SDL_Rect) {x * 8 + 4, y * 8 + 4, 8, 8}); } } } tilemap->tilemaps[l] = layer; } float *parallax = (void *) (map->tiles + map->width * map->height * map->layers); size -= (char *) parallax - (char *) map; if (sizeof (struct parallax) * map->layers > size) { goto fail; } tilemap->parallax = malloc(sizeof (struct parallax) * map->layers); for (int i = 0; i < map->layers; i++) { tilemap->parallax[i].x = *parallax++; tilemap->parallax[i].y = *parallax++; } size -= sizeof (struct parallax) * map->layers; tilemap->input_bytes = insize - size; //fprintf(stderr, "size: %zuB remaining: %zuB total: %zuB\n", tilemap->input_bytes, size, insize); SDL_SetRenderTarget(renderer, NULL); return tilemap; fail: tilemap_free(tilemap); SDL_SetRenderTarget(renderer, NULL); return NULL; }