diff --git a/src/common.h b/src/common.h index 61ab044..2b499a8 100644 --- a/src/common.h +++ b/src/common.h @@ -104,14 +104,12 @@ typedef struct { } Enemy; // Floating damage text -typedef enum { LABEL_NONE = 0, LABEL_DODGE, LABEL_BLOCK, LABEL_CRIT, LABEL_SLAIN, LABEL_PROC } FloatingLabel; - typedef struct { int x, y; int value; int lifetime; // frames remaining int is_critical; - FloatingLabel label; // label type instead of string + char label[8]; // non-empty -> show label instead of numeric value StatusEffectType effect_type; // used to pick color for proc labels } FloatingText; diff --git a/src/main.c b/src/main.c index d450d75..5b4f122 100644 --- a/src/main.c +++ b/src/main.c @@ -42,14 +42,14 @@ static void spawn_floating_text(GameState *gs, int x, int y, int value, int is_c gs->floating_texts[slot].x = x; gs->floating_texts[slot].y = y; gs->floating_texts[slot].value = value; - gs->floating_texts[slot].lifetime = FLOATING_TEXT_LIFETIME; + gs->floating_texts[slot].lifetime = 60; gs->floating_texts[slot].is_critical = is_critical; - gs->floating_texts[slot].label = LABEL_NONE; + gs->floating_texts[slot].label[0] = '\0'; // numeric, no label gs->floating_texts[slot].effect_type = EFFECT_NONE; } // spawn floating label text (DODGE, BLOCK, CRIT!, proc names, SLAIN) -static void spawn_floating_label(GameState *gs, int x, int y, FloatingLabel label, StatusEffectType effect_type) { +static void spawn_floating_label(GameState *gs, int x, int y, const char *label, StatusEffectType effect_type) { int slot = float_slot(gs); if (slot < 0) return; @@ -58,24 +58,25 @@ static void spawn_floating_label(GameState *gs, int x, int y, FloatingLabel labe gs->floating_texts[slot].value = 0; gs->floating_texts[slot].lifetime = 60; gs->floating_texts[slot].is_critical = 0; - gs->floating_texts[slot].label = label; gs->floating_texts[slot].effect_type = effect_type; + strncpy(gs->floating_texts[slot].label, label, 7); + gs->floating_texts[slot].label[7] = '\0'; } -static FloatingLabel proc_label_for(StatusEffectType effect) { +static const char *proc_label_for(StatusEffectType effect) { switch (effect) { case EFFECT_POISON: - return LABEL_PROC; + return "POISON!"; case EFFECT_BLEED: - return LABEL_PROC; + return "BLEED!"; case EFFECT_BURN: - return LABEL_PROC; + return "BURN!"; case EFFECT_STUN: - return LABEL_PROC; + return "STUN!"; case EFFECT_WEAKEN: - return LABEL_PROC; + return "WEAKEN!"; default: - return LABEL_NONE; + return ""; } } @@ -91,8 +92,8 @@ static void update_effects(GameState *gs) { // update screen shake if (gs->screen_shake > 0) { gs->screen_shake--; - gs->shake_x = rng_int(-SHAKE_MAX_OFFSET, SHAKE_MAX_OFFSET); - gs->shake_y = rng_int(-SHAKE_MAX_OFFSET, SHAKE_MAX_OFFSET); + gs->shake_x = rng_int(-4, 4); + gs->shake_y = rng_int(-4, 4); } else { gs->shake_x = 0; gs->shake_y = 0; @@ -138,7 +139,7 @@ static void tick_all_effects(GameState *gs) { int player_effect_dmg = combat_tick_effects(&gs->player); if (player_effect_dmg > 0) { spawn_floating_text(gs, gs->player.x * TILE_SIZE + 8, gs->player.y * TILE_SIZE, player_effect_dmg, 0); - gs->screen_shake = SHAKE_EFFECT_DURATION; + gs->screen_shake = 4; } // Check if player died from effects @@ -186,7 +187,7 @@ static void post_action(GameState *gs, Enemy *attacked_enemy) { int ey = attacked_enemy->y * TILE_SIZE; if (combat_was_dodged()) { - spawn_floating_label(gs, ex, ey, LABEL_DODGE, EFFECT_NONE); + spawn_floating_label(gs, ex, ey, "DODGE", EFFECT_NONE); audio_play_dodge(gs); } else { if (combat_get_last_damage() > 0) @@ -194,21 +195,21 @@ static void post_action(GameState *gs, Enemy *attacked_enemy) { spawn_floating_text(gs, ex, ey, combat_get_last_damage(), combat_was_critical()); audio_play_attack(gs); if (combat_was_blocked()) { - spawn_floating_label(gs, ex, ey - 10, LABEL_BLOCK, EFFECT_NONE); + spawn_floating_label(gs, ex, ey - 10, "BLOCK", EFFECT_NONE); audio_play_block(gs); } if (combat_was_critical()) { - spawn_floating_label(gs, ex + 8, ey - 10, LABEL_CRIT, EFFECT_NONE); + spawn_floating_label(gs, ex + 8, ey - 10, "CRIT!", EFFECT_NONE); audio_play_crit(gs); gs->crits_landed++; } StatusEffectType applied = combat_get_applied_effect(); if (applied != EFFECT_NONE) { - spawn_floating_label(gs, ex, ey - 20, LABEL_PROC, applied); + spawn_floating_label(gs, ex, ey - 20, proc_label_for(applied), applied); audio_play_proc(); } if (!attacked_enemy->alive) { - spawn_floating_label(gs, ex, ey - 20, LABEL_SLAIN, EFFECT_NONE); + spawn_floating_label(gs, ex, ey - 20, "SLAIN", EFFECT_NONE); audio_play_enemy_death(gs); gs->total_kills++; } @@ -221,7 +222,7 @@ static void post_action(GameState *gs, Enemy *attacked_enemy) { // Check if player took damage if (combat_was_player_damage() && combat_get_last_damage() > 0) { audio_play_player_damage(gs); - gs->screen_shake = SHAKE_PLAYER_DAMAGE_DURATION; + gs->screen_shake = 8; gs->damage_taken += combat_get_last_damage(); gs->times_hit++; spawn_floating_text(gs, gs->player.x * TILE_SIZE + 8, gs->player.y * TILE_SIZE, combat_get_last_damage(), diff --git a/src/render.c b/src/render.c index 3c24e15..7a1f274 100644 --- a/src/render.c +++ b/src/render.c @@ -46,13 +46,13 @@ void render_enemies(const Enemy *enemies, int count) { Color enemy_color; switch (enemies[i].type) { case ENEMY_GOBLIN: - enemy_color = COLOR_ENEMY_GOBLIN; // dark red + enemy_color = (Color){150, 50, 50, 255}; // dark red break; case ENEMY_SKELETON: - enemy_color = COLOR_ENEMY_SKELETON; // light gray + enemy_color = (Color){200, 200, 200, 255}; // light gray break; case ENEMY_ORC: - enemy_color = COLOR_ENEMY_ORC; // dark green + enemy_color = (Color){50, 150, 50, 255}; // dark green break; default: enemy_color = RED; @@ -91,13 +91,13 @@ void render_items(const Item *items, int count) { Color item_color; switch (items[i].type) { case ITEM_POTION: - item_color = COLOR_ITEM_POTION; // red/pink + item_color = (Color){255, 100, 100, 255}; // red/pink break; case ITEM_WEAPON: - item_color = COLOR_ITEM_WEAPON; // yellow + item_color = (Color){255, 255, 100, 255}; // yellow break; case ITEM_ARMOR: - item_color = COLOR_ITEM_ARMOR; // blue + item_color = (Color){100, 100, 255, 255}; // blue break; default: item_color = GREEN; @@ -373,12 +373,10 @@ void render_inventory_overlay(const Player *p, int selected) { // Item name const char *name = item_get_name(item); - if (name) { - Color name_color = (item->type == ITEM_POTION) ? (Color){255, 140, 140, 255} - : (item->type == ITEM_WEAPON) ? (Color){255, 255, 140, 255} - : (Color){140, 140, 255, 255}; - DrawText(name, overlay.x + 45, y_pos + 4, 14, name_color); - } + Color name_color = (item->type == ITEM_POTION) ? (Color){255, 140, 140, 255} + : (item->type == ITEM_WEAPON) ? (Color){255, 255, 140, 255} + : (Color){140, 140, 255, 255}; + DrawText(name, overlay.x + 45, y_pos + 4, 14, name_color); // Power snprintf(slot_text, sizeof(slot_text), "+%d", item->power); @@ -405,60 +403,34 @@ void render_inventory_overlay(const Player *p, int selected) { } static Color label_color(FloatingText *ft, int alpha) { - if (ft->label == LABEL_NONE) - return FLOAT_DAMAGE; // numeric damage default + if (ft->label[0] == '\0') + return (Color){255, 100, 100, alpha}; // numeric damage default + if (strcmp(ft->label, "DODGE") == 0) + return (Color){160, 160, 160, alpha}; + if (strcmp(ft->label, "BLOCK") == 0) + return (Color){80, 130, 220, alpha}; + if (strcmp(ft->label, "CRIT!") == 0) + return (Color){255, 200, 50, alpha}; + if (strcmp(ft->label, "SLAIN") == 0) + return (Color){220, 50, 50, alpha}; - switch (ft->label) { - case LABEL_DODGE: - return FLOAT_DODGE; - case LABEL_BLOCK: - return FLOAT_BLOCK; - case LABEL_CRIT: - return FLOAT_CRIT; - case LABEL_SLAIN: - return FLOAT_SLAIN; - case LABEL_PROC: - // Proc label, color driven by effect_type stored in the struct - switch (ft->effect_type) { - case EFFECT_POISON: - return (Color){50, 200, 50, alpha}; - case EFFECT_BLEED: - return (Color){200, 50, 50, alpha}; - case EFFECT_BURN: - return (Color){230, 130, 30, alpha}; - case EFFECT_STUN: - return (Color){200, 200, 50, alpha}; - case EFFECT_WEAKEN: - return (Color){120, 120, 120, alpha}; - default: - return FLOAT_DEFAULT; - } + // Proc label, color driven by effect_type stored in the struct + switch (ft->effect_type) { + case EFFECT_POISON: + return (Color){50, 200, 50, alpha}; + case EFFECT_BLEED: + return (Color){200, 50, 50, alpha}; + case EFFECT_BURN: + return (Color){230, 130, 30, alpha}; + case EFFECT_STUN: + return (Color){200, 200, 50, alpha}; + case EFFECT_WEAKEN: + return (Color){120, 120, 120, alpha}; default: - return FLOAT_DAMAGE; + return (Color){200, 200, 200, alpha}; } } -static const char *label_text(FloatingLabel label) { - switch (label) { - case LABEL_DODGE: - return "DODGE"; - case LABEL_BLOCK: - return "BLOCK"; - case LABEL_CRIT: - return "CRIT!"; - case LABEL_SLAIN: - return "SLAIN"; - case LABEL_PROC: - return "PROC"; - default: - return ""; - } -} - -static int label_font_size(FloatingLabel label) { - return (label == LABEL_CRIT) ? FONT_SIZE_FLOAT_CRIT : FONT_SIZE_FLOAT_LABEL; -} - void render_floating_texts(FloatingText *texts, int count, int shake_x, int shake_y) { for (int i = 0; i < count; i++) { if (texts[i].lifetime <= 0) @@ -469,14 +441,13 @@ void render_floating_texts(FloatingText *texts, int count, int shake_x, int shak float alpha = (float)texts[i].lifetime / 60.0f; int a = (int)(255 * alpha); - if (texts[i].label != LABEL_NONE) { - // Label text (DODGE, BLOCK, CRIT!, SLAIN, or PROC) - // CRIT! gets larger font size - int font_size = label_font_size(texts[i].label); + if (texts[i].label[0] != '\0') { + // Label text (DODGE, BLOCK, CRIT!, proc name, SLAIN) + // Check for "CRIT!" specifically rather than just 'C' prefix + int font_size = (strcmp(texts[i].label, "CRIT!") == 0) ? 16 : 14; Color color = label_color(&texts[i], a); - const char *text = label_text(texts[i].label); - int text_w = MeasureText(text, font_size); - DrawText(text, x - text_w / 2, y, font_size, color); + int text_w = MeasureText(texts[i].label, font_size); + DrawText(texts[i].label, x - text_w / 2, y, font_size, color); } else { // Numeric damage Color color = texts[i].is_critical ? (Color){255, 200, 50, a} : (Color){255, 100, 100, a}; diff --git a/src/render.h b/src/render.h index 016291f..966793b 100644 --- a/src/render.h +++ b/src/render.h @@ -3,76 +3,6 @@ #include "common.h" -// HUD colors -#define HUD_BG (Color){25, 20, 15, 255} -#define HUD_BORDER (Color){139, 119, 89, 255} -#define HUD_LIGHT_LINE (Color){60, 55, 50, 255} -#define HUD_DARK_LINE (Color){15, 12, 10, 255} -#define TEXT_DIM (Color){160, 150, 140, 255} -#define TEXT_BRIGHT (Color){240, 230, 220, 255} - -// HP bar colors -#define HP_HIGH (Color){60, 180, 60, 255} -#define HP_MED (Color){200, 180, 40, 255} -#define HP_LOW (Color){200, 60, 60, 255} -#define HP_BAR_BG (Color){20, 15, 15, 255} -#define HP_BAR_BORDER (Color){80, 70, 60, 255} - -// Enemy type colors -#define COLOR_ENEMY_GOBLIN (Color){150, 50, 50, 255} -#define COLOR_ENEMY_SKELETON (Color){200, 200, 200, 255} -#define COLOR_ENEMY_ORC (Color){50, 150, 50, 255} - -// Item type colors -#define COLOR_ITEM_POTION (Color){255, 100, 100, 255} -#define COLOR_ITEM_WEAPON (Color){255, 255, 100, 255} -#define COLOR_ITEM_ARMOR (Color){100, 100, 255, 255} - -// Action log colors -#define LOG_BG (Color){15, 12, 10, 230} -#define LOG_BORDER (Color){100, 85, 65, 255} -#define LOG_DARK (Color){60, 50, 40, 255} -#define LOG_TITLE_BG (Color){30, 25, 20, 255} -#define LOG_TEXT (Color){180, 160, 130, 255} -#define LOG_NEWEST (Color){220, 210, 200, 255} -#define LOG_RECENT (Color){180, 170, 160, 255} -#define LOG_OLDER (Color){150, 140, 130, 230} -#define LOG_OLDEST (Color){120, 110, 100, 200} - -// Inventory overlay colors -#define INV_OVERLAY_BG (Color){12, 12, 12, 252} -#define INV_BORDER (Color){70, 70, 70, 255} -#define INV_SLOT_BG (Color){45, 45, 45, 255} -#define INV_SELECTED (Color){180, 160, 80, 255} -#define INV_EMPTY (Color){40, 40, 40, 255} -#define INV_HINT (Color){65, 65, 65, 255} - -// Floating text colors -#define FLOAT_DAMAGE (Color){255, 100, 100, 255} -#define FLOAT_CRIT (Color){255, 200, 50, 255} -#define FLOAT_DODGE (Color){160, 160, 160, 255} -#define FLOAT_BLOCK (Color){80, 130, 220, 255} -#define FLOAT_SLAIN (Color){220, 50, 50, 255} -#define FLOAT_DEFAULT (Color){200, 200, 200, 255} - -// Floating label font sizes -#define FONT_SIZE_FLOAT_LABEL 14 -#define FONT_SIZE_FLOAT_CRIT 16 -#define FONT_SIZE_FLOAT_DMG 18 - -// Message box colors -#define MSG_BG (Color){45, 45, 45, 235} -#define MSG_BORDER (Color){180, 180, 180, 255} - -// End screen colors -#define END_OVERLAY (Color){0, 0, 0, 210} -#define END_BOX_BG (Color){20, 20, 20, 240} -#define END_BOX_BORDER (Color){100, 100, 100, 255} - -// Portrait placeholder -// FIXME: remove when player sprites are available -#define PORTRAIT_BG (Color){30, 30, 45, 255} - // Render the map tiles void render_map(const Map *map); diff --git a/src/settings.h b/src/settings.h index 8b8ac54..e87c082 100644 --- a/src/settings.h +++ b/src/settings.h @@ -52,15 +52,4 @@ #define PLAYER_BASE_DODGE 5 #define PLAYER_BASE_BLOCK 0 -// Screen shake -#define SHAKE_EFFECT_DURATION 4 -#define SHAKE_PLAYER_DAMAGE_DURATION 8 -#define SHAKE_MAX_OFFSET 4 - -// Floating text -#define FLOATING_TEXT_LIFETIME 60 - -// Message timer -#define MESSAGE_TIMER_DURATION 60 - #endif // SETTINGS_H