fencer/src/tileset.c

223 lines
7 KiB
C

#include "tileset.h"
#include "debug.h"
#include "spritesheet.h"
#include "assets.h"
#include "render.h"
#include "SDL2/SDL_image.h"
#include <cjson/cJSON.h>
struct TileDef {
size_t tid;
Sprite* sprite;
Shape* shape;
TileCollision collision;
};
struct Tileset {
size_t uid;
Spritesheet* atlas;
TileDef* tiledefs;
size_t tiledefs_len;
};
typedef struct TilesetStore {
Tileset* set;
size_t iid;
} TilesetStore;
static TilesetStore* _tilesets = NULL;
static size_t _tilesets_len = 0;
static size_t _tilesets_cap = 0;
static
int _internal_tilesets_resize_if_needed(size_t required_cap) {
if(required_cap < _tilesets_cap) {
return 1;
}
size_t needed = _tilesets_cap ? _tilesets_cap : 2;
while(needed < required_cap) {
needed *= 2;
}
TilesetStore* new = realloc(_tilesets, needed * sizeof(TilesetStore));
if(new == NULL) {
LOG_ERROR("Failed to resize tileset store array.");
return 0;
}
_tilesets_cap = needed;
_tilesets = new;
return 1;
}
static
void _internal_tileset_deallocate(void* self_void) {
Tileset* self = self_void;
TilesetStore* store = _tilesets;
for(; store < _tilesets + _tilesets_len; ++store) {
if(store->set == self) {
break;
}
}
size_t diff = _tilesets_len - (size_t)(store - _tilesets);
memmove(store, store+1, diff-1);
--_tilesets_len;
spritesheet_destroy(self->atlas);
free(self->tiledefs);
}
static
void _internal_tileset_store(Tileset* self) {
_internal_tilesets_resize_if_needed(_tilesets_len + 1);
_tilesets[_tilesets_len].set = self;
_tilesets[_tilesets_len].iid = self->uid;
}
Tileset* tileset_from_json(cJSON* json) {
// required uid, atlas path and grid size elements
cJSON* uid = cJSON_GetObjectItem(json, "uid");
ASSERT_RETURN(uid != NULL && cJSON_IsNumber(uid), NULL, "Failed to find uid while loading tileset");
cJSON* path = cJSON_GetObjectItem(json, "relPath");
ASSERT_RETURN(path != NULL && cJSON_IsString(path), NULL, "Failed to find relPath while loading tileset");
cJSON* gridsize = cJSON_GetObjectItem(json, "tileGridSize");
ASSERT_RETURN(gridsize != NULL && cJSON_IsNumber(gridsize), NULL, "Failed to find tileGridSize while loading tileset");
// Optional tags and custom data arrays
cJSON* tags = cJSON_GetObjectItem(json, "enumTags");
cJSON* custom_data = cJSON_GetObjectItem(json, "customData");
SDL_Surface* atlas_surface = IMG_Load(path->valuestring);
ASSERT_RETURN(atlas_surface != NULL, NULL, "Failed to load atlas image while loading tileset");
Tileset* self = malloc(sizeof(Tileset));
if(self == NULL) {
SDL_FreeSurface(atlas_surface);
RETURN_ERROR(NULL, "Failed to allocate space for new tileset.");
}
SDL_Texture* texture = SDL_CreateTextureFromSurface(g_renderer, atlas_surface);
if(texture == NULL) {
free(self);
SDL_FreeSurface(atlas_surface);
RETURN_ERROR(NULL, "Failed to convert atlas surface to texture");
}
IVector grid = {gridsize->valueint, gridsize->valueint};
_internal_tileset_store(self);
*self = (Tileset) {
.uid = uid->valueint,
.atlas = spritesheet_from_texture(texture, grid),
};
IVector resolution = spritesheet_get_resolution(self->atlas);
self->tiledefs_len = resolution.x / grid.x * resolution.y / grid.y;
self->tiledefs = malloc(self->tiledefs_len * sizeof(TileDef));
LOG_INFO("tiledefs_len: %zu", self->tiledefs_len);
LOG_INFO("resolution: %d, %d", resolution.x, resolution.y);
LOG_INFO("grid: %d, %d", grid.x, grid.y);
for(size_t tid = 0; tid < self->tiledefs_len; ++tid) {
self->tiledefs[tid] = (TileDef) {
.tid = tid,
.sprite = sprite_from_spritesheet(self->atlas, tid),
// default is no collision
.collision = TILE_COL_NONE,
.shape = NULL
};
sprite_set_origin(self->tiledefs[tid].sprite, ZeroVector);
}
SDL_FreeSurface(atlas_surface);
cJSON* tag;
cJSON_ArrayForEach(tag, tags) {
cJSON* ids = cJSON_GetObjectItem(tag, "tileIds");
cJSON* tagval = cJSON_GetObjectItem(tag, "enumValueId");
cJSON* id;
if(strcmp(tagval->valuestring, "fullrect") == 0) {
cJSON_ArrayForEach(id, ids) {
self->tiledefs[id->valueint].shape = shape_new_square(OneVector);
self->tiledefs[id->valueint].collision = TILE_COL_RECT;
}
} else if(strcmp(tagval->valuestring, "pointshape") == 0) {
cJSON_ArrayForEach(id, ids) {
self->tiledefs[id->valueint].collision = TILE_COL_SHAPE;
}
}
}
cJSON* data_tuple;
cJSON_ArrayForEach(data_tuple, custom_data) {
cJSON* id = cJSON_GetObjectItem(data_tuple, "tileId");
TileDef* def = self->tiledefs + id->valueint;
CHECK(def->collision == TILE_COL_SHAPE, "Shape with shape description has to have tile collision type set to 'pointshape'");
cJSON* data_string = cJSON_GetObjectItem(data_tuple, "data");
cJSON* data_json = cJSON_Parse(data_string->valuestring);
ASSERT_RETURN(data_json != NULL, self, "Failed to get shape data for tile of tid %d", id->valueint);
cJSON* shape_data = cJSON_GetObjectItem(data_json, "collisionShape");
CHECK(shape_data != NULL, "could not find shape data");
cJSON* vertex_json;
self->tiledefs[id->valueint].shape = shape_new(NULL, 0);
CHECK(def->shape != NULL, "Failed to create a shape for tiledef");
cJSON_ArrayForEach(vertex_json, shape_data) {
Vector vertex = json_array_to_vector(vertex_json);
shape_add_point(self->tiledefs[id->valueint].shape, vertex);
}
cJSON_Delete(data_json);
}
return self;
}
Tileset* tileset_load(size_t uid) {
for(TilesetStore* store = _tilesets; store < _tilesets + _tilesets_len; ++store) {
if(store->iid == uid) {
return store->set;
}
}
cJSON* defs = cJSON_GetObjectItem(g_levels_json, "defs");
ASSERT_RETURN(defs != NULL, NULL, "Failed to find defs element in levels json.");
cJSON* tilesets = cJSON_GetObjectItem(defs, "tilesets");
ASSERT_RETURN(tilesets != NULL, NULL, "Failed to find tileset defs region in levels json");
cJSON* tileset = NULL;
cJSON_ArrayForEach(tileset, tilesets) {
cJSON* tileset_uid = cJSON_GetObjectItem(tileset, "uid");
if(tileset_uid == NULL) continue;
LOG_INFO("finding uid: %d", tileset_uid->valueint);
if(uid == tileset_uid->valueint) {
break;
}
}
ASSERT_RETURN(tileset != NULL, NULL, "Failed to find a tileset with the uid matching %zu", uid);
return tileset_from_json(tileset);
}
void tileset_destroy(Tileset* self) {
_internal_tileset_deallocate(self);
}
TileDef* tileset_get_tiledef(Tileset* self, size_t t) {
return &self->tiledefs[t];
}
Sprite* tiledef_get_sprite(const TileDef* self) {
return self->sprite;
}
Shape* tiledef_get_shape(TileDef* self) {
return self->shape;
}