From fadae61b2b9f103a3f259ba93f46254de62435a2 Mon Sep 17 00:00:00 2001 From: Cameron Reikes Date: Wed, 22 Feb 2023 13:54:07 -0800 Subject: [PATCH] Load entities from file, barrel roll! --- assets.mdesk | 4 + assets/_Roll.png | Bin 0 -> 3925 bytes assets/level0.json | 54 +++++++++---- assets/testsmalllevel.json | 18 ++--- codegen.c | 25 ++++-- main.c | 162 +++++++++++++++++++++++++++---------- 6 files changed, 194 insertions(+), 69 deletions(-) create mode 100644 assets/_Roll.png diff --git a/assets.mdesk b/assets.mdesk index 9d6c6f9..602908b 100644 --- a/assets.mdesk +++ b/assets.mdesk @@ -14,6 +14,10 @@ { filepath: "_Attack.png", } +@image knight_roll: +{ + filepath: "_Roll.png", +} @image old_man: { filepath: "small_old_man.png", diff --git a/assets/_Roll.png b/assets/_Roll.png new file mode 100644 index 0000000000000000000000000000000000000000..e85dcff86585a2f1fa4be3d8f0b81efd5cd4d99f GIT binary patch literal 3925 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 a+Bd(d6=1iCP1^pt0PdH(Txl*hlK%$?^1xyM literal 0 HcmV?d00001 diff --git a/assets/level0.json b/assets/level0.json index 74f13df..0258194 100644 --- a/assets/level0.json +++ b/assets/level0.json @@ -1,11 +1,4 @@ { "compressionlevel":-1, - "editorsettings": - { - "export": - { - "format":"json" - } - }, "height":60, "infinite":false, "layers":[ @@ -32,11 +25,11 @@ 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 261, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 212, 213, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 158, 210, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 265, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 261, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 265, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 261, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 265, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 261, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 265, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 261, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 265, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 261, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 265, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 366, 314, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 265, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 261, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 212, 159, 159, 159, 159, 159, 159, 159, 213, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 261, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 265, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 261, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 316, 367, 314, 263, 263, 263, 263, 263, 265, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 261, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 265, 53, 261, 263, 263, 263, 263, 263, 265, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 366, 314, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 265, 53, 313, 367, 367, 367, 367, 367, 368, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 261, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 265, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 261, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 265, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 261, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 265, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, @@ -89,12 +82,45 @@ "class":"", "height":32, "id":1, - "name":"spawn", + "name":"PLAYER", "rotation":0, "visible":true, "width":32, "x":960, "y":928 + }, + { + "class":"", + "height":32, + "id":2, + "name":"OLD_MAN", + "rotation":0, + "visible":true, + "width":32, + "x":804.666333333333, + "y":770.666333333333 + }, + { + "class":"", + "height":32, + "id":3, + "name":"OLD_MAN", + "rotation":0, + "visible":true, + "width":32, + "x":834, + "y":916 + }, + { + "class":"", + "height":32, + "id":4, + "name":"OLD_MAN", + "rotation":0, + "visible":true, + "width":32, + "x":970, + "y":761.333333333333 }], "opacity":1, "type":"objectgroup", @@ -103,7 +129,7 @@ "y":0 }], "nextlayerid":6, - "nextobjectid":2, + "nextobjectid":5, "orientation":"orthogonal", "renderorder":"right-down", "tiledversion":"1.9.2", diff --git a/assets/testsmalllevel.json b/assets/testsmalllevel.json index 1033133..a7f322a 100644 --- a/assets/testsmalllevel.json +++ b/assets/testsmalllevel.json @@ -4,13 +4,13 @@ "layers":[ { "data":[53, 53, 53, 53, 53, 53, 53, 53, - 53, 209, 159, 159, 159, 160, 209, 160, - 53, 261, 263, 263, 263, 265, 261, 265, - 53, 261, 263, 263, 263, 265, 261, 265, - 53, 261, 263, 263, 263, 212, 210, 265, - 53, 366, 367, 367, 314, 263, 316, 368, - 53, 53, 53, 53, 366, 367, 368, 53, - 53, 53, 53, 53, 53, 53, 53, 53], + 53, 209, 159, 159, 159, 160, 209, 160, + 53, 261, 263, 263, 263, 265, 261, 265, + 53, 261, 263, 263, 263, 265, 261, 265, + 53, 261, 263, 263, 263, 212, 210, 265, + 53, 366, 367, 367, 314, 263, 316, 368, + 53, 53, 53, 53, 366, 367, 368, 53, + 53, 53, 53, 53, 53, 53, 53, 53], "height":8, "id":1, "name":"Tile Layer 1", @@ -30,7 +30,7 @@ "class":"", "height":31.604938271605, "id":1, - "name":"spawn", + "name":"PLAYER", "rotation":0, "visible":true, "width":29.9415204678363, @@ -62,4 +62,4 @@ "type":"map", "version":"1.9", "width":8 -} +} \ No newline at end of file diff --git a/codegen.c b/codegen.c index ea207cb..c81ef74 100644 --- a/codegen.c +++ b/codegen.c @@ -48,6 +48,15 @@ void dump_root(MD_Node* from) { } } +bool has_decimal(MD_String8 s) +{ + for(int i = 0; i < s.size; i++) + { + if(s.str[i] == '.') return true; + } + return false; +} + MD_Arena *cg_arena = NULL; MD_String8 ChildValue(MD_Node *n, MD_String8 name) { @@ -114,7 +123,6 @@ int main(int argc, char **argv) { MD_String8List load_list = {0}; MD_String8List level_decl_list = {0}; MD_String8List tileset_decls = {0}; - MD_String8List object_decls_list = {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)); @@ -172,20 +180,28 @@ int main(int argc, char **argv) { log("New level variable %.*s\n", MD_S8VArg(variable_name)); MD_String8 filepath = asset_file_path(ChildValue(node, MD_S8Lit("filepath"))); MD_ParseResult level_parse = MD_ParseWholeFile(cg_arena, filepath); - assert(level_parse.node != 0, MD_S8Lit("Failed to load level file")); + assert(!MD_NodeIsNil(level_parse.node->first_child), MD_S8Lit("Failed to load level file")); MD_Node *layers = MD_ChildFromString(level_parse.node->first_child, MD_S8Lit("layers"), 0); fprintf(output, "Level %.*s = {\n", MD_S8VArg(variable_name)); for(MD_EachNode(lay, layers->first_child)) { MD_String8 type = MD_ChildFromString(lay, MD_S8Lit("type"), 0)->first_child->string; if(MD_S8Match(type, MD_S8Lit("objectgroup"), 0)) { + fprintf(output, ".initial_entities = {\n"); for(MD_EachNode(object, MD_ChildFromString(lay, MD_S8Lit("objects"), 0)->first_child)) { dump(object); + // negative numbers for object position aren't supported here MD_String8 name = MD_ChildFromString(object, MD_S8Lit("name"), 0)->first_child->string; MD_String8 x_string = MD_ChildFromString(object, MD_S8Lit("x"), 0)->first_child->string; MD_String8 y_string = MD_ChildFromString(object, MD_S8Lit("y"), 0)->first_child->string; - list_printf(&object_decls_list, "Vec2 %.*s_tilepoint = { %.*s, %.*s };\n", MD_S8VArg(name), MD_S8VArg(x_string), MD_S8VArg(y_string)); + y_string = MD_S8Fmt(cg_arena, "-%.*s", MD_S8VArg(y_string)); + + if(has_decimal(x_string)) x_string = MD_S8Fmt(cg_arena, "%.*sf", MD_S8VArg(x_string)); + if(has_decimal(y_string)) y_string = MD_S8Fmt(cg_arena, "%.*sf", MD_S8VArg(y_string)); + + fprintf(output, "{ .exists = true, .kind = ENTITY_%.*s, .pos = { .X=%.*s, .Y=%.*s }, }, ", MD_S8VArg(name), MD_S8VArg(x_string), MD_S8VArg(y_string)); } + fprintf(output, "\n}, // entities\n"); } if(MD_S8Match(type, MD_S8Lit("tilelayer"), 0)) { int width = atoi(nullterm(MD_ChildFromString(layers->first_child, MD_S8Lit("width"), 0)->first_child->string)); @@ -215,10 +231,9 @@ int main(int argc, char **argv) { MD_StringJoin join = MD_ZERO_STRUCT; MD_String8 declarations = MD_S8ListJoin(cg_arena, declarations_list, &join); - MD_String8 object_declarations = MD_S8ListJoin(cg_arena, object_decls_list, &join); MD_String8 loads = MD_S8ListJoin(cg_arena, load_list, &join); fprintf(output, "%.*s\nvoid load_assets() {\n%.*s\n}\n", MD_S8VArg(declarations), MD_S8VArg(loads)); - fprintf(output, "%.*s\n%.*s\n", MD_S8VArg(MD_S8ListJoin(cg_arena, tileset_decls, &join)), MD_S8VArg(object_declarations)); + 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 9cadb91..5574c1e 100644 --- a/main.c +++ b/main.c @@ -68,13 +68,49 @@ typedef struct AnimatedSprite bool no_wrap; // does not wrap when playing } AnimatedSprite; +typedef enum CharacterState +{ + CHARACTER_WALKING, + CHARACTER_IDLE, + CHARACTER_ATTACK, +} CharacterState; + +typedef enum EntityKind +{ + ENTITY_INVALID, // zero initialized is invalid entity + + ENTITY_PLAYER, + ENTITY_OLD_MAN, +} EntityKind; + + +typedef struct Entity +{ + bool exists; + EntityKind kind; + + // fields for all entities + Vec2 pos; + bool facing_left; + + // character + CharacterState state; + bool is_rolling; // can only roll in idle or walk states + float speed; // for lerping to the speed, so that roll gives speed boost which fades + double roll_progress; + double swing_progress; +} Entity; + #define LEVEL_TILES 60 #define TILE_SIZE 32 // in pixels +#define MAX_ENTITIES 128 +#define PLAYER_SPEED 3.0f // in meters per second +#define PLAYER_ROLL_SPEED 7.0f typedef struct Level { TileInstance tiles[LEVEL_TILES][LEVEL_TILES]; - Vec2 spawnpoint; + Entity initial_entities[MAX_ENTITIES]; // shouldn't be directly modified, only used to initialize entities on loading of level } Level; typedef struct TileCoord @@ -134,6 +170,7 @@ char *tprint(const char *format, ...) return to_return; } +// tilecoord is integer tile position, not like tile coord Vec2 tilecoord_to_world(TileCoord t) { return V2( (float)t.x * (float)TILE_SIZE * 1.0f, -(float)t.y * (float)TILE_SIZE * 1.0f ); @@ -232,11 +269,9 @@ AnimatedSprite knight_idle = .img = &image_knight_idle, .time_per_frame = 0.3, .num_frames = 10, - .start = - {16.0f, 0.0f}, + .start = {16.0f, 0.0f}, .horizontal_diff_btwn_frames = 120.0, - .region_size = - {80.0f, 80.0f}, + .region_size = {80.0f, 80.0f}, }; AnimatedSprite knight_running = @@ -249,6 +284,18 @@ AnimatedSprite knight_running = .region_size = {80.0f, 80.0f}, }; +AnimatedSprite knight_rolling = +{ + .img = &image_knight_roll, + .time_per_frame = 0.05, + .num_frames = 12, + .start = {19.0f, 0.0f}, + .horizontal_diff_btwn_frames = 120.0, + .region_size = {80.0f, 80.0f}, + .no_wrap = true, +}; + + AnimatedSprite knight_attack = { .img = &image_knight_attack, @@ -284,34 +331,8 @@ static struct sg_bindings bind; } state; -typedef enum CharacterState -{ - CHARACTER_WALKING, - CHARACTER_IDLE, - CHARACTER_ATTACK, -} CharacterState; - -typedef enum EntityKind -{ - Player, - OldMan, -} EntityKind; - -typedef struct Entity -{ - bool exists; - EntityKind kind; - - Vec2 pos; - bool facing_left; - - // character - CharacterState state; - double swing_progress; -} Entity; - -Entity entities[128] = {0}; +Entity entities[MAX_ENTITIES] = {0}; Entity *player = NULL; Entity *new_entity() @@ -340,8 +361,23 @@ void init(void) load_assets(); - player = new_entity(); - player->pos = tilepoint_to_world(spawn_tilepoint); + // load level + Level *to_load = &level_level0; + { + assert(ARRLEN(to_load->initial_entities) == ARRLEN(entities)); + memcpy(entities, to_load->initial_entities, sizeof(Entity) * MAX_ENTITIES); + + player = NULL; + for(int i = 0; i < MAX_ENTITIES; i++) + { + if(entities[i].exists && entities[i].kind == ENTITY_PLAYER) + { + assert(player == NULL); + player = &entities[i]; + } + } + assert(player != NULL); // level initial config must have player entity + } // load font { @@ -459,8 +495,7 @@ typedef struct Camera // everything is in pixels in world space, 43 pixels is approx 1 meter measured from // merchant sprite being 5'6" const float pixels_per_meter = 43.0f; -Camera cam = -{.scale = 2.0f }; +Camera cam = {.scale = 2.0f }; Vec2 cam_offset() { @@ -859,7 +894,7 @@ Vec2 move_and_slide(Vec2 position, Vec2 movement_this_frame, Vec2 collision_aabb TileCoord to_check = world_to_tilecoord(*it); uint16_t tile_id = get_tile(&level_level0, to_check).kind; - if(tile_id == 53 || tile_id == 0 || tile_id == 367 || tile_id == 317 || tile_id == 313 || tile_id == 366 ) + if(tile_id == 53 || tile_id == 0 || tile_id == 367 || tile_id == 317 || tile_id == 313 || tile_id == 366 || tile_id == 368) { dbgsquare(tilecoord_to_world(to_check)); AABB to_depenetrate_from = tile_aabb(to_check); @@ -929,6 +964,7 @@ void frame(void) (float)keydown[SAPP_KEYCODE_W] - (float)keydown[SAPP_KEYCODE_S] ); bool attack = keydown[SAPP_KEYCODE_J]; + bool roll = keydown[SAPP_KEYCODE_K]; if(LenV2(movement) > 1.0) { movement = NormV2(movement); @@ -1033,7 +1069,14 @@ void frame(void) #endif // devtools - draw_animated_sprite(&old_man_idle, time, false, V2(884.788635f, -928.000000f), WHITE); + for(int i = 0; i < ARRLEN(entities); i++) if(entities[i].exists) + { + if(entities[i].kind == ENTITY_OLD_MAN) + { + draw_animated_sprite(&old_man_idle, time, false, entities[i].pos, WHITE); + } + } + // player character { @@ -1045,11 +1088,41 @@ void frame(void) player->swing_progress = 0.0; } + // rolling + if(roll && !player->is_rolling && (player->state == CHARACTER_IDLE || player->state == CHARACTER_WALKING)) + { + player->is_rolling = true; + player->roll_progress = 0.0; + player->speed = PLAYER_ROLL_SPEED; + } + if(player->state != CHARACTER_IDLE && player->state != CHARACTER_WALKING) + { + player->roll_progress = 0.0; + player->is_rolling = false; + } + if(player->is_rolling) + { + player->roll_progress += dt; + if(player->roll_progress > anim_sprite_duration(&knight_rolling)) + { + player->is_rolling = false; + } + } + cam.pos = LerpV2(cam.pos, dt*8.0f, MulV2F(player->pos, -1.0f * cam.scale)); if(player->state == CHARACTER_WALKING) { - player->pos = move_and_slide(player->pos, MulV2F(movement, dt * pixels_per_meter * 4.0f), V2(TILE_SIZE, TILE_SIZE)); - draw_animated_sprite(&knight_running, time, player->facing_left, character_sprite_pos, WHITE); + if(player->speed <= 0.01f) player->speed = PLAYER_SPEED; + player->speed = Lerp(player->speed, dt * 3.0f, PLAYER_SPEED); + player->pos = move_and_slide(player->pos, MulV2F(movement, dt * pixels_per_meter * player->speed), V2(TILE_SIZE, TILE_SIZE)); + if(player->is_rolling) + { + draw_animated_sprite(&knight_rolling, player->roll_progress, player->facing_left, character_sprite_pos, WHITE); + } + else + { + draw_animated_sprite(&knight_running, time, player->facing_left, character_sprite_pos, WHITE); + } if(LenV2(movement) == 0.0) { player->state = CHARACTER_IDLE; @@ -1061,7 +1134,14 @@ void frame(void) } else if(player->state == CHARACTER_IDLE) { - draw_animated_sprite(&knight_idle, time, player->facing_left, character_sprite_pos, WHITE); + if(player->is_rolling) + { + draw_animated_sprite(&knight_rolling, player->roll_progress, player->facing_left, character_sprite_pos, WHITE); + } + else + { + draw_animated_sprite(&knight_idle, time, player->facing_left, character_sprite_pos, WHITE); + } if(LenV2(movement) > 0.01) player->state = CHARACTER_WALKING; } else if(player->state == CHARACTER_ATTACK)