From c1122f511f20583950515a6fbb77c7635d7596fe Mon Sep 17 00:00:00 2001 From: Lephenixnoir Date: Sat, 31 Dec 2022 18:26:57 +0100 Subject: [PATCH] add core scoring mechanics and score computation --- CMakeLists.txt | 2 + TODO | 4 ++ assets-cg/fxconv-metadata.txt | 2 + assets-cg/hud_arcade_font2.png | Bin 0 -> 3147 bytes assets-cg/hud_arcade_font2.xcf | Bin 0 -> 12261 bytes assets-cg/hud_top.png | Bin 0 -> 7074 bytes src/comp/fighter.c | 6 ++- src/comp/fighter.h | 2 + src/game.c | 69 ++++++++++++++++++++++++++++++++- src/game.h | 29 ++++++++++++++ src/main.c | 28 ++++++++++--- src/render.c | 60 ++++++++++++++++++++-------- src/render.h | 5 ++- src/util.c | 3 ++ 14 files changed, 184 insertions(+), 26 deletions(-) create mode 100644 assets-cg/hud_arcade_font2.png create mode 100644 assets-cg/hud_arcade_font2.xcf create mode 100644 assets-cg/hud_top.png diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d3f1af..5670453 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,6 +63,7 @@ set(ASSETS # HUD assets-cg/hud.png assets-cg/hud_arcade_font.png + assets-cg/hud_arcade_font2.png assets-cg/hud_backpack.ase assets-cg/hud_combo.png assets-cg/hud_delay.png @@ -71,6 +72,7 @@ set(ASSETS assets-cg/hud_life.png assets-cg/hud_panel.png assets-cg/hud_small.png + assets-cg/hud_top.png assets-cg/hud_xp.ase assets-cg/skillicons.png assets-cg/font_hud.png diff --git a/TODO b/TODO index 866b9ad..a3de8d7 100644 --- a/TODO +++ b/TODO @@ -11,6 +11,7 @@ Pixel art to do * Skeletons * More tifuciles (including mamafucile) * Crypt boss (Dracula / Skeleton archmagus / etc) +- Better dash animation - Environment damage - Particles after monsters' deaths? Slime puddles, dark enemy outlines, ... @@ -35,6 +36,9 @@ Core mechanics: Content: * Additional skills +Bugfixes: +* Fix ability to attack continuously by holding SHIFT + Extra details ============= diff --git a/assets-cg/fxconv-metadata.txt b/assets-cg/fxconv-metadata.txt index 511f6e5..a6873ba 100644 --- a/assets-cg/fxconv-metadata.txt +++ b/assets-cg/fxconv-metadata.txt @@ -17,6 +17,8 @@ hud_backpack.ase: hud_arcade_font.png: profile: p8 +hud_arcade_font2.png: + profile: p8 font_rogue.png: type: font diff --git a/assets-cg/hud_arcade_font2.png b/assets-cg/hud_arcade_font2.png new file mode 100644 index 0000000000000000000000000000000000000000..3c7e89098855f610da7e71acb81c62131a85b016 GIT binary patch literal 3147 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 == 0) return 0; + bool full_health = (f->HP >= f->HP_max); base_damage = max(base_damage - f->DEF, 0); int variation = (base_damage >= 4) ? (rand() % (base_damage / 4)) : 0; @@ -71,8 +72,11 @@ int fighter_damage(entity_t *e, int base_damage) else f->HP -= damage; if(f->enemy) { - if(f->HP == 0) + if(f->HP == 0) { visible_set_anim(e, f->enemy->id->anim_death, 4); + if(full_health) + f->one_shot_killed = true; + } else visible_set_anim(e, f->enemy->id->anim_hit, 3); } diff --git a/src/comp/fighter.h b/src/comp/fighter.h index 5179b05..1063a00 100644 --- a/src/comp/fighter.h +++ b/src/comp/fighter.h @@ -52,6 +52,8 @@ typedef struct fixed_t stun_delay; fixed_t invulnerability_delay; fixed_t speed_delay; + /* Whether entity was one-shot killed */ + bool one_shot_killed; } fighter_t; diff --git a/src/game.c b/src/game.c index d45d89f..91a0c9d 100644 --- a/src/game.c +++ b/src/game.c @@ -51,6 +51,9 @@ bool game_load(game_t *g, level_t const *level) g->menu_time = fix(0); g->menu_open = false; g->menu_cursor = 0; + g->hud_wave_number_timer = fix(0); + + memset(&g->score, 0, sizeof g->score); game_next_event(g); return true; @@ -115,6 +118,11 @@ bool game_current_event_finished(game_t const *g) void game_next_event(game_t *g) { + if(g->event >= 0 && g->level + && g->level->events[g->event].type == LEVEL_EVENT_WAVE) { + g->score.waves_survived++; + } + if(g->event >= g->level->event_count) return; g->event++; @@ -128,6 +136,7 @@ void game_next_event(game_t *g) if(event && event->type == LEVEL_EVENT_WAVE) { g->wave_number++; + g->hud_wave_number_timer = fix(1); /* Copy the amounts of monsters to spawn for the next wave */ g->wave_left = malloc(event->wave->entry_count * sizeof *g->wave_left); @@ -135,8 +144,6 @@ void game_next_event(game_t *g) for(int i = 0; i < event->wave->entry_count; i++) g->wave_left[i] = event->wave->entries[i].amount; - - game_message(g, fix(2.0), "Wave %d incoming!", g->wave_number); } if(event && event->type == LEVEL_EVENT_ITEM) { int x=-1, y=-1; @@ -207,6 +214,29 @@ void game_hud_anim_backpack_close(game_t *g) g->hud_backpack_anim.elapsed = 0; } +static int game_score_combo_chain(int chain) +{ + return (chain * chain) / 2; +} + +static int game_score_simult_kills(int kills) +{ + return (kills < 3) ? 0 : kills * kills; +} + +int game_compute_score(game_t const *g) +{ + score_t const *s = &g->score; + return + s->kill_number * 3 + + s->longest_combo_chain * 8 + + s->combo_chains + + s->largest_simult_kill * 17 + + s->simult_kills + + s->one_shot_kills * 4 + + s->waves_survived * 16; +} + //--- // Object management functions //--- @@ -299,6 +329,16 @@ void game_remove_dead_entities(game_t *g) g->combo++; g->combo_health = fix(1); + /* Update score-related metrics */ + g->score.kill_number++; + g->score.one_shot_kills += (f->one_shot_killed == true); + g->score.longest_combo_chain = max(g->score.longest_combo_chain, + g->combo); + g->score.current_simult_kills++; + g->score.largest_simult_kill = max(g->score.largest_simult_kill, + g->score.current_simult_kills); + g->score.current_simult_kill_timer = fix(1); + entity_mark_to_delete(e); } } @@ -457,6 +497,8 @@ void game_update_animations(game_t *g, fixed_t dt, fixed_t dt_rt) anim_state_update(&g->hud_xp_anim, dt_rt); anim_state_update(&g->hud_backpack_anim, dt_rt); + + g->hud_wave_number_timer = max(0, g->hud_wave_number_timer - dt_rt); } void game_update_effects(game_t *g, fixed_t dt) @@ -475,8 +517,31 @@ void game_update_effects(game_t *g, fixed_t dt) g->combo_health -= dt / 5; if(g->combo_health <= 0) { g->combo_health = 0; + + if(g->combo >= 30) + game_message(g, fix(2.0), "Fabulous %d-combo!", g->combo); + else if(g->combo >= 10) + game_message(g, fix(2.0), "Excellent %d-combo!", g->combo); + + g->score.combo_chains += game_score_combo_chain(g->combo); g->combo = 0; } + + g->score.current_simult_kill_timer -= dt * 4; + if(g->score.current_simult_kill_timer <= 0) { + g->score.current_simult_kill_timer = 0; + + if(g->score.current_simult_kills >= 8) + game_message(g, fix(2.0), "\"This is a massacre!\""); + else if(g->score.current_simult_kills >= 5) + game_message(g, fix(2.0), "\"Begone, idiots!\""); + else if(g->score.current_simult_kills >= 3) + game_message(g, fix(2.0), "\"Come and get me!\""); + + g->score.simult_kills += + game_score_simult_kills(g->score.current_simult_kills); + g->score.current_simult_kills = 0; + } } void game_update_aoes(game_t *g, fixed_t dt) diff --git a/src/game.h b/src/game.h index 2e2d8c6..9d3344e 100644 --- a/src/game.h +++ b/src/game.h @@ -13,6 +13,28 @@ #include "comp/entity.h" +typedef struct score { + /* Number of kills */ + int kill_number; + /* Longest combo chain so far */ + int longest_combo_chain; + /* Total (accumulated) combo chain score */ + int combo_chains; + /* Largest simultaneous kill streak */ + int largest_simult_kill; + /* Total (accumulated) kill streak score */ + int simult_kills; + /* Number of one shots kills */ + int one_shot_kills; + /* Number of waves survived */ + int waves_survived; + + /* Current kill streak and its timer (basically a faster combo) */ + int current_simult_kills; + fixed_t current_simult_kill_timer; + +} score_t; + typedef struct game { /* The map's coordinate system is the primary coordinate system in all of this game's code */ @@ -53,6 +75,8 @@ typedef struct game { at a variable rate) */ int combo; fixed_t combo_health; + /* Score information */ + score_t score; /* XP bar animation */ anim_state_t hud_xp_anim; @@ -69,6 +93,8 @@ typedef struct game { /* Current UI message, and how long it stays on (if not overwritten) */ char const *message; fixed_t message_time; + /* Flashing wave number timer */ + fixed_t hud_wave_number_timer; } game_t; @@ -101,6 +127,9 @@ void game_hud_anim_backpack_item(game_t *g); void game_hud_anim_backpack_open(game_t *g); void game_hud_anim_backpack_close(game_t *g); +/* Compute total score */ +int game_compute_score(game_t const *g); + //--- // Managing dynamic game elements //--- diff --git a/src/main.c b/src/main.c index ed5a034..ae6be20 100644 --- a/src/main.c +++ b/src/main.c @@ -194,12 +194,9 @@ int main(void) /* Developer/tweaking menu */ if(debug.show_vars) { uint32_t *vram = (void *)gint_vram; - for(int y = 0; y < 224; y++) { - for(int i = 0; i < 396/4; i++) - vram[i] = (vram[i] & 0xf7def7de) >> 1; - vram += 396/2; + for(int i = 0; i < 396 * 224 / 2; i++) { + vram[i] = (vram[i] & 0xf7def7de) >> 1; } - uint16_t gray = C_RGB(16, 16, 16); dprint(3, 40, C_WHITE, "Player speed: %g tiles/s", @@ -217,6 +214,27 @@ int main(void) dprint(3, 145, C_WHITE, "Dash duration: %g s", f2double(player_data.mechanical_limits.dash_duration)); dprint(15, 160, gray, "[)] -/+ [sin]"); + + dprint(DWIDTH/2, 40, C_WHITE, "Score: %d", + game_compute_score(&game)); + dprint(DWIDTH/2, 55, C_WHITE, "kill_number: %d", + game.score.kill_number); + dprint(DWIDTH/2, 70, C_WHITE, "longest_combo_chain: %d", + game.score.longest_combo_chain); + dprint(DWIDTH/2, 85, C_WHITE, "combo_chains: %d", + game.score.combo_chains); + dprint(DWIDTH/2, 100, C_WHITE, "largest_simult_kill: %d", + game.score.largest_simult_kill); + dprint(DWIDTH/2, 115, C_WHITE, "simult_kills: %d", + game.score.simult_kills); + dprint(DWIDTH/2, 130, C_WHITE, "one_shot_kills: %d", + game.score.one_shot_kills); + dprint(DWIDTH/2, 145, C_WHITE, "waves_survived: %d", + game.score.waves_survived); + dprint(DWIDTH/2, 160, C_WHITE, "current_simult_kills: %d", + game.score.current_simult_kills); + dprint(DWIDTH/2, 175, C_WHITE, "*_timer: %f", + f2double(game.score.current_simult_kill_timer)); } if(debug.dev_menu_view) { diff --git a/src/render.c b/src/render.c index 2863505..9ae263f 100644 --- a/src/render.c +++ b/src/render.c @@ -360,6 +360,14 @@ static void render_info(int x, int y, game_t const *g) snprintf(str, 32, "Wave %d", level_wave_count(g->level)); dsize(str, NULL, &intro_w, NULL); snprintf(str, 32, "Wave %d", g->wave_number); + + int wave_color_r = fround(g->hud_wave_number_timer * 31); + if(g->hud_wave_number_timer) { + dtext(x+8, y-2, C_RGB(wave_color_r, 0, 0), str); + dtext(x+8, y, C_RGB(wave_color_r, 0, 0), str); + dtext(x+7, y-1, C_RGB(wave_color_r, 0, 0), str); + dtext(x+9, y-1, C_RGB(wave_color_r, 0, 0), str); + } dtext(x+8, y-1, C_WHITE, str); x += intro_w + 20; @@ -473,30 +481,34 @@ int render_small_text(int x, int y, int color, char const *text, int size) return 0; } -static int render_arcade_char_size(int c) +static int render_arcade_char_size(int c, int font_size) { if(c < '0' || c > '9') return 0; - return 7 - (c == '1' || c == '7') + (c == '4'); + if(font_size == 1) + return 7 - (c == '1' || c == '7') + (c == '4'); + if(font_size == 2) + return 8 + (c == '0' || c == '4'); + return 0; } - -int render_arcade_dsize(int value) +int render_arcade_dsize(int value, int font_size) { char str[16]; sprintf(str, "%d", value); int pixels = 0; for(int i = 0; str[i]; i++) - pixels += render_arcade_char_size(str[i]); - return pixels; - + pixels += render_arcade_char_size(str[i], font_size); return pixels; } -void render_arcade(int x, int y, int halign, int value, int color_style) +void render_arcade(int x, int y, int halign, int value, int color_style, + int font_size) { extern bopti_image_t img_hud_arcade_font; - int w = render_arcade_dsize(value); + extern bopti_image_t img_hud_arcade_font2; + + int w = render_arcade_dsize(value, font_size); if(halign == DTEXT_RIGHT) x -= w; else if(halign == DTEXT_CENTER) @@ -510,9 +522,14 @@ void render_arcade(int x, int y, int halign, int value, int color_style) sprintf(str, "%d", value); for(int i = 0; str[i]; i++) { - int w = render_arcade_char_size(str[i]); - dsubimage(x, y, &img_hud_arcade_font, 1 + 9 * (str[i] - '0'), - 1 + 9 * color_style, w, 8, DIMAGE_NONE); + int w = render_arcade_char_size(str[i], font_size); + + if(font_size == 1) + dsubimage(x, y, &img_hud_arcade_font, 1 + 9 * (str[i] - '0'), + 1 + 9 * color_style, w, 8, DIMAGE_NONE); + if(font_size == 2) + dsubimage(x, y, &img_hud_arcade_font2, 1 + 10 * (str[i] - '0'), + 1 + 12 * color_style, w, 12, DIMAGE_NONE); x += w; } } @@ -624,12 +641,23 @@ void render_game(game_t const *g, bool show_hitboxes) int HUD_Y = cubic(DHEIGHT+30, DHEIGHT, gui_time, MAX_GUI_TIME); int HEADER_Y = cubic(-15, 2, gui_time, MAX_GUI_TIME); + /* Render score box */ + extern bopti_image_t img_hud_top; + dimage(6, HEADER_Y - 2, &img_hud_top); + int score = game_compute_score(g); + render_arcade(42, HEADER_Y, DTEXT_LEFT, score, + 0 + (score >= 100) + (score >= 1000), 2); + /* Render wave information */ - render_info(0, HEADER_Y, g); + render_info(90, HEADER_Y, g); /* Render current message */ - if(g->message) - dtext(8, HEADER_Y + 13, C_RGB(20, 20, 20), g->message); + if(g->message) { + dtext_opt(DWIDTH - 8, HEADER_Y + 16, RGB24(0x15171a), C_NONE, + DTEXT_RIGHT, DTEXT_TOP, g->message, -1); + dtext_opt(DWIDTH - 8, HEADER_Y + 15, RGB24(0xabb1ba), C_NONE, + DTEXT_RIGHT, DTEXT_TOP, g->message, -1); + } /* Render HUD */ extern bopti_image_t img_hud; @@ -704,7 +732,7 @@ void render_game(game_t const *g, bool show_hitboxes) dsubimage(224, HUD_Y - 9 - fill_height, &img_hud_combo, 0, img_hud_combo.height - fill_height, img_hud_combo.width, fill_height, DIMAGE_NONE); - render_arcade(234, HUD_Y-25, DTEXT_CENTER, g->combo, -1); + render_arcade(234, HUD_Y-25, DTEXT_CENTER, g->combo, -1, 1); if(g->menu_time > 0) { int x1 = cubic(-130, 0, g->menu_time, fix(1)); diff --git a/src/render.h b/src/render.h index 14bb471..a532d5d 100644 --- a/src/render.h +++ b/src/render.h @@ -76,5 +76,6 @@ void render_pfg_all2one(pfg_all2one_t const *paths, camera_t const *c, int render_small_text(int x, int y, int color, char const *text, int size); /* Colored arcade font. */ -int render_arcade_dsize(int value); -void render_arcade(int x, int y, int halign, int value, int color_style); +int render_arcade_dsize(int value, int font_size); +void render_arcade(int x, int y, int halign, int value, int color_style, + int font_size); diff --git a/src/util.c b/src/util.c index ac00017..88e0723 100644 --- a/src/util.c +++ b/src/util.c @@ -212,6 +212,9 @@ void font_damage_print(int x, int y, int color, int align_x, int align_y, int cubic(int start, int end, fixed_t t, fixed_t tmax) { + if(t >= tmax) + return end; + t = fdiv(t, tmax); fixed_t x = fix(1.0) - fmul(fmul(fix(1.0)-t, fix(1.0)-t), fix(1.0)-t); x = fix(start) + fmul(fix(end - start), x);