fencer/src/level.c
2023-11-06 21:16:21 +01:00

149 lines
4.1 KiB
C

#include "level.h"
#include "assets.h"
#include "debug.h"
#include "physics_world.h"
#include "tilemap.h"
#include <cjson/cJSON.h>
#include <stdio.h>
#define LEVEL_EXT ".ldtkl"
#define WORLD_EXT ".ldtk"
#define LEVEL_DIR "assets/levels/"
struct Level {
asset_id asset_id;
size_t level_iid;
Transform transform;
Tilemap** tilemaps;
size_t tilemaps_len;
Vector player_start;
};
void _internal_level_destroy(Level* self) {
for(size_t map_index = 0; map_index < self->tilemaps_len; ++map_index) {
physics_world_remove_map(self->tilemaps[map_index]);
tilemap_destroy(self->tilemaps[map_index]);
}
free(self);
}
static
size_t _count_layers(cJSON* layers, size_t* entities, size_t* automap) {
size_t ent_ = 0, auto_ = 0, tot_ = 0;
cJSON* layer;
cJSON_ArrayForEach(layer, layers) {
cJSON* type = cJSON_GetObjectItem(layer, "__type");
if(type == NULL) {
continue;
} else if(strcmp(type->valuestring, "AutoLayer") == 0) {
auto_++;
} else if(strcmp(type->valuestring, "Entities") == 0) {
ent_++;
}
tot_++;
}
if(entities != NULL)
*entities = ent_;
if(automap != NULL)
*automap = auto_;
return tot_;
}
Level* level_from_json(cJSON* json) {
// allocate a new level struct
Level* self = malloc(sizeof(Level));
ASSERT_RETURN(self != NULL, NULL, "Failed to allocate level object.");
*self = (Level) {
.asset_id = 0,
.transform = IdentityTransform
};
// fetch the instance id
cJSON* iid = cJSON_GetObjectItem(json, "iid");
if(iid != NULL && cJSON_IsNumber(iid))
self->level_iid = iid->valueint;
// get the level's layers
cJSON* layers = cJSON_GetObjectItem(json, "layerInstances");
// count the different kinds of layers
size_t entity_layer_count;
_count_layers(layers, &entity_layer_count, &self->tilemaps_len);
// allocate tilemap array
self->tilemaps = malloc(self->tilemaps_len * sizeof(Tilemap*));
Tilemap** next_tilemap = self->tilemaps;
if(layers != NULL && cJSON_IsArray(layers)) {
cJSON* layer;
cJSON_ArrayForEach(layer, layers) {
cJSON* type = cJSON_GetObjectItem(layer, "__type");
if(strcmp(type->valuestring, "AutoLayer") == 0) {
// load a tilemap as an autolayer
LOG_INFO("loading autolayer");
*next_tilemap = tilemap_from_autolayer(layer);
physics_world_add_map(*next_tilemap);
++next_tilemap;
} else if(strcmp(type->valuestring, "Entities")) {
// load entities
}
}
}
store_asset(Level_as_Asset(self));
return self;
}
Level* level_load(const char* level_id) {
// Convert the level ID to a filepath
size_t filename_len = strlen(LEVEL_DIR) + strlen(level_id) + strlen(LEVEL_EXT) + 1;
char* filename = malloc(filename_len);
sprintf(filename, "%s%s%s", LEVEL_DIR, level_id, LEVEL_EXT);
LOG_INFO("loading level %s at %s", level_id, filename);
cJSON* level = load_json_from_file(filename);
if(level == NULL) {
LOG_ERROR("Failed to load level from '%s'.", filename);
free(filename);
return NULL;
}
free(filename);
return level_from_json(level);
}
void level_destroy(Level* self) {
free_asset(self->asset_id);
}
void level_draw(Level* self) {
Tilemap** end = self->tilemaps + self->tilemaps_len;
for(Tilemap** map = self->tilemaps; map != end; ++map) {
tilemap_draw(*map, self->transform);
}
}
Tilemap* level_get_tilemap_layer(Level* self, size_t layer) {
ASSERT_RETURN(layer < self->tilemaps_len, NULL, "Layer index %zu out of range", layer);
return self->tilemaps[layer];
}
size_t level_get_tilemap_count(Level* self) {
return self->tilemaps_len;
}
asset_id level_get_asset_id(Level* self) {
LOG_INFO("(get) level asset id %zu", self->asset_id);
return self->asset_id;
}
void level_set_asset_id(Level* self, asset_id id) {
self->asset_id = id;
LOG_INFO("(set) level asset id %zu", self->asset_id);
}