178 lines
5.2 KiB
C
178 lines
5.2 KiB
C
#include "level.h"
|
|
#include "game_world.h"
|
|
#include "physics_entity.h"
|
|
#include "physics_world.h"
|
|
#include "strutil.h"
|
|
#include "variant.h"
|
|
#include "ctype.h"
|
|
|
|
static Dictionary internal_deserializers = {
|
|
.list = { .data = NULL, .len = 0, .cap = 0, .element_size = 0 },
|
|
.element_size = 0
|
|
};
|
|
|
|
static int is_open_bracket(int c) { return c == '{'; }
|
|
static int is_close_bracket(int c) { return c == '}'; }
|
|
static int is_colon(int c) { return c == ':'; }
|
|
static int ends_parameter(int c) { return c == '}' || c == ','; }
|
|
|
|
int level_init() {
|
|
internal_deserializers = dictionary_from_type(DeserializeFn);
|
|
return 0;
|
|
}
|
|
|
|
int level_clean() {
|
|
dictionary_empty(&internal_deserializers);
|
|
return 0;
|
|
}
|
|
|
|
int level_register_spawner(const char* object_tag, DeserializeFn spawn_function) {
|
|
dictionary_set_value(DeserializeFn, &internal_deserializers, object_tag, spawn_function);
|
|
return 0;
|
|
}
|
|
|
|
static inline
|
|
void spawn_object(DeserializeFn spawner, Dictionary* args) {
|
|
BehaviourEntity entity = spawner(args);
|
|
game_world_add_entity(entity);
|
|
if(TC_MIRRORS(entity, PhysicsEntity))
|
|
physics_world_add_entity(TC_CAST(entity, PhysicsEntity));
|
|
}
|
|
|
|
static inline
|
|
long get_until(FILE* fp, char* out_buf, size_t buf_len, CharPredFn predfn) {
|
|
if(feof(fp)) return 0;
|
|
int c;
|
|
char* write = out_buf;
|
|
long count = 0;
|
|
while(c != EOF) {
|
|
// don't overflow
|
|
if(out_buf != NULL && count + 1 >= buf_len)
|
|
return count;
|
|
// fetch next character
|
|
c = fgetc(fp);
|
|
// check if this is the desired character
|
|
if(predfn(c)) {
|
|
if(write != NULL) {
|
|
*write = c;
|
|
if(count + 2 < buf_len) *(write + 1) = '\0';
|
|
}
|
|
return count + 1;
|
|
}
|
|
// skip space characters (unless search is a space character, which is handled above)
|
|
if(isspace(c)) continue;
|
|
// write and increment
|
|
if(write != NULL) {
|
|
*write = c;
|
|
++write;
|
|
}
|
|
++count;
|
|
} // EOF reached
|
|
// write null terminator in place of EOF if possible
|
|
if(write != NULL)
|
|
*(write-1) = '\0';
|
|
return count + 1;
|
|
}
|
|
|
|
static inline
|
|
long get_key(FILE* fp, char* out, size_t out_size) {
|
|
long length = get_until(fp, out, out_size, is_colon);
|
|
if(strfirst(out, out + length, '}') == 0)
|
|
return 0;
|
|
if(feof(fp))
|
|
return -1;
|
|
length--;
|
|
out[length] = '\0';
|
|
return length;
|
|
}
|
|
|
|
static inline
|
|
Variant get_value(FILE* fp, char* buffer, size_t buffer_size, int* out_end_of_object) {
|
|
long length = 0;
|
|
*out_end_of_object = 0;
|
|
do {
|
|
length += get_until(fp, buffer+length, buffer_size-length, ends_parameter);
|
|
if (length <= 0)
|
|
return UndefinedVariant();
|
|
*out_end_of_object = strfirst(buffer, buffer + length, '}') != -1;
|
|
} while(strcount(buffer, buffer+length, '(') != strcount(buffer, buffer+length, ')') && !(*out_end_of_object));
|
|
length--;
|
|
buffer[length] = '\0';
|
|
return variant_from_str(buffer);
|
|
}
|
|
|
|
static inline
|
|
int peek_next_non_blank(FILE* fp) {
|
|
long position = ftell(fp);
|
|
int c;
|
|
do {
|
|
c = fgetc(fp);
|
|
} while(isspace(c));
|
|
fseek(fp, position, SEEK_SET);
|
|
return c;
|
|
}
|
|
|
|
static inline
|
|
int load_args(FILE* fp, Dictionary* out, char* buffer, size_t buffer_size) {
|
|
char* key;
|
|
long length;
|
|
int is_end;
|
|
do {
|
|
length = get_key(fp, buffer, buffer_size);
|
|
if(length == -1) // detect EOF before end of key
|
|
return -1;
|
|
key = malloc(length+1);
|
|
strncpy(key, buffer, length);
|
|
Variant var = get_value(fp, buffer, buffer_size, &is_end);
|
|
dictionary_set_value(Variant, out, key, var);
|
|
free(key);
|
|
if(peek_next_non_blank(fp) == '}') {
|
|
get_until(fp, NULL, 0, ends_parameter);
|
|
break;
|
|
}
|
|
} while(!is_end);
|
|
return 0;
|
|
}
|
|
|
|
int level_parse_file(FILE* fp) {
|
|
// initialize a buffer string
|
|
size_t length = 81;
|
|
char buffer[length];
|
|
char* obj_tag = NULL;
|
|
Dictionary args = dictionary_new(sizeof(Variant));
|
|
DeserializeFn spawner = NULL;
|
|
do {
|
|
// read the next line of the level file
|
|
length = get_until(fp, buffer, sizeof(buffer)-1, is_open_bracket);
|
|
--length;
|
|
buffer[length] = '\0';
|
|
long start = strfirst(buffer, buffer+length, '}') + 1;
|
|
// initialize object tag buffer
|
|
obj_tag = malloc((length-start)+1);
|
|
obj_tag[length - start] = '\0';
|
|
strncpy(obj_tag, buffer + start, length - start);
|
|
// find the spawn function
|
|
if(dictionary_try_get(&internal_deserializers, obj_tag, &spawner)) {
|
|
// load arguments from file and spawn object
|
|
load_args(fp, &args, buffer, sizeof(buffer));
|
|
spawn_object(spawner, &args);
|
|
// clean up for next loop
|
|
free(obj_tag);
|
|
dictionary_empty(&args);
|
|
} else {
|
|
// clean up for next loop
|
|
free(obj_tag);
|
|
dictionary_empty(&args);
|
|
obj_tag = NULL;
|
|
// return -1;
|
|
}
|
|
} while(!feof(fp));
|
|
return 0;
|
|
}
|
|
|
|
int level_load_file(const char* path) {
|
|
FILE* fp = fopen("assets/test.sc", "r");
|
|
level_parse_file(fp);
|
|
return 0;
|
|
}
|