223 lines
7 KiB
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;
|
|
}
|