From ce13cd4be59c0415877571d92aaea3fd0d5a7e49 Mon Sep 17 00:00:00 2001 From: zlago Date: Wed, 2 Oct 2024 20:39:39 +0200 Subject: entities --- src/entity.h | 2 +- src/funsinit.c | 7 ++ src/loader.c | 106 ++++++++++++++++++-- src/loader.h | 11 +++ src/main.c | 52 ++++++++++ src/player.c | 2 + utl/json2map/common.c | 21 ++++ utl/json2map/common.h | 4 + utl/json2map/main.c | 266 ++++++++++++++++++++++++++++++++++++++------------ 9 files changed, 395 insertions(+), 76 deletions(-) create mode 100644 src/funsinit.c diff --git a/src/entity.h b/src/entity.h index 52a2eac..354fbc1 100644 --- a/src/entity.h +++ b/src/entity.h @@ -1,6 +1,6 @@ #pragma once -#include "loader.h" +#include #define from_fixed(a) (a / 16) #define to_fixed(a) (a * 16) diff --git a/src/funsinit.c b/src/funsinit.c new file mode 100644 index 0000000..5d15cc4 --- /dev/null +++ b/src/funsinit.c @@ -0,0 +1,7 @@ +#include "entity.h" +#include "loader.h" +#include "main.h" + + +void funs_init(void) { +} diff --git a/src/loader.c b/src/loader.c index 6a50341..3b39bca 100644 --- a/src/loader.c +++ b/src/loader.c @@ -39,6 +39,22 @@ struct blob_collection { } items[]; } *textures = NULL, *maps = NULL, *collisions = NULL; +struct newfun_collection { + size_t count; + struct newfun_item { + struct entity *(*fun)(void); + name_T name; + } *items; +} newfuns = {0, NULL}; + +struct setfun_collection { + size_t count; + struct setfun_item { + int (*fun)(struct entity *const restrict self, char const *const restrict key, char const *const restrict value); + name_T name; + } *items; +} setfuns = {0, NULL}; + static int name_compare(name_T a, name_T b) { return strcmp(a, b); // return (a > b) - (a < b); @@ -124,24 +140,40 @@ void res_free_collision(void) { collisions = NULL; } +void res_free_newfun(void) { + for (size_t i = 0; i < newfuns.count; i++) { + free(newfuns.items[i].name); + } + free(newfuns.items); + newfuns.count = 0; +} + +void res_free_setfun(void) { + for (size_t i = 0; i < setfuns.count; i++) { + free(setfuns.items[i].name); + } + free(setfuns.items); + setfuns.count = 0; +} + static int res_compare_blobs(void const *a, void const *b) { struct blob_item const *aa = a, *bb = b; return name_compare(aa->name, bb->name); } +static int res_compare_newfuns(void const *a, void const *b) { + struct newfun_item const *aa = a, *bb = b; + return name_compare(aa->name, bb->name); +} + +static int res_compare_setfuns(void const *a, void const *b) { + struct setfun_item const *aa = a, *bb = b; + return name_compare(aa->name, bb->name); +} + static struct blob res_get_blob(name_T const name, struct blob_collection *collection) { struct blob_item new = {.name = name}; struct blob_item *dst = bsearch(&new, collection->items, collection->count, sizeof (struct blob_item), res_compare_blobs); - #if 0 - fprintf(stderr, "== chunk %zu ==\n", dst - collection->items); - for (int i = 0; i < collection->count; i++) { - fprintf(stderr, "= %s, %p\n", collection->items[i].name, collection->items[i].blob.data); - } - if (dst != NULL) - fprintf(stderr, "== value %p ==\n", dst->blob.data); - else - fprintf(stderr, "== not found ==\n"); - #endif if (dst == NULL) { return (struct blob) {.data = NULL, .size = 0}; } @@ -160,6 +192,24 @@ struct blob res_get_collision(name_T const name) { return res_get_blob(name, collisions); } +struct entity *(*res_get_newfun(name_T const name))(void) { + struct newfun_item new = {.name = name}; + struct newfun_item *dst = bsearch(&new, newfuns.items, newfuns.count, sizeof (struct newfun_item), res_compare_newfuns); + if (dst == NULL) { + return NULL; + } + return dst->fun; +} + +int (*res_get_setfun(name_T const name))(struct entity *const restrict self, char const *const restrict key, char const *const restrict value) { + struct setfun_item new = {.name = name}; + struct setfun_item *dst = bsearch(&new, setfuns.items, setfuns.count, sizeof (struct setfun_item), res_compare_setfuns); + if (dst == NULL) { + return NULL; + } + return dst->fun; +} + static struct blob_collection *res_push_blob(struct blob *blob, name_T name, struct blob_collection *collection, void (*freeFunc)(void *)) { int found; struct blob_item new = {.blob = *blob, .name = name}; @@ -206,6 +256,42 @@ void res_push_collision(struct blob *blob, name_T name) { collisions = res_push_blob(blob, name, collisions, blob_free_func); } +void res_push_newfun(struct entity *(*newfun)(void), name_T name) { + int found; + struct newfun_item new = {.fun = newfun, .name = name}; + struct newfun_item *dst = bsearchinsertposition(&new, newfuns.items, newfuns.count, sizeof (struct newfun_item), res_compare_newfuns, &found); + if (found) { + fprintf(stderr, "warn: name collision: %s\n", new.name); + free(dst->name); + memcpy(dst, &new, sizeof (struct newfun_item)); + } else { + size_t index = dst - newfuns.items; // hack + newfuns.count++; + newfuns.items = realloc(newfuns.items, sizeof (struct newfun_item) * newfuns.count); + dst = newfuns.items + index; // hack for the struct having been reallocated + memmove(dst + 1, dst, sizeof (struct newfun_item) * (newfuns.count - 1 - (dst - newfuns.items))); + memcpy(dst, &new, sizeof (struct newfun_item)); + } +} + +void res_push_setfun(int (*setfun)(struct entity *const restrict self, char const *const restrict key, char const *const restrict value), name_T name) { + int found; + struct setfun_item new = {.fun = setfun, .name = name}; + struct setfun_item *dst = bsearchinsertposition(&new, setfuns.items, setfuns.count, sizeof (struct setfun_item), res_compare_setfuns, &found); + if (found) { + fprintf(stderr, "warn: name collision: %s\n", new.name); + free(dst->name); + memcpy(dst, &new, sizeof (struct setfun_item)); + } else { + size_t index = dst - setfuns.items; // hack + setfuns.count++; + setfuns.items = realloc(setfuns.items, sizeof (struct setfun_item) * setfuns.count); + dst = setfuns.items + index; // hack for the struct having been reallocated + memmove(dst + 1, dst, sizeof (struct setfun_item) * (setfuns.count - 1 - (dst - setfuns.items))); + memcpy(dst, &new, sizeof (struct setfun_item)); + } +} + int loadResources(char *filename) { // open the file FILE *zip = fopen(filename, "rb"); diff --git a/src/loader.h b/src/loader.h index b1563bb..016b8b2 100644 --- a/src/loader.h +++ b/src/loader.h @@ -1,5 +1,8 @@ #pragma once +#include +#include "entity.h" + typedef char * name_T; struct blob { @@ -21,4 +24,12 @@ void res_init_collision(void); void res_free_collision(void); struct blob res_get_collision(name_T const name); +void res_free_newfun(void); +void res_push_newfun(struct entity *(*newfun)(void), name_T name); +struct entity *(*res_get_newfun(name_T const name))(void); + +void res_free_setfun(void); +void res_push_setfun(int (*setfun)(struct entity *const restrict self, char const *const restrict key, char const *const restrict value), name_T name); +int (*res_get_setfun(name_T const name))(struct entity *const restrict self, char const *const restrict key, char const *const restrict value); + int loadResources(char *filename); diff --git a/src/main.c b/src/main.c index 6b195b4..8fc400f 100644 --- a/src/main.c +++ b/src/main.c @@ -39,6 +39,8 @@ struct entity player[1] = {0}; struct entity *player_new(void); int player_property(struct entity *const restrict entity, char const *const restrict property, char const *const restrict value); +void funs_init(void); + int main(int const argc, char *const *const argv) { struct option opts[] = { {"help", 0, NULL, 'h'}, @@ -156,6 +158,7 @@ int main(int const argc, char *const *const argv) { { res_init(); + funs_init(); if (optind == argc) { void *a = util_executableRelativePath("assets.res", *argv, 0); puts(a); @@ -175,6 +178,55 @@ int main(int const argc, char *const *const argv) { } struct blob map = res_get_map("untitled"); tilemap = tilemap_load(map.data, map.size); + char *a = (char *) map.data + tilemap->input_bytes; + map.size -= tilemap->input_bytes; + while (map.size) { + size_t len; + if ((len = strnlen(a, map.size)) == map.size) { + return 1; // hack + } + char *name = a; + puts(name); + struct entity *(*fun)(void) = res_get_newfun(name); + a += len + 1; + map.size -= len + 1; + if (fun) { + struct entity *entity = fun(); + while (1) { + if ((len = strnlen(a, map.size)) == map.size) { + return 1; // hack + } else if (len == 0) { + a++; + map.size--; + break; + } + char *key = a; + a += len + 1; + map.size -= len + 1; + if ((len = strnlen(a, map.size)) == map.size) { + return 1; // hack + } + char *value = a; + a += len + 1; + map.size -= len + 1; + if (res_get_setfun(name)(entity, key, value)) { + return 1; // hack + } + } + } else { + while (1) { // hack + if ((len = strnlen(a, map.size)) == map.size) { // hack + return 1; // hack + } else if (len == 0) { + a++; + map.size--; + break; + } + a += len + 1; + map.size -= len + 1; + } + } + } //memmem(map.data + tilemap->byte_count, map.size - tilemap->byte_count, (char []) {0, 0}, 2); printf("load_tilemap %p\n", (void *) tilemap); SDL_SetRenderTarget(renderer, NULL); diff --git a/src/player.c b/src/player.c index 9efd0ac..b23b2f0 100644 --- a/src/player.c +++ b/src/player.c @@ -1,5 +1,6 @@ #include "main.h" #include "entity.h" +#include "loader.h" #include "input.h" #include "tilemap.h" #include @@ -119,6 +120,7 @@ void anim(struct entity *self, unsigned anim) { static int player_update(struct entity *self) { if (self->hp <= 0) { + self->state = 0; return 1; } switch (self->state) { diff --git a/utl/json2map/common.c b/utl/json2map/common.c index c401fc1..8c2c4c2 100644 --- a/utl/json2map/common.c +++ b/utl/json2map/common.c @@ -1,7 +1,28 @@ #include #include +#include #include "common.h" +struct blob blob_new(void) { + return (struct blob) {.data = NULL, .size = 0}; +} + +void blob_append(struct blob *const blob, void const *const data, size_t const size) { + char *new = realloc(blob->data, blob->size + size); + if (new == NULL) { + abort(); + } + memcpy(new + blob->size, data, size); + blob->data = new; + blob->size += size; +} + +void blob_free(struct blob *const blob) { + free(blob->data); + blob->data = NULL; + blob->size = 0; +} + struct blob load_file(char const *const name) { const size_t START_SIZE = 8192; FILE *file = fopen(name, "rb"); diff --git a/utl/json2map/common.h b/utl/json2map/common.h index 73e3240..e5a7590 100644 --- a/utl/json2map/common.h +++ b/utl/json2map/common.h @@ -5,4 +5,8 @@ struct blob { size_t size; }; +struct blob blob_new(void); +void blob_append(struct blob *const blob, void const *const data, size_t const size); +void blob_free(struct blob *const blob); + struct blob load_file(char const *const name); diff --git a/utl/json2map/main.c b/utl/json2map/main.c index 684e0ed..9ffa9b9 100644 --- a/utl/json2map/main.c +++ b/utl/json2map/main.c @@ -21,7 +21,7 @@ struct PACKED sets { }; struct PACKED map { - struct { + struct color { uint8_t r, g, b, a; } backdrop; uint32_t width; @@ -91,6 +91,56 @@ size_t safe_write(void const *restrict buf, size_t size, FILE *restrict stream) return fwrite(buf, 1, size, stream); } +int string_to_color(struct color *const restrict color, char const *const restrict string) { + if (string[0] != '#') { + fprintf(stderr, "color %s: missing a hash\n", string); + return 1; + } + char *end = NULL; + unsigned const value = strtoul(string + 1, &end, 16); + size_t const len = strlen(string); + if (end != string + len) { + fprintf(stderr, "color %s: not a color string\n", string); + return 1; + } + switch (len) { + case 9: + color->a = value >> 24; + break; + case 7: + color->a = 0xff; + break; + default: + fprintf(stderr, "color %s: not a color string\n", string); + return 1; + } + color->r = value >> 16; + color->g = value >> 8; + color->b = value >> 0; + return 0; +} + +// will turn, say, example/file.name into just file +// or another/example/file.with.multiple.dots into file.with.multiple +struct blob strip_filename(char *str) { + size_t length = strlen(str); + char *temp, *start = str; + while ((temp = memchr(start, '/', length))) { + temp++; + length -= temp - start; + start = temp; + } + char *data = start; + char *end = data + length; + while ((temp = memchr(data, '.', length))) { + end = temp; + temp++; + length -= temp - data; + data = temp; + } + return (struct blob) {.data = start, .size = end - start}; +} + int main(int argc, char **argv) { if (argc != 2 && argc != 3) { return EXIT_FAILURE; @@ -124,8 +174,7 @@ int main(int argc, char **argv) { char *wang_tileset = NULL; uint32_t wang_height; - char *tilesets_data = NULL; - size_t tilesets_size = 0; + struct blob tilesets_blob = blob_new(); struct sets counts = {MAGIC, 0, 0}; cJSON_ArrayForEach(tileset, tilesets) { @@ -163,28 +212,13 @@ int main(int argc, char **argv) { } cJSON const *image = cJSON_GetObjectItemCaseSensitive(tileset, "image"); { - char const *const str = image->valuestring; - size_t start = 0, length = strlen(str); - for (size_t i = 0; i < length; i++) { - if (str[i] == '/') { - start = i + 1; - } - } - length -= start; - for (size_t i = length; i > 0; i--) { - if (str[start + i] == '.') { - length = i; - break; - } - } + struct blob fragment = strip_filename(image->valuestring); //printf("%.*s\n", (int) length, str + start); uint32_t height = cJSON_GetObjectItemCaseSensitive(tileset, "imageheight")->valueint; - tilesets_data = realloc(tilesets_data, tilesets_size + length + sizeof (uint32_t) + 1); - memcpy(tilesets_data + tilesets_size, &height, sizeof (uint32_t)); - memcpy(tilesets_data + tilesets_size + sizeof (uint32_t), str + start, length); - tilesets_data[tilesets_size + sizeof (uint32_t) + length] = 0; + blob_append(&tilesets_blob, &height, sizeof (uint32_t)); + blob_append(&tilesets_blob, fragment.data, fragment.size); + blob_append(&tilesets_blob, &(char) {0}, 1); //puts(tilesets_data + tilesets_size + sizeof (uint32_t)); - tilesets_size += length + sizeof (uint32_t) + 1; counts.tilesets_count++; } //puts(image->valuestring); @@ -196,24 +230,11 @@ int main(int argc, char **argv) { wang_continue:; } if (counts.wang_count == 1) { - char const *const str = wang_tileset; - size_t start = 0, length = strlen(str); - for (size_t i = 0; i < length; i++) { - if (str[i] == '/') { - start = i + 1; - } - } - length -= start; - for (size_t i = length; i > 0; i--) { - if (str[start + i] == '.') { - length = i; - break; - } - } + struct blob fragment = strip_filename(wang_tileset); //printf("%.*s\n", (int) length, str + start); - wang_tileset = malloc(length + 1); - memcpy(wang_tileset, str + start, length); - wang_tileset[length] = 0; + wang_tileset = malloc(fragment.size + 1); + memcpy(wang_tileset, fragment.data, fragment.size); + wang_tileset[fragment.size] = 0; //puts(wang_tileset); } @@ -235,29 +256,14 @@ int main(int argc, char **argv) { cJSON const *backdrop = cJSON_GetObjectItemCaseSensitive(json, "backgroundcolor"); if (cJSON_IsString(backdrop)) { - switch (strlen(backdrop->valuestring)) { - unsigned value; - case 9: - value = strtoul(backdrop->valuestring + 1, NULL, 16); - map.backdrop.r = value >> 16; - map.backdrop.g = value >> 8; - map.backdrop.b = value >> 0; - map.backdrop.a = value >> 24; - break; - case 7: - value = strtoul(backdrop->valuestring + 1, NULL, 16); - map.backdrop.r = value >> 16; - map.backdrop.g = value >> 8; - map.backdrop.b = value >> 0; - map.backdrop.a = 0xff; - break; - default: - fputs("incorrect backdrop color format\n", stderr); - return 1; + if (string_to_color(&map.backdrop, backdrop->valuestring)) { + return 1; } } //printf("sky: # %02x %02x %02x %02x\n", map.backdrop.r, map.backdrop.g, map.backdrop.b, map.backdrop.a); + struct blob entities = blob_new(); + cJSON const *layers = cJSON_GetObjectItemCaseSensitive(json, "layers"); cJSON const *layer = NULL; cJSON_ArrayForEach (layer, layers) { @@ -365,6 +371,127 @@ int main(int argc, char **argv) { puts(str); free(str); #endif + } else if (strcmp(type, "object")) { + // object layer + cJSON const *object = NULL; + cJSON_ArrayForEach(object, cJSON_GetObjectItemCaseSensitive(layer, "objects")) { + struct blob entity = blob_new(); + + cJSON const *property = cJSON_GetObjectItemCaseSensitive(object, "type"); + blob_append(&entity, property->valuestring, strlen(property->valuestring) + 1); + //printf("%s:", property->valuestring); + + char const *const *default_props = (char const *const[]) {"x", "y", "width", "height", NULL}; + if (cJSON_IsTrue(cJSON_GetObjectItemCaseSensitive(object, "point"))) { + default_props = (char const *const[]) {"x", "y", NULL}; + } + for (int i = 0; default_props[i] != NULL; i++) { + property = cJSON_GetObjectItemCaseSensitive(object, default_props[i]); + if (property == NULL) { + continue; + } + + blob_append(&entity, default_props[i], strlen(default_props[i]) + 1); + if (cJSON_IsNumber(property)) { + char buf[32]; + sprintf(buf, "%i", property->valueint); + blob_append(&entity, buf, strlen(buf) + 1); + //printf(" %s=%i", default_props[i], property->valueint); + } else if (cJSON_IsString(property)) { + blob_append(&entity, property->valuestring, strlen(property->valuestring) + 1); + //printf(" %s=%s", default_props[i], property->valuestring); + } else { + char *str = cJSON_Print(property); + puts(str); + free(str); + return 1; + } + } + cJSON_ArrayForEach(property, cJSON_GetObjectItemCaseSensitive(object, "properties")) { + char const *const name = cJSON_GetObjectItemCaseSensitive(property, "name")->valuestring; + char const *const type = cJSON_GetObjectItemCaseSensitive(property, "type")->valuestring; + cJSON const *value_obj = cJSON_GetObjectItemCaseSensitive(property, "value"); + + blob_append(&entity, name, strlen(name) + 1); + if (strcmp(type, "string") == 0) { + if (!cJSON_IsString(value_obj)) { + fprintf(stderr, "%s: not actually a %s\n", name, type); + return 1; + } + char const *const value = value_obj->valuestring; + blob_append(&entity, value, strlen(value) + 1); + //printf(" %s='%s'", name, value); + } else if (strcmp(type, "file") == 0) { + if (!cJSON_IsString(value_obj)) { + fprintf(stderr, "%s: not actually a %s\n", name, type); + return 1; + } + struct blob const fragment = strip_filename(value_obj->valuestring); + blob_append(&entity, fragment.data, fragment.size); + blob_append(&entity, &(char) {0}, 1); + //printf(" %s='%.*s'", name, fragment.size, fragment.data); + } else if (strcmp(type, "bool") == 0) { + if (!cJSON_IsBool(value_obj)) { + fprintf(stderr, "%s: not actually a %s\n", name, type); + return 1; + } + if (cJSON_IsTrue(value_obj)) { + blob_append(&entity, "true", sizeof ("true")); + //printf(" %s=true", name); + } else { + blob_append(&entity, "false", sizeof ("false")); + //printf(" %s=false", name); + } + } else if (strcmp(type, "color") == 0) { + if (!cJSON_IsString(value_obj)) { + fprintf(stderr, "%s: not actually a %s\n", name, type); + return 1; + } + struct color color; + char const *const value = value_obj->valuestring; + string_to_color(&color, value); + char buf[10]; + sprintf(buf, "#%02x%02x%02x%02x", color.r, color.g, color.b, color.a); + blob_append(&entity, buf, strlen(buf) + 1); + //printf(" %s='%s'", name, buf); + } else if (strcmp(type, "int") == 0) { + if (!cJSON_IsNumber(value_obj)) { + fprintf(stderr, "%s: not actually a %s\n", name, type); + return 1; + } + + char buf[32]; + sprintf(buf, "%i", value_obj->valueint); + blob_append(&entity, buf, strlen(buf) + 1); + //printf(" %s=%s", name, buf); + } else if (strcmp(type, "float") == 0) { + if (!cJSON_IsNumber(value_obj)) { + fprintf(stderr, "%s: not actually a %s\n", name, type); + return 1; + } + + char buf[32]; + sprintf(buf, "%a", value_obj->valuedouble); + blob_append(&entity, buf, strlen(buf) + 1); + //printf(" %s=%s", name, buf); + } else if (strcmp(type, "object") == 0) { + fputs("object properties are not supported\n", stderr); + return 1; + } else { + fprintf(stderr, "%s: dont know what '%s' is\n", name, type); + return 1; + } + } + blob_append(&entity, &(char) {0}, 1); + //putchar('\n'); + #if 0 + char *str = cJSON_Print(property); + puts(str); + free(str); + #endif + blob_append(&entities, entity.data, entity.size); + blob_free(&entity); + } } else { // ??? layer } @@ -384,17 +511,26 @@ int main(int argc, char **argv) { } } - fwrite(&counts, 1, sizeof (counts), outfile); - fwrite(tilesets_data, 1, tilesets_size, outfile); + safe_write(&counts, sizeof (counts), outfile); + safe_write(tilesets_blob.data, tilesets_blob.size, outfile); + blob_free(&tilesets_blob); + if (wang_tileset != NULL) { - fwrite(&wang_height, 1, sizeof (uint32_t), outfile); - fwrite(wang_tileset, 1, strlen(wang_tileset) + 1, outfile); + safe_write(&wang_height, sizeof (uint32_t), outfile); + safe_write(wang_tileset, strlen(wang_tileset) + 1, outfile); + free(wang_tileset); } - fwrite(&map, 1, sizeof (map), outfile); - fwrite(tile_data, 1, map.width * map.height * map.layers, outfile); - fwrite(parallaxes, 1, sizeof (float) * 2 * map.layers, outfile); + safe_write(&map, sizeof (map), outfile); + safe_write(tile_data, map.width * map.height * map.layers, outfile); free(tile_data); + + safe_write(parallaxes, sizeof (float) * 2 * map.layers, outfile); + free(parallaxes); + + safe_write(entities.data, entities.size, outfile); + blob_free(&entities); + end: if (outfile != NULL) { fclose(outfile); -- cgit 1.4.1-2-gfad0