diff --git a/assets.mdesk b/assets.mdesk index 694a1c5..de7e2a8 100644 --- a/assets.mdesk +++ b/assets.mdesk @@ -14,7 +14,12 @@ { filepath: "mystery_tile.png", } +@tileset ruins_animated: +{ + image: image_animated_terrain, + filepath: "copyrighted/ruins_animated.tsx", +} @level level0: { - filepath: "testsmalllevel.json", + filepath: "level0.json", } diff --git a/assets/testsmalllevel.json b/assets/testsmalllevel.json index 3155e9d..9ce1760 100644 --- a/assets/testsmalllevel.json +++ b/assets/testsmalllevel.json @@ -3,13 +3,13 @@ "infinite":false, "layers":[ { - "data":[53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 158, 213, 53, 53, 53, - 53, 53, 209, 210, 212, 160, 53, 53, - 53, 158, 210, 263, 263, 212, 213, 53, - 53, 366, 314, 263, 263, 316, 368, 53, - 53, 53, 366, 314, 316, 368, 53, 53, - 53, 53, 53, 366, 368, 53, 53, 53, + "data":[53, 53, 53, 209, 160, 53, 53, 53, + 53, 209, 159, 210, 265, 53, 53, 53, + 209, 210, 263, 263, 212, 213, 53, 53, + 261, 263, 263, 263, 263, 265, 53, 53, + 313, 314, 263, 263, 263, 265, 53, 53, + 53, 313, 314, 316, 367, 317, 53, 53, + 53, 53, 366, 317, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53], "height":8, "id":1, @@ -31,6 +31,10 @@ { "firstgid":1, "source":"..\/EPIC RPG World Pack - Ancient Ruins V 1.7\/EPIC RPG World Pack - Ancient Ruins V 1.7\/TiledMap Editor\/Ancient Ruins-Animated Terrains-16 frames.tsx" + }, + { + "firstgid":2341, + "source":"..\/EPIC RPG World Pack - Ancient Ruins V 1.7\/EPIC RPG World Pack - Ancient Ruins V 1.7\/TiledMap Editor\/Terrain - Ancient Ruins.tsx" }], "tilewidth":32, "type":"map", diff --git a/codegen.c b/codegen.c index 0e1901b..1ddc56c 100644 --- a/codegen.c +++ b/codegen.c @@ -68,6 +68,33 @@ char *nullterm(MD_String8 s) { return to_return; } +char* fillnull(char *s, char c) { + while(*s != '\0') { + if(*s == c) { + *s = '\0'; + return s + 1; + } + s++; + } + assert(false, MD_S8Lit("Couldn't find char")); + return NULL; +} + +char* goto_end_of(char *tomove, size_t max_move, char *pattern) { + size_t pattern_len = strlen(pattern); + for(int i = 0; i < max_move; i++) { + if(strncmp(tomove, pattern, pattern_len) == 0) { + tomove += pattern_len; + return tomove; + } + tomove++; + } + return NULL; +} + +#define list_printf(list_ptr, ...) MD_S8ListPush(cg_arena, list_ptr, MD_S8Fmt(cg_arena, __VA_ARGS__)) + + int main(int argc, char **argv) { cg_arena = MD_ArenaAlloc(); assert(cg_arena, MD_S8Lit("Memory")); @@ -86,6 +113,7 @@ int main(int argc, char **argv) { MD_String8List declarations_list = {0}; MD_String8List load_list = {0}; MD_String8List level_decl_list = {0}; + MD_String8List tileset_decls = {0}; for(MD_EachNode(node, parse.node->first_child)) { if(MD_S8Match(node->first_tag->string, MD_S8Lit("image"), 0)) { MD_String8 variable_name = MD_S8Fmt(cg_arena, "image_%.*s", MD_S8VArg(node->string)); @@ -100,6 +128,44 @@ int main(int argc, char **argv) { MD_S8ListPush(cg_arena, &declarations_list, MD_S8Fmt(cg_arena, "sg_image %.*s = {0};\n", MD_S8VArg(variable_name))); MD_S8ListPush(cg_arena, &load_list, MD_S8Fmt(cg_arena, "%.*s = load_image(\"%.*s\");\n", MD_S8VArg(variable_name), MD_S8VArg(filepath))); } + if(MD_S8Match(node->first_tag->string, MD_S8Lit("tileset"), 0)) { + MD_String8 variable_name = MD_S8Fmt(cg_arena, "tileset_%.*s", MD_S8VArg(node->string)); + log("New tileset %.*s\n", MD_S8VArg(variable_name)); + MD_String8 filepath = asset_file_path(ChildValue(node, MD_S8Lit("filepath"))); + + MD_String8 tileset_file_contents = MD_LoadEntireFile(cg_arena, filepath); + list_printf(&tileset_decls, "TileSet %.*s = {\n", MD_S8VArg(variable_name)); + list_printf(&tileset_decls, ".img = &%.*s,\n", MD_S8VArg(ChildValue(node, MD_S8Lit("image")))); + + list_printf(&tileset_decls, ".animated = {\n"); + char *end = tileset_file_contents.str + tileset_file_contents.size; + char *cur = tileset_file_contents.str; + while(cur < end) { + cur = goto_end_of(cur, end - cur, ""); + + int num_frames = 0; + while(true) { + char *next_frame = goto_end_of(cur, end - cur, " end_of_anim) break; + char *new_cur = fillnull(next_frame, '"'); + int frame = atoi(next_frame); + + list_printf(&tileset_decls, "%d, ", frame); + num_frames++; + + cur = new_cur; + } + list_printf(&tileset_decls, "}, .num_frames = %d },\n", num_frames); + } + list_printf(&tileset_decls, "}};\n"); + } if(MD_S8Match(node->first_tag->string, MD_S8Lit("level"), 0)) { MD_String8 variable_name = MD_S8Fmt(cg_arena, "level_%.*s", MD_S8VArg(node->string)); log("New level variable %.*s\n", MD_S8VArg(variable_name)); @@ -107,7 +173,6 @@ int main(int argc, char **argv) { MD_ParseResult level_parse = MD_ParseWholeFile(cg_arena, filepath); assert(level_parse.node != 0, MD_S8Lit("Failed to load level file")); - //dump(level_parse.node); MD_Node *layers = MD_ChildFromString(level_parse.node->first_child, MD_S8Lit("layers"), 0); int width = atoi(nullterm(MD_ChildFromString(layers->first_child, MD_S8Lit("width"), 0)->first_child->string)); int height = atoi(nullterm(MD_ChildFromString(layers->first_child, MD_S8Lit("height"), 0)->first_child->string)); @@ -128,53 +193,14 @@ int main(int argc, char **argv) { } num_index += 1; } - //fprintf(output, "},\n};\n"); - - -#if 0 - jsmn_parser p; - jsmntok_t t[1024*5]; - jsmn_init(&p); - const char *json_string = level_file_contents.str; - int r = jsmn_parse(&p, json_string, level_file_contents.size, t, sizeof(t) / sizeof(t[0])); - assert(r > 0, MD_S8Lit("Failed to parse json")); - - int layer_obj_token = 0; - fprintf(output, "Level %.*s = {\n.tiles = {\n", MD_S8VArg(variable_name)); - // this code is absolutely disgusting but I think it's fine because this is meta code, I guess time will tell if this needs to be cleaner or not... sorry future self! - for(int i = 0; i < r; i++) { - if(jsoneq(json_string, &t[i], "data")) { - jsmntok_t *arr = &t[i+1]; - assert(arr->type == JSMN_ARRAY, MD_S8Lit("Expected array after data")); - int width = getnextofname(json_string, &t[i+1], "width"); - int height = getnextofname(json_string, &t[i+1], "height"); - - fprintf(output, "{ "); - int last_token_index = i + 2 + arr->size; - for(int ii = i + 2; ii < last_token_index; ii++) { - jsmntok_t *num_to_add = &t[ii]; - assert(num_to_add->type == JSMN_PRIMITIVE, MD_S8Lit("not a number")); - int num_index = ii - (i + 2); - fprintf(output, "%.*s, ", num_to_add->end - num_to_add->start, json_string + num_to_add->start); - if(num_index % width == width - 1) { - if(ii == last_token_index - 1) { - fprintf(output, "},\n}\n}; // %.*s\n", MD_S8VArg(variable_name)); - } else { - fprintf(output, "},\n{ "); - } - } - } - } - } -#endif - } } MD_StringJoin join = MD_ZERO_STRUCT; MD_String8 declarations = MD_S8ListJoin(cg_arena, declarations_list, &join); MD_String8 loads = MD_S8ListJoin(cg_arena, load_list, &join); - fprintf(output, "%.*s\nvoid load_assets() {\n%.*s\n}", MD_S8VArg(declarations), MD_S8VArg(loads)); + fprintf(output, "%.*s\nvoid load_assets() {\n%.*s\n}\n", MD_S8VArg(declarations), MD_S8VArg(loads)); + fprintf(output, "%.*s\n", MD_S8VArg(MD_S8ListJoin(cg_arena, tileset_decls, &join))); fclose(output); return 0; diff --git a/main.c b/main.c index a742c19..c8c79ae 100644 --- a/main.c +++ b/main.c @@ -25,12 +25,16 @@ typedef struct TileInstance { uint16_t kind; } TileInstance; -typedef struct TileInfo { - uint16_t kind; - int num_frames; // each frame of animation is same size, to the right of the region - HMM_Vec2 region_upper_left; +typedef struct AnimatedTile { + uint16_t id_from; + int num_frames; + uint16_t frames[32]; +} AnimatedTile; + +typedef struct TileSet { sg_image *img; -} TileInfo; + AnimatedTile animated[128]; +} TileSet; #define LEVEL_TILES 60 #define TILE_SIZE 32 // in pixels @@ -70,93 +74,6 @@ sg_image load_image(const char *path) { #include "quad-sapp.glsl.h" #include "assets.gen.c" -// the `kind`, or the ID of the tile, is off by one from what tiled displays in editor. Sucks! -TileInfo tiles[] = { - { - .kind = 53, - .num_frames = 15, - .region_upper_left = { 0, 32 }, - .img = &image_animated_terrain, - }, - { - .kind = 158, - .num_frames = 15, - .region_upper_left = { 672, 128 }, - .img = &image_animated_terrain, - }, - { - .kind = 159, - .num_frames = 15, - .region_upper_left = { 672, 96 }, - .img = &image_animated_terrain, - }, - { - .kind = 160, - .num_frames = 15, - .region_upper_left = { 160, 192 }, - .img = &image_animated_terrain, - }, - { - .kind = 210, - .num_frames = 15, - .region_upper_left = { 672, 160 }, - .img = &image_animated_terrain, - }, - { - .kind = 261, - .num_frames = 15, - .region_upper_left = { 672, 224 }, - .img = &image_animated_terrain, - }, - { - .kind = 367, - .num_frames = 15, - .region_upper_left = { 1184, 160 }, - .img = &image_animated_terrain, - }, - { - .kind = 316, - .num_frames = 15, - .region_upper_left = { 160, 96 }, - .img = &image_animated_terrain, - }, - { - .kind = 209, - .num_frames = 15, - .region_upper_left = { 672, 128 }, - .img = &image_animated_terrain, - }, - { - .kind = 213, - .num_frames = 15, - .region_upper_left = { 672, 64 }, - .img = &image_animated_terrain, - }, - { - .kind = 263, - .num_frames = 1, - .region_upper_left = { 64, 160 }, - .img = &image_animated_terrain, - }, - { - .kind = 213, - .num_frames = 15, - .region_upper_left = { 672, 64 }, - .img = &image_animated_terrain, - }, - { - .kind = 213, - .num_frames = 15, - .region_upper_left = { 672, 64 }, - .img = &image_animated_terrain, - }, -}; -TileInfo mystery_tile = { - .img = &image_mystery_tile, - .num_frames = 1, - .region_upper_left = { 0, 0 }, -}; - sg_image image_font = {0}; const float font_size = 64.0; stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs @@ -451,6 +368,15 @@ void colorbox(bool world_space, HMM_Vec2 upper_left, HMM_Vec2 lower_right, Color draw_quad(world_space, points, image_white_square, full_region(image_white_square), color); } +HMM_Vec2 tile_id_to_coord(sg_image tileset_image, HMM_Vec2 tile_size, uint16_t tile_id) { + int tiles_per_row = (int)(img_size(tileset_image).X / tile_size.X); + int tile_index = tile_id - 1; + int tile_image_row = tile_index / tiles_per_row; + int tile_image_col = tile_index - tile_image_row*tiles_per_row; + HMM_Vec2 tile_image_coord = HMM_V2((float)tile_image_col * tile_size.X, (float)tile_image_row*tile_size.Y); + return tile_image_coord; +} + // returns bounds. To measure text you can set dry run to true and get the bounds AABB draw_text(bool world_space, bool dry_run, const char *text, size_t length, HMM_Vec2 pos, Color color) { @@ -555,30 +481,33 @@ void frame(void) { for(int col = 0; col < LEVEL_TILES; col++) { TileInstance cur = cur_level->tiles[row][col]; + TileSet tileset = tileset_ruins_animated; if(cur.kind != 0){ HMM_Vec2 points[4] = {0}; HMM_Vec2 tile_size = HMM_V2(TILE_SIZE, TILE_SIZE); quad_points_corner_size(points, tilecoord_to_world(col, row), tile_size); - TileInfo *info = NULL; - for(int i = 0; i < sizeof(tiles)/sizeof(*tiles); i++) { - if(tiles[i].kind == cur.kind) { - info = &tiles[i]; - break; + + sg_image tileset_image = *tileset.img; + + HMM_Vec2 tile_image_coord = tile_id_to_coord(tileset_image, tile_size, cur.kind); + + AnimatedTile *anim = NULL; + for(int i = 0; i < sizeof(tileset.animated)/sizeof(*tileset.animated); i++) { + if(tileset.animated[i].id_from == cur.kind-1) { + anim = &tileset.animated[i]; } } - if(info == NULL) info = &mystery_tile; + if(anim) { + double time_per_frame = 0.1; + int frame_index = (int)(time/time_per_frame) % anim->num_frames; + tile_image_coord = tile_id_to_coord(tileset_image, tile_size, anim->frames[frame_index]+1); + } AABB region; - region.upper_left = info->region_upper_left; + region.upper_left = tile_image_coord; region.lower_right = HMM_AddV2(region.upper_left, tile_size); - double time_per_frame = 0.1; - int frame_index = (int)(time/time_per_frame) % info->num_frames; - float width = region.lower_right.X - region.upper_left.X; - assert(width > 0.0f); - region.upper_left.X += width*(float)frame_index; - region.lower_right.X += width*(float)frame_index; - - draw_quad(true, points, *info->img, region, WHITE); + + draw_quad(true, points, tileset_image, region, WHITE); } } } diff --git a/run_codegen.bat b/run_codegen.bat index f3349b1..1e8cfd2 100644 --- a/run_codegen.bat +++ b/run_codegen.bat @@ -8,6 +8,7 @@ mkdir assets\copyrighted copy "EPIC RPG World Pack - Ancient Ruins V 1.7\EPIC RPG World Pack - Ancient Ruins V 1.7\Characters\NPC Merchant-idle.png" "assets\copyrighted\merchant.png" || goto :error copy "EPIC RPG World Pack - Ancient Ruins V 1.7\EPIC RPG World Pack - Ancient Ruins V 1.7\Tilesets\wall-1 - 3 tiles tall.png" "assets\copyrighted\wall-1 - 3 tiles tall.png" || goto :error copy "EPIC RPG World Pack - Ancient Ruins V 1.7\EPIC RPG World Pack - Ancient Ruins V 1.7\Tilesets\Tileset-Animated Terrains-16 frames.png" "assets\copyrighted\animated_terrain.png" || goto :error +copy "EPIC RPG World Pack - Ancient Ruins V 1.7\EPIC RPG World Pack - Ancient Ruins V 1.7\TiledMap Editor\Ancient Ruins-Animated Terrains-16 frames.tsx" "assets\copyrighted\ruins_animated.tsx" || goto :error rmdir /S /q gen mkdir gen