From 488ff43f859ce12f79241d418f36e7e8c8a9740a Mon Sep 17 00:00:00 2001 From: Andrei Alexeyev <0x416b617269@gmail.com> Date: Sun, 7 Apr 2019 01:55:13 +0300 Subject: [PATCH] add optional floating scoring text --- src/boss.c | 2 +- src/config.h | 1 + src/item.c | 12 +++++------ src/menu/options.c | 4 ++++ src/player.c | 53 ++++++++++++++++++++++++++++++++++++++++------ src/player.h | 4 ++-- src/stage.c | 2 +- src/stageobjects.c | 3 +++ src/stageobjects.h | 1 + src/stagetext.c | 13 ++++++------ src/stagetext.h | 23 ++++++++++++-------- src/util/crap.h | 12 +++++++++++ 12 files changed, 98 insertions(+), 32 deletions(-) diff --git a/src/boss.c b/src/boss.c index 15f4d672..eba7ef46 100644 --- a/src/boss.c +++ b/src/boss.c @@ -826,7 +826,7 @@ static void boss_give_spell_bonus(Boss *boss, Attack *a, Player *plr) { char diff_bonus_text[6]; snprintf(diff_bonus_text, sizeof(diff_bonus_text), "x%.2f", bonus.diff_multiplier); - player_add_points(plr, bonus.total); + player_add_points(plr, bonus.total, plr->pos); StageTextTable tbl; stagetext_begin_table(&tbl, title, RGB(1, 1, 1), RGB(1, 1, 1), VIEWPORT_W/2, 0, diff --git a/src/config.h b/src/config.h index d04cf29e..3556ce1f 100644 --- a/src/config.h +++ b/src/config.h @@ -113,6 +113,7 @@ CONFIGDEF_INT (POSTPROCESS, "postprocess", 2) \ CONFIGDEF_INT (HEALTHBAR_STYLE, "healthbar_style", 1) \ CONFIGDEF_INT (SKIP_SPEED, "skip_speed", 10) \ + CONFIGDEF_FLOAT (SCORETEXT_ALPHA, "scoretext_alpha", 1) \ KEYDEFS \ CONFIGDEF_INT (GAMEPAD_ENABLED, "gamepad_enabled", 1) \ CONFIGDEF_STRING (GAMEPAD_DEVICE, "gamepad_device", "default") \ diff --git a/src/item.c b/src/item.c index 4eddbe86..5efb0287 100644 --- a/src/item.c +++ b/src/item.c @@ -240,31 +240,31 @@ void process_items(void) { switch(item->type) { case ITEM_POWER: player_add_power(&global.plr, POWER_VALUE); - player_add_points(&global.plr, 25); + player_add_points(&global.plr, 25, item->pos); player_extend_powersurge(&global.plr, PLR_POWERSURGE_POSITIVE_GAIN*3, PLR_POWERSURGE_NEGATIVE_GAIN*3); play_sound("item_generic"); break; case ITEM_POWER_MINI: player_add_power(&global.plr, POWER_VALUE_MINI); - player_add_points(&global.plr, 5); + player_add_points(&global.plr, 5, item->pos); play_sound("item_generic"); break; case ITEM_SURGE: player_extend_powersurge(&global.plr, PLR_POWERSURGE_POSITIVE_GAIN, PLR_POWERSURGE_NEGATIVE_GAIN); - player_add_points(&global.plr, 25); + player_add_points(&global.plr, 25, item->pos); play_sound("item_generic"); break; case ITEM_POINTS: - player_add_points(&global.plr, round(global.plr.point_item_value * item->pickup_value)); + player_add_points(&global.plr, round(global.plr.point_item_value * item->pickup_value), item->pos); play_sound("item_generic"); break; case ITEM_PIV: - player_add_piv(&global.plr, 1); + player_add_piv(&global.plr, 1, item->pos); play_sound("item_generic"); break; case ITEM_VOLTAGE: player_add_voltage(&global.plr, 1); - player_add_piv(&global.plr, 10); + player_add_piv(&global.plr, 10, item->pos); play_sound("item_generic"); break; case ITEM_LIFE: diff --git a/src/menu/options.c b/src/menu/options.c index c92d04ee..b6a28fcf 100644 --- a/src/menu/options.c +++ b/src/menu/options.c @@ -793,6 +793,10 @@ MenuData* create_options_menu(void) { ); bind_addvalue(b, "classic"); bind_addvalue(b, "modern"); + add_menu_entry(m, "Floating score text visibility", do_nothing, + b = bind_scale(CONFIG_SCORETEXT_ALPHA, 0, 1, 0.05) + ); + add_menu_separator(m); add_menu_entry(m, "SFX Volume", do_nothing, diff --git a/src/player.c b/src/player.c index ac461891..23008b08 100644 --- a/src/player.c +++ b/src/player.c @@ -614,7 +614,7 @@ static void player_powersurge_expired(Player *plr) { .angle = M_PI*2*frand(), ); - player_add_points(&global.plr, bonus.score); + player_add_points(&global.plr, bonus.score, plr->pos); ent_area_damage(plr->pos, bonus.discharge_range, &(DamageInfo) { bonus.discharge_damage, DMG_PLAYER_DISCHARGE }, NULL, NULL); stage_clear_hazards_at(plr->pos, bonus.discharge_range, CLEAR_HAZARDS_ALL | CLEAR_HAZARDS_NOW | CLEAR_HAZARDS_SPAWN_VOLTAGE); @@ -1181,7 +1181,7 @@ void player_graze(Player *plr, complex pos, int pts, int effect_intensity, const pos = (pos + plr->pos) * 0.5; - player_add_points(plr, pts); + player_add_points(plr, pts, pos); play_sound("graze"); Color *c = COLOR_COPY(color); @@ -1215,7 +1215,7 @@ static void player_add_fragments(Player *plr, int frags, int *pwhole, int *pfrag int excess_frags = total_frags + frags - maxwhole * maxfrags; if(excess_frags > 0) { - player_add_points(plr, excess_frags * score_per_excess); + player_add_points(plr, excess_frags * score_per_excess, plr->pos); frags -= excess_frags; } @@ -1285,16 +1285,40 @@ void player_add_bombs(Player *plr, int bombs) { player_add_bomb_fragments(plr, PLR_MAX_BOMB_FRAGMENTS); } -void player_add_points(Player *plr, uint points) { +static void scoretext_predraw(StageText *txt, int t, float a) { + float r = bits_to_float((uintptr_t)txt->custom.data1); + txt->pos -= I * cexp(I*r) * a; +} + +static float scoreval_importance(Player *plr, uint points) { + return clamp((double)points / (double)plr->point_item_value, 0, 1); +} + +void player_add_points(Player *plr, uint points, complex location) { plr->points += points; while(plr->points >= plr->extralife_threshold) { plr->extralife_threshold = player_next_extralife_threshold(++plr->extralives_given); player_add_lives(plr, 1); } + + float rnd = nfrand(); + float imp = scoreval_importance(plr, points); + float a = clamp((0.5 + 0.5 * cbrtf(imp)) * config_get_float(CONFIG_SCORETEXT_ALPHA), 0, 1); + + if(a < 1e-4) { + return; + } + + char buf[64]; + format_huge_num(0, points, sizeof(buf), buf); + Color *c = color_mul_scalar(color_lerp(RGB(1.0, 0.8, 0.4), RGB(0.4, 1.0, 0.3), imp), a); + StageText *t = stagetext_add(buf, location, ALIGN_CENTER, get_font("small"), c, 0, 25 + 20 * imp, 10, 20); + t->custom.data1 = (void*)(uintptr_t)float_to_bits(rnd); + t->custom.predraw = scoretext_predraw; } -void player_add_piv(Player *plr, uint piv) { +void player_add_piv(Player *plr, uint piv, complex location) { uint v = plr->point_item_value + piv; if(v > PLR_MAX_PIV || v < plr->point_item_value) { @@ -1302,6 +1326,21 @@ void player_add_piv(Player *plr, uint piv) { } else { plr->point_item_value = v; } + + float rnd = nfrand(); + float a = clamp((0.5 + 0.5 * min(piv/10.0, 1)) * config_get_float(CONFIG_SCORETEXT_ALPHA), 0, 1); + + if(a < 1e-4) { + return; + } + + char buf[64]; + format_huge_num(0, piv, sizeof(buf), buf); + strcat(buf, "v"); + Color *c = color_mul_scalar(RGB(0.5, 0.8, 1.0), a); + StageText *t = stagetext_add(buf, location, ALIGN_CENTER, get_font("small"), c, 0, 35, 10, 20); + t->custom.data1 = (void*)(uintptr_t)float_to_bits(rnd); + t->custom.predraw = scoretext_predraw; } void player_add_voltage(Player *plr, uint voltage) { @@ -1338,14 +1377,14 @@ void player_register_damage(Player *plr, EntityInterface *target, const DamageIn if(target != NULL) { switch(target->type) { case ENT_ENEMY: { - player_add_points(&global.plr, damage->amount * 0.5); pos = ENT_CAST(target, Enemy)->pos; + player_add_points(&global.plr, damage->amount * 0.5, pos); break; } case ENT_BOSS: { - player_add_points(&global.plr, damage->amount * 0.2); pos = ENT_CAST(target, Boss)->pos; + player_add_points(&global.plr, damage->amount * 0.2, pos); break; } diff --git a/src/player.h b/src/player.h index f2edebe5..4aa01ec1 100644 --- a/src/player.h +++ b/src/player.h @@ -201,8 +201,8 @@ void player_add_life_fragments(Player *plr, int frags); void player_add_bomb_fragments(Player *plr, int frags); void player_add_lives(Player *plr, int lives); void player_add_bombs(Player *plr, int bombs); -void player_add_points(Player *plr, uint points); -void player_add_piv(Player *plr, uint piv); +void player_add_points(Player *plr, uint points, complex location); +void player_add_piv(Player *plr, uint piv, complex location); void player_add_voltage(Player *plr, uint voltage); bool player_drain_voltage(Player *plr, uint voltage); void player_extend_powersurge(Player *plr, float pos, float neg); diff --git a/src/stage.c b/src/stage.c index 6c483107..85eaad5f 100644 --- a/src/stage.c +++ b/src/stage.c @@ -715,7 +715,7 @@ static void stage_give_clear_bonus(const StageInfo *stage, StageClearBonus *bonu // TODO: maybe a difficulty multiplier? bonus->total = bonus->base + bonus->voltage + bonus->lives; - player_add_points(&global.plr, bonus->total); + player_add_points(&global.plr, bonus->total, global.plr.pos); } static LogicFrameAction stage_logic_frame(void *arg) { diff --git a/src/stageobjects.c b/src/stageobjects.c index f76b6c14..58036822 100644 --- a/src/stageobjects.c +++ b/src/stageobjects.c @@ -13,18 +13,21 @@ #include "item.h" #include "enemy.h" #include "laser.h" +#include "stagetext.h" #include "aniplayer.h" #define MAX_projectiles 1024 #define MAX_items MAX_projectiles #define MAX_enemies 64 #define MAX_lasers 64 +#define MAX_stagetext 128 #define OBJECT_POOLS \ OBJECT_POOL(Projectile, projectiles) \ OBJECT_POOL(Item, items) \ OBJECT_POOL(Enemy, enemies) \ OBJECT_POOL(Laser, lasers) \ + OBJECT_POOL(StageText, stagetext) \ StageObjectPools stage_object_pools; diff --git a/src/stageobjects.h b/src/stageobjects.h index dfd942ef..82493ea0 100644 --- a/src/stageobjects.h +++ b/src/stageobjects.h @@ -20,6 +20,7 @@ typedef struct StageObjectPools { ObjectPool *items; ObjectPool *enemies; ObjectPool *lasers; + ObjectPool *stagetext; }; ObjectPool *first; diff --git a/src/stagetext.c b/src/stagetext.c index 2a6fa43e..ade7db8d 100644 --- a/src/stagetext.c +++ b/src/stagetext.c @@ -17,10 +17,14 @@ static StageText *textlist = NULL; #define NUM_PLACEHOLDER "........................" StageText* stagetext_add(const char *text, complex pos, Alignment align, Font *font, const Color *clr, int delay, int lifetime, int fadeintime, int fadeouttime) { - StageText *t = malloc(sizeof(StageText)); + StageText *t = (StageText*)objpool_acquire(stage_object_pools.stagetext); list_append(&textlist, t); - t->text = strdup(text); + if(text != NULL) { + assert(strlen(text) < sizeof(t->text)); + strcpy(t->text, text); + } + t->font = font; t->pos = pos; t->align = align; @@ -31,8 +35,6 @@ StageText* stagetext_add(const char *text, complex pos, Alignment align, Font *f t->time.fadeout = fadeouttime; t->time.life = lifetime + fadeouttime; - memset(&t->custom, 0, sizeof(t->custom)); - return t; } @@ -49,8 +51,7 @@ StageText* stagetext_add_numeric(int n, complex pos, Alignment align, Font *font } static void* stagetext_delete(List **dest, List *txt, void *arg) { - free(((StageText*)txt)->text); - free(list_unlink(dest, txt)); + objpool_release(stage_object_pools.stagetext, (ObjectInterface*)list_unlink(dest, txt)); return NULL; } diff --git a/src/stagetext.h b/src/stagetext.h index e4fa7c5d..53a0767f 100644 --- a/src/stagetext.h +++ b/src/stagetext.h @@ -13,6 +13,7 @@ #include "util.h" #include "color.h" +#include "objectpool.h" #include "resource/font.h" typedef struct StageText StageText; @@ -21,15 +22,23 @@ typedef void (*StageTextPreDrawFunc)(StageText* txt, int t, float alpha); typedef struct StageTextTable StageTextTable; +// NOTE: tweaked to consume all padding in StageText, assuming x86_64 ABI +#define STAGETEXT_BUF_SIZE 76 + struct StageText { - LIST_INTERFACE(StageText); + OBJECT_INTERFACE(StageText); - char *text; Font *font; - complex pos; - Alignment align; + + struct { + StageTextPreDrawFunc predraw; + void *data1; + void *data2; + } custom; + Color color; + Alignment align; struct { int spawn; @@ -38,11 +47,7 @@ struct StageText { int fadeout; } time; - struct { - StageTextPreDrawFunc predraw; - void *data1; - void *data2; - } custom; + char text[STAGETEXT_BUF_SIZE]; }; void stagetext_free(void); diff --git a/src/util/crap.h b/src/util/crap.h index 7ecda6aa..ca72ad26 100644 --- a/src/util/crap.h +++ b/src/util/crap.h @@ -17,6 +17,18 @@ void* memdup(const void *src, size_t size); void inherit_missing_pointers(uint num, void *dest[num], void *const base[num]); bool is_main_thread(void); +static inline attr_must_inline uint32_t float_to_bits(float f) { + union { uint32_t i; float f; } u; + u.f = f; + return u.i; +} + +static inline attr_must_inline float bits_to_float(uint32_t i) { + union { uint32_t i; float f; } u; + u.i = i; + return u.f; +} + extern SDL_threadID main_thread_id; #define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(*(arr)))