add core scoring mechanics and score computation
This commit is contained in:
parent
03e9a822e3
commit
c1122f511f
|
@ -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
|
||||
|
|
4
TODO
4
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
|
||||
=============
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@ hud_backpack.ase:
|
|||
|
||||
hud_arcade_font.png:
|
||||
profile: p8
|
||||
hud_arcade_font2.png:
|
||||
profile: p8
|
||||
|
||||
font_rogue.png:
|
||||
type: font
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 6.9 KiB |
|
@ -61,6 +61,7 @@ int fighter_damage(entity_t *e, int base_damage)
|
|||
|
||||
if(f->HP == 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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
69
src/game.c
69
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)
|
||||
|
|
29
src/game.h
29
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
|
||||
//---
|
||||
|
|
28
src/main.c
28
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) {
|
||||
|
|
60
src/render.c
60
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));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue