From 7d131a193110ee359bd69ab21112fbbdc3cdb15b Mon Sep 17 00:00:00 2001 From: Cameron Reikes Date: Wed, 8 Mar 2023 02:56:16 -0800 Subject: [PATCH] Add skeleton enemies --- assets.mdesk | 4 + assets/level0.json | 54 ++++++++- build_web_debug.bat | 8 +- build_web_release.bat | 5 +- main.c | 267 ++++++++++++++++++++++++++++++------------ profiling.h | 2 +- run_codegen.bat | 1 + 7 files changed, 260 insertions(+), 81 deletions(-) diff --git a/assets.mdesk b/assets.mdesk index d4bd79a..35e9aeb 100644 --- a/assets.mdesk +++ b/assets.mdesk @@ -58,6 +58,10 @@ { filepath: "drop_shadow.png", } +@image skeleton: +{ + filepath: "copyrighted/skeleton.png", +} @image shift_icon: { filepath: "shift_icon.png", diff --git a/assets/level0.json b/assets/level0.json index 6132467..2ade691 100644 --- a/assets/level0.json +++ b/assets/level0.json @@ -348,8 +348,8 @@ "rotation":0, "visible":true, "width":32, - "x":958.999999999997, - "y":913.333333333337 + "x":1528.66666666667, + "y":748.333333333337 }, { "class":"", @@ -359,8 +359,52 @@ "rotation":0, "visible":true, "width":32, - "x":1287.33333333333, - "y":748.666666666666 + "x":1258, + "y":732.666666666666 + }, + { + "class":"", + "height":32, + "id":6, + "name":"SKELETON", + "rotation":0, + "visible":true, + "width":32, + "x":1637, + "y":921.833333333333 + }, + { + "class":"", + "height":32, + "id":7, + "name":"SKELETON", + "rotation":0, + "visible":true, + "width":32, + "x":1721.33333333333, + "y":1098.66666666667 + }, + { + "class":"", + "height":32, + "id":8, + "name":"SKELETON", + "rotation":0, + "visible":true, + "width":32, + "x":1652, + "y":1224 + }, + { + "class":"", + "height":32, + "id":9, + "name":"SKELETON", + "rotation":0, + "visible":true, + "width":32, + "x":1716, + "y":1222.66666666667 }], "opacity":1, "type":"objectgroup", @@ -369,7 +413,7 @@ "y":0 }], "nextlayerid":8, - "nextobjectid":6, + "nextobjectid":10, "orientation":"orthogonal", "renderorder":"right-down", "tiledversion":"1.9.2", diff --git a/build_web_debug.bat b/build_web_debug.bat index 67643c2..fa498fd 100644 --- a/build_web_debug.bat +++ b/build_web_debug.bat @@ -5,7 +5,13 @@ mkdir build_web call run_codegen.bat || goto :error -emcc -sEXPORTED_FUNCTIONS=_main,_end_text_input -sEXPORTED_RUNTIME_METHODS=ccall,cwrap -O0 -s ALLOW_MEMORY_GROWTH --source-map-base . -gsource-map -DDEVTOOLS -Ithirdparty -Igen main.c -o build_web\index.html --preload-file assets --shell-file web_template.html || goto :error +@REM set FLAGS=-fsanitize=undefined -fsanitize=address +@REM GO FUCK YOURSELF +set FLAGS=-s TOTAL_STACK=5242880 + +@echo on +emcc -sEXPORTED_FUNCTIONS=_main,_end_text_input -sEXPORTED_RUNTIME_METHODS=ccall,cwrap -O0 -s ALLOW_MEMORY_GROWTH %FLAGS% --source-map-base ../ -gsource-map -DDEVTOOLS -Ithirdparty -Igen main.c -o build_web\index.html --preload-file assets --shell-file web_template.html || goto :error +@echo off goto :EOF diff --git a/build_web_release.bat b/build_web_release.bat index bfcf0a4..527ead8 100644 --- a/build_web_release.bat +++ b/build_web_release.bat @@ -5,8 +5,11 @@ mkdir build_web_release call run_codegen.bat || goto :error +@REM GO FUCK YOURSELF +set FLAGS=-s TOTAL_STACK=5242880 + echo Building release -emcc -sEXPORTED_FUNCTIONS=_main,_end_text_input -sEXPORTED_RUNTIME_METHODS=ccall,cwrap -DNDEBUG -O2 -s ALLOW_MEMORY_GROWTH -Ithirdparty -Igen main.c -o build_web_release\index.html --preload-file assets --shell-file web_template.html || goto :error +emcc -sEXPORTED_FUNCTIONS=_main,_end_text_input -sEXPORTED_RUNTIME_METHODS=ccall,cwrap -DNDEBUG -O2 -s ALLOW_MEMORY_GROWTH %FLAGS% -Ithirdparty -Igen main.c -o build_web_release\index.html --preload-file assets --shell-file web_template.html || goto :error goto :EOF diff --git a/main.c b/main.c index b8db6f5..255c8be 100644 --- a/main.c +++ b/main.c @@ -23,10 +23,10 @@ #include -#define PROFILING_IMPL #ifdef DEVTOOLS #ifdef DESKTOP #define PROFILING +#define PROFILING_IMPL #endif #endif #include "profiling.h" @@ -98,6 +98,7 @@ typedef struct AnimatedSprite double time_per_frame; int num_frames; Vec2 start; + Vec2 offset; float horizontal_diff_btwn_frames; Vec2 region_size; bool no_wrap; // does not wrap when playing @@ -152,6 +153,7 @@ typedef enum NpcKind { OLD_MAN, DEATH, + SKELETON, } NpcKind; typedef struct Entity @@ -163,6 +165,8 @@ typedef struct Entity Vec2 vel; // only used sometimes, like in old man and bullet float damage; // at 1.0, dead! zero initialized bool facing_left; + // multiple entities have a sword swing + BUFF(struct Entity*, 8) done_damage_to_this_swing; // only do damage once, but hitbox stays around bool is_bullet; @@ -180,12 +184,13 @@ typedef struct Entity // only for death npc bool going_to_target; Vec2 target_goto; + // only for skeleton npc + double swing_timer; // character bool is_character; CharacterState state; struct Entity *talking_to; // Maybe should be generational index, but I dunno. No death yet - BUFF(struct Entity*, 8) done_damage_to_this_swing; // only do damage once, but hitbox stays around bool is_rolling; // can only roll in idle or walk states double time_not_rolling; // for cooldown for roll, so you can't just hold it and be invincible double roll_progress; @@ -498,6 +503,54 @@ char *tprint(const char *format, ...) return to_return; } +AABB entity_sword_aabb(Entity *e, float width, float height) +{ + if(e->facing_left) + { + return (AABB){ + .upper_left = AddV2(e->pos, V2(-width, height)), + .lower_right = AddV2(e->pos, V2(0.0, -height)), + }; + } + else + { + return (AABB){ + .upper_left = AddV2(e->pos, V2(0.0, height)), + .lower_right = AddV2(e->pos, V2(width, -height)), + }; + } +} + +typedef BUFF(Entity*, 16) SwordToDamage; +SwordToDamage entity_sword_to_do_damage(Entity *from, Overlapping o) +{ + SwordToDamage to_return = {0}; + BUFF_ITER(Overlap, &o) + { + if(!it->is_tile && it->e != from) + { + bool done_damage = false; + Entity *looking_for = it->e; + BUFF_ITER(Entity*, &from->done_damage_to_this_swing) + { + if(*it == looking_for) done_damage = true; + } + if(!done_damage) + { + if(!BUFF_HAS_SPACE(&from->done_damage_to_this_swing)) + { + BUFF_REMOVE_FRONT(&from->done_damage_to_this_swing); + Log("Too many things to do damage to...\n"); + assert(false); + } + BUFF_APPEND(&to_return, looking_for); + BUFF_APPEND(&from->done_damage_to_this_swing, looking_for); + } + } + } + return to_return; +} + Vec2 entity_aabb_size(Entity *e) { if(e->is_character == ENTITY_PLAYER) @@ -514,6 +567,10 @@ Vec2 entity_aabb_size(Entity *e) { return V2(TILE_SIZE*1.10f, TILE_SIZE*1.10f); } + else if(e->npc_kind == SKELETON) + { + return V2(TILE_SIZE*1.0f, TILE_SIZE*1.0f); + } else { assert(false); @@ -705,6 +762,37 @@ AnimatedSprite death_idle = .horizontal_diff_btwn_frames = 100.0f, .region_size = {100.0f, 100.0f}, }; +AnimatedSprite skeleton_idle = +{ + .img = &image_skeleton, + .time_per_frame = 0.15, + .num_frames = 6, + .start = {0.0f, 0.0f}, + .horizontal_diff_btwn_frames = 80.0, + .offset = {0.0f, 20.0f}, + .region_size = {80.0f, 80.0f}, +}; +AnimatedSprite skeleton_swing_sword = +{ + .img = &image_skeleton, + .time_per_frame = 0.10, + .num_frames = 6, + .start = {0.0f, 240.0f}, + .horizontal_diff_btwn_frames = 80.0, + .offset = {0.0f, 20.0f}, + .region_size = {80.0f, 80.0f}, +}; +AnimatedSprite skeleton_run = +{ + .img = &image_skeleton, + .time_per_frame = 0.07, + .num_frames = 8, + .start = {0.0f, 160.0f}, + .horizontal_diff_btwn_frames = 80.0, + .offset = {0.0f, 20.0f}, + .region_size = {80.0f, 80.0f}, +}; + sg_image image_font = {0}; float font_line_advance = 0.0f; @@ -1129,7 +1217,7 @@ Vec2 into_clip_space(Vec2 screen_space_point) // The image region is in pixel space of the image void draw_quad(DrawParams d) { - PROFILE_SCOPE("quad") + PROFILE_SCOPE("Draw quad") { quad_fs_params_t params = {0}; params.tint[0] = d.tint.R; @@ -1258,6 +1346,7 @@ double anim_sprite_duration(AnimatedSprite *s) void draw_animated_sprite(AnimatedSprite *s, double elapsed_time, bool flipped, Vec2 pos, Color tint) { + pos = AddV2(pos, s->offset); sg_image spritesheet_img = *s->img; int index = (int)floor(elapsed_time/s->time_per_frame) % s->num_frames; if(s->no_wrap) @@ -1687,48 +1776,51 @@ void draw_dialog_panel(Entity *talking_to) float new_line_height = dialog_panel.lower_right.Y; int i = 0; //BUFF_ITER(Sentence, &talking_to->player_dialog) - BUFF_ITER_EX(Sentence, &talking_to->player_dialog, talking_to->player_dialog.cur_index-1, it >= &talking_to->player_dialog.data[0], it--) + if(talking_to->player_dialog.cur_index > 0) { - bool player_talking = i % 2 != 0; // iterating backwards - Color *colors = calloc(sizeof(*colors), it->cur_index); - bool in_astrix = false; - for(int char_i = 0; char_i < it->cur_index; char_i++) + BUFF_ITER_EX(Sentence, &talking_to->player_dialog, talking_to->player_dialog.cur_index-1, it >= &talking_to->player_dialog.data[0], it--) { - bool set_in_astrix_false = false; - if(it->data[char_i] == '*') + bool player_talking = i % 2 != 0; // iterating backwards + Color *colors = calloc(sizeof(*colors), it->cur_index); + bool in_astrix = false; + for(int char_i = 0; char_i < it->cur_index; char_i++) { - if(in_astrix) + bool set_in_astrix_false = false; + if(it->data[char_i] == '*') { - set_in_astrix_false = true; + if(in_astrix) + { + set_in_astrix_false = true; + } + else + { + in_astrix = true; + } } - else + if(player_talking) { - in_astrix = true; - } - } - if(player_talking) - { - colors[char_i] = BLACK; - } - else - { - if(in_astrix) - { - colors[char_i] = colhex(0xffdf24); + colors[char_i] = BLACK; } else { - colors[char_i] = colhex(0x345e22); + if(in_astrix) + { + colors[char_i] = colhex(0xffdf24); + } + else + { + colors[char_i] = colhex(0x345e22); + } } + if(set_in_astrix_false) in_astrix = false; } - if(set_in_astrix_false) in_astrix = false; - } - float measured_line_height = draw_wrapped_text(true, V2(dialog_panel.upper_left.X, new_line_height), dialog_panel.lower_right.X - dialog_panel.upper_left.X, it->data, colors, 0.5f, true, dialog_panel); - new_line_height += (new_line_height - measured_line_height); - draw_wrapped_text(false, V2(dialog_panel.upper_left.X, new_line_height), dialog_panel.lower_right.X - dialog_panel.upper_left.X, it->data, colors, 0.5f, true, dialog_panel); + float measured_line_height = draw_wrapped_text(true, V2(dialog_panel.upper_left.X, new_line_height), dialog_panel.lower_right.X - dialog_panel.upper_left.X, it->data, colors, 0.5f, true, dialog_panel); + new_line_height += (new_line_height - measured_line_height); + draw_wrapped_text(false, V2(dialog_panel.upper_left.X, new_line_height), dialog_panel.lower_right.X - dialog_panel.upper_left.X, it->data, colors, 0.5f, true, dialog_panel); - free(colors); - i++; + free(colors); + i++; + } } dbgrect(dialog_panel); @@ -1953,6 +2045,8 @@ void frame(void) } } #endif + if(fabsf(it->vel.x) > 0.01f) + it->facing_left = it->vel.x < 0.0f; if(it->is_npc) { @@ -1970,7 +2064,7 @@ void frame(void) { it->pos = LerpV2(it->pos, dt*5.0f, it->target_goto); } - if(it->aggressive) + if(it->npc_kind == OLD_MAN && it->aggressive) { draw_dialog_panel(it); Entity *targeting = player; @@ -2004,7 +2098,67 @@ void frame(void) Color col = LerpV4(WHITE, it->damage, RED); if(it->npc_kind == OLD_MAN) { - draw_animated_sprite(&old_man_idle, elapsed_time, false, it->pos, col); + bool face_left = false; + if(it->aggressive) + { + face_left = SubV2(player->pos, it->pos).x < 0.0f; + } + draw_animated_sprite(&old_man_idle, elapsed_time, face_left, it->pos, col); + } + else if(it->npc_kind == SKELETON) + { + if(fabsf(it->vel.x) > 0.01f) + it->facing_left = it->vel.x < 0.0f; + + Color col = WHITE; + it->pos = move_and_slide(it, it->pos, MulV2F(it->vel, pixels_per_meter * dt)); + AABB weapon_aabb = entity_sword_aabb(it, 30.0f, 18.0f); + dbgrect(weapon_aabb); + Vec2 target_vel = {0}; + it->pos = AddV2(it->pos, MulV2F(it->vel, dt)); + Overlapping overlapping_weapon = get_overlapping(cur_level, weapon_aabb); + if(it->swing_timer > 0.0) + { + // swinging sword + draw_animated_sprite(&skeleton_swing_sword, it->swing_timer, it->facing_left, it->pos, col); + it->swing_timer += dt; + if(it->swing_timer >= anim_sprite_duration(&skeleton_swing_sword)) + { + it->swing_timer = 0.0; + } + if(it->swing_timer >= 0.4f) + { + SwordToDamage to_damage = entity_sword_to_do_damage(it, overlapping_weapon); + Entity *from = it; + BUFF_ITER(Entity *, &to_damage) + { + request_do_damage(*it, from->pos, 0.2f); + } + } + } + else + { + // in huntin' range + if(LenV2(SubV2(player->pos, it->pos)) < 250.0f) + { + Entity *skele = it; + BUFF_ITER(Overlap, &overlapping_weapon) + { + if(it->e && it->e->is_character) + { + skele->swing_timer += dt; + BUFF_CLEAR(&skele->done_damage_to_this_swing); + } + } + draw_animated_sprite(&skeleton_run, elapsed_time, it->facing_left, it->pos, col); + target_vel = MulV2F(NormV2(SubV2(player->pos, it->pos)), 4.0f); + } + else + { + draw_animated_sprite(&skeleton_idle, elapsed_time, it->facing_left, it->pos, col); + } + } + it->vel = LerpV2(it->vel, dt*8.0f, target_vel); } else if(it->npc_kind == DEATH) { @@ -2069,6 +2223,7 @@ void frame(void) if(entity_talkable) entity_talkable = entity_talkable && !it->is_tile; if(entity_talkable) entity_talkable = entity_talkable && it->e->is_npc; if(entity_talkable) entity_talkable = entity_talkable && !it->e->aggressive; + if(entity_talkable) entity_talkable = entity_talkable && !(it->e->npc_kind == SKELETON); #ifdef WEB if(entity_talkable) entity_talkable = entity_talkable && it->e->gen_request_id == 0; #endif @@ -2189,8 +2344,6 @@ draw_dialog_panel(talking_to); } else { - if(fabsf(player->vel.x) > 0.01f) - player->facing_left = player->vel.x < 0.0f; } } else if(player->state == CHARACTER_IDLE) @@ -2207,44 +2360,12 @@ draw_dialog_panel(talking_to); } else if(player->state == CHARACTER_ATTACK) { - AABB weapon_aabb = {0}; - if(player->facing_left) - { - weapon_aabb = (AABB){ - .upper_left = AddV2(player->pos, V2(-40.0, 25.0)), - .lower_right = AddV2(player->pos, V2(0.0, -25.0)), - }; - } - else - { - weapon_aabb = (AABB){ - .upper_left = AddV2(player->pos, V2(0.0, 25.0)), - .lower_right = AddV2(player->pos, V2(40.0, -25.0)), - }; - } + AABB weapon_aabb = entity_sword_aabb(player, 40.0f, 25.0f); dbgrect(weapon_aabb); - Overlapping overlapping_weapon = get_overlapping(cur_level, weapon_aabb); - BUFF_ITER(Overlap, &overlapping_weapon) + SwordToDamage to_damage = entity_sword_to_do_damage(player, get_overlapping(cur_level, weapon_aabb)); + BUFF_ITER(Entity*, &to_damage) { - if(!it->is_tile && it->e != player) - { - bool done_damage = false; - Entity *looking_for = it->e; - BUFF_ITER(Entity*, &player->done_damage_to_this_swing) - { - if(*it == looking_for) done_damage = true; - } - if(!done_damage) - { - if(!BUFF_HAS_SPACE(&player->done_damage_to_this_swing)) - { - BUFF_REMOVE_FRONT(&player->done_damage_to_this_swing); - Log("Too many things to do damage to...\n"); - } - BUFF_APPEND(&player->done_damage_to_this_swing, looking_for); - request_do_damage(it->e, player->pos, 0.2f); - } - } + request_do_damage(*it, player->pos, 0.2f); } player->swing_progress += dt; diff --git a/profiling.h b/profiling.h index 32962e4..d98b6b1 100644 --- a/profiling.h +++ b/profiling.h @@ -101,5 +101,5 @@ void init_profiling_mythread(uint32_t id) { (void)id; } void end_profiling() {} void end_profiling_mythread() {} -#define PROFILE_SCOPE(name) DeferLoop((void)0, (void)0) // so loop stuff in it doesn't break +#define PROFILE_SCOPE(name) for(int _i_ = 0; _i_ == 0; _i_ += 1) #endif diff --git a/run_codegen.bat b/run_codegen.bat index aa44c2d..8af2b50 100644 --- a/run_codegen.bat +++ b/run_codegen.bat @@ -13,6 +13,7 @@ copy "EPIC RPG World Pack - Ancient Ruins V 1.7\EPIC RPG World Pack - Ancient Ru copy "EPIC RPG World Pack - Ancient Ruins V 1.7\EPIC RPG World Pack - Ancient Ruins V 1.7\TiledMap Editor\Terrain - Ancient Ruins.tsx" "assets\copyrighted\ruins_ancient.tsx" || goto :error copy "EPIC RPG World Pack - Ancient Ruins V 1.7\EPIC RPG World Pack - Ancient Ruins V 1.7\Tilesets\Tileset-Terrain.png" "assets\copyrighted\ruins_ancient.png" || goto :error copy "Undead - Pixel Art Characters\Undead - Pixel Art Characters\Sprites\Wraith_Red.png" "assets\copyrighted\wraith.png" || goto :error +copy "Undead - Pixel Art Characters\Undead - Pixel Art Characters\Sprites\Skeleton_Blue.png" "assets\copyrighted\skeleton.png" || goto :error rmdir /S /q gen mkdir gen