From 66ad3253926a7c7ea71429213f4bccfc7f6dd792 Mon Sep 17 00:00:00 2001 From: Elias Fleckenstein Date: Mon, 14 Jun 2021 17:39:27 +0200 Subject: [PATCH] Fancy up score display --- doc/GAMEPLAY.md | 7 ++-- plugins/game/game.c | 42 ++++++++++++++++++++++++ plugins/game/game.h | 1 + plugins/score/score.c | 75 +++++++++++++++++++++++++++++++++++++++++-- plugins/score/score.h | 1 + 5 files changed, 122 insertions(+), 4 deletions(-) diff --git a/doc/GAMEPLAY.md b/doc/GAMEPLAY.md index bdcc1bb..5af1300 100644 --- a/doc/GAMEPLAY.md +++ b/doc/GAMEPLAY.md @@ -8,6 +8,9 @@ You are in a pixely dungeon. There is no exit, the goal of the game is to surviv You can use WASD to move up / left / down / right. Use Q to quit and Space to shoot fireballs if you have them in your inventory. Use E to use your sword if you have one in your inventory. To navigate the inventory, use the arrow keys (up and down) to select an item and then press enter to use it. +## Score +You can gain score from various things explained later. You need a certain amount of score to reach the next level; e.g. to reach level 1 when you are level 0 you need 5 score, to reach level 2 you need 10, for level 3 15 and so on. When you reach a new level, your score is reset, to to reach level 3 you'll need 5 + 10 + 15 = 30 score in total. + ## The map The map consists of nodes and entities. The nodes are static; they cannot move and you cannot break or replace them. They are automatically generated on startup. Some nodes are solid, which means entities collide with them, others are not. @@ -64,9 +67,9 @@ In the lower center of your screen you can see 10 hearts. Depending on whether t If you use an item that needs to recharge after being used, a recharge meter will show. It tells you the recharge percentage and visualizes it using a bar. You cannot use -### The score count +### The score bar -In the upper light corner there is a display that tells you your current score. +On the top of the screen a green bar displays how close you are to reaching the next score level. It also shows the current level in roman numerals at the center (it will not display the level if the level is 0). ### The inventory diff --git a/plugins/game/game.c b/plugins/game/game.c index 152a5c9..94ad148 100644 --- a/plugins/game/game.c +++ b/plugins/game/game.c @@ -129,6 +129,48 @@ double calculate_dtime(struct timespec from, struct timespec to) return (double) (to.tv_sec - from.tv_sec) + (double) (to.tv_nsec - from.tv_nsec) / 1000000000.0; } +/*struct roman_conversion_rule +{ + int number; + const char *symbol; +};*/ + +static struct +{ + int number; + const char *symbol; +} roman_ct[13] = { + {1000, "M"}, + {900, "CM"}, + {500, "D"}, + {400, "CD"}, + {100, "C"}, + {90, "XC"}, + {50, "L"}, + {40, "XL"}, + {10, "X"}, + {9, "IX"}, + {5, "V"}, + {4, "IV"}, + {1, "I"} +}; + +void get_roman_numeral(int number, char **ptr, size_t *len) +{ + *ptr = NULL; + *len = 0; + + for (int i = 0; i < 13; i++) { + while (number >= roman_ct[i].number) { + number -= roman_ct[i].number; + size_t old_len = *len; + *len += 1 + i % 2; + *ptr = realloc(*ptr, *len + 1); + strcpy(*ptr + old_len, roman_ct[i].symbol); + } + } +} + /* Game-related utility functions */ void quit() diff --git a/plugins/game/game.h b/plugins/game/game.h index 60c1d61..21a9433 100644 --- a/plugins/game/game.h +++ b/plugins/game/game.h @@ -113,6 +113,7 @@ int max(int a, int b); int min(int a, int b); void *make_buffer(void *ptr, size_t size); double calculate_dtime(struct timespec from, struct timespec to); +void get_roman_numeral(int number, char **ptr, size_t *len); void quit(); bool player_dead(); diff --git a/plugins/score/score.c b/plugins/score/score.c index 7ad8b66..c310d3b 100644 --- a/plugins/score/score.c +++ b/plugins/score/score.c @@ -1,11 +1,40 @@ #include +#include +#include +#include #include "../game/game.h" static int score = 0; +static int needed_score = 5; +static int level = 0; +static char *level_symbol = NULL; +static size_t level_symbol_len = 0; +static double score_timer = 0.0; +static double level_timer = 0.0; + +static void level_up() +{ + level += 1; + needed_score = (level + 1) * 5; + + if (level_symbol) + free(level_symbol); + + get_roman_numeral(level, &level_symbol, &level_symbol_len); + + level_timer = 2.0; +} void add_score(int s) { score += s; + + if (score >= needed_score) { + score -= needed_score; + level_up(); + } else { + score_timer = 2.0; + } } int get_score() @@ -13,14 +42,56 @@ int get_score() return score; } +int get_level() +{ + return level; +} + static void render_score(struct winsize ws) { - (void) ws; + int bar_flash = clamp(score_timer * 255, 0, 255); + set_color((struct color) {bar_flash, 255, bar_flash}, true); - printf("\e[32m\e[3mScore:\e[23m %d", score); + int level_flash = clamp(level_timer * 255, 0, 255); + set_color((struct color) {255, 255, 255 - level_flash}, false); + + size_t level_len = level_symbol_len > 0 ? 6 + level_symbol_len + 1 : 0; + char level_display[level_len]; + + if (level_len > 0) + sprintf(level_display, "Level %s", level_symbol); + + int bar = (double) score / (double) needed_score * ws.ws_col; + int level_start = (ws.ws_col - level_len) / 2; + int level_end = level_start + level_len - 1; + + for (int i = 0; i < ws.ws_col; i++) { + if (i == bar) + printf("\e[49m"); + + if (i >= level_start && i < level_end) + printf("%c", level_display[i - level_start]); + else + printf(" "); + } + + printf("\n"); +} + +static void score_globalstep(double dtime) +{ + if (level_timer > 0.0) + level_timer -= dtime; + + if (score_timer > 0.0) + score_timer -= dtime * 3.0; } __attribute__ ((constructor)) static void init() { register_render_component(&render_score); + register_globalstep((struct globalstep) { + .run_if_dead = true, + .callback = &score_globalstep, + }); } diff --git a/plugins/score/score.h b/plugins/score/score.h index 8ee531a..4d02903 100644 --- a/plugins/score/score.h +++ b/plugins/score/score.h @@ -3,5 +3,6 @@ void add_score(int s); int get_score(); +int get_level(); #endif