Compare commits

..

No commits in common. "eed5c3aff30232c786a5cff24fe81c59a4e7ae53" and "6281c756a84c8f2535333fbdf54dad9e9273470a" have entirely different histories.

5 changed files with 62 additions and 173 deletions

View file

@ -104,14 +104,12 @@ typedef struct {
} Enemy; } Enemy;
// Floating damage text // Floating damage text
typedef enum { LABEL_NONE = 0, LABEL_DODGE, LABEL_BLOCK, LABEL_CRIT, LABEL_SLAIN, LABEL_PROC } FloatingLabel;
typedef struct { typedef struct {
int x, y; int x, y;
int value; int value;
int lifetime; // frames remaining int lifetime; // frames remaining
int is_critical; 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 StatusEffectType effect_type; // used to pick color for proc labels
} FloatingText; } FloatingText;

View file

@ -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].x = x;
gs->floating_texts[slot].y = y; gs->floating_texts[slot].y = y;
gs->floating_texts[slot].value = value; 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].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; gs->floating_texts[slot].effect_type = EFFECT_NONE;
} }
// spawn floating label text (DODGE, BLOCK, CRIT!, proc names, SLAIN) // 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); int slot = float_slot(gs);
if (slot < 0) if (slot < 0)
return; 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].value = 0;
gs->floating_texts[slot].lifetime = 60; gs->floating_texts[slot].lifetime = 60;
gs->floating_texts[slot].is_critical = 0; gs->floating_texts[slot].is_critical = 0;
gs->floating_texts[slot].label = label;
gs->floating_texts[slot].effect_type = effect_type; 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) { switch (effect) {
case EFFECT_POISON: case EFFECT_POISON:
return LABEL_PROC; return "POISON!";
case EFFECT_BLEED: case EFFECT_BLEED:
return LABEL_PROC; return "BLEED!";
case EFFECT_BURN: case EFFECT_BURN:
return LABEL_PROC; return "BURN!";
case EFFECT_STUN: case EFFECT_STUN:
return LABEL_PROC; return "STUN!";
case EFFECT_WEAKEN: case EFFECT_WEAKEN:
return LABEL_PROC; return "WEAKEN!";
default: default:
return LABEL_NONE; return "";
} }
} }
@ -91,8 +92,8 @@ static void update_effects(GameState *gs) {
// update screen shake // update screen shake
if (gs->screen_shake > 0) { if (gs->screen_shake > 0) {
gs->screen_shake--; gs->screen_shake--;
gs->shake_x = rng_int(-SHAKE_MAX_OFFSET, SHAKE_MAX_OFFSET); gs->shake_x = rng_int(-4, 4);
gs->shake_y = rng_int(-SHAKE_MAX_OFFSET, SHAKE_MAX_OFFSET); gs->shake_y = rng_int(-4, 4);
} else { } else {
gs->shake_x = 0; gs->shake_x = 0;
gs->shake_y = 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); int player_effect_dmg = combat_tick_effects(&gs->player);
if (player_effect_dmg > 0) { if (player_effect_dmg > 0) {
spawn_floating_text(gs, gs->player.x * TILE_SIZE + 8, gs->player.y * TILE_SIZE, 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 // 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; int ey = attacked_enemy->y * TILE_SIZE;
if (combat_was_dodged()) { 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); audio_play_dodge(gs);
} else { } else {
if (combat_get_last_damage() > 0) 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()); spawn_floating_text(gs, ex, ey, combat_get_last_damage(), combat_was_critical());
audio_play_attack(gs); audio_play_attack(gs);
if (combat_was_blocked()) { 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); audio_play_block(gs);
} }
if (combat_was_critical()) { 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); audio_play_crit(gs);
gs->crits_landed++; gs->crits_landed++;
} }
StatusEffectType applied = combat_get_applied_effect(); StatusEffectType applied = combat_get_applied_effect();
if (applied != EFFECT_NONE) { 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(); audio_play_proc();
} }
if (!attacked_enemy->alive) { 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); audio_play_enemy_death(gs);
gs->total_kills++; gs->total_kills++;
} }
@ -221,7 +222,7 @@ static void post_action(GameState *gs, Enemy *attacked_enemy) {
// Check if player took damage // Check if player took damage
if (combat_was_player_damage() && combat_get_last_damage() > 0) { if (combat_was_player_damage() && combat_get_last_damage() > 0) {
audio_play_player_damage(gs); audio_play_player_damage(gs);
gs->screen_shake = SHAKE_PLAYER_DAMAGE_DURATION; gs->screen_shake = 8;
gs->damage_taken += combat_get_last_damage(); gs->damage_taken += combat_get_last_damage();
gs->times_hit++; gs->times_hit++;
spawn_floating_text(gs, gs->player.x * TILE_SIZE + 8, gs->player.y * TILE_SIZE, combat_get_last_damage(), spawn_floating_text(gs, gs->player.x * TILE_SIZE + 8, gs->player.y * TILE_SIZE, combat_get_last_damage(),

View file

@ -46,13 +46,13 @@ void render_enemies(const Enemy *enemies, int count) {
Color enemy_color; Color enemy_color;
switch (enemies[i].type) { switch (enemies[i].type) {
case ENEMY_GOBLIN: case ENEMY_GOBLIN:
enemy_color = COLOR_ENEMY_GOBLIN; // dark red enemy_color = (Color){150, 50, 50, 255}; // dark red
break; break;
case ENEMY_SKELETON: case ENEMY_SKELETON:
enemy_color = COLOR_ENEMY_SKELETON; // light gray enemy_color = (Color){200, 200, 200, 255}; // light gray
break; break;
case ENEMY_ORC: case ENEMY_ORC:
enemy_color = COLOR_ENEMY_ORC; // dark green enemy_color = (Color){50, 150, 50, 255}; // dark green
break; break;
default: default:
enemy_color = RED; enemy_color = RED;
@ -91,13 +91,13 @@ void render_items(const Item *items, int count) {
Color item_color; Color item_color;
switch (items[i].type) { switch (items[i].type) {
case ITEM_POTION: case ITEM_POTION:
item_color = COLOR_ITEM_POTION; // red/pink item_color = (Color){255, 100, 100, 255}; // red/pink
break; break;
case ITEM_WEAPON: case ITEM_WEAPON:
item_color = COLOR_ITEM_WEAPON; // yellow item_color = (Color){255, 255, 100, 255}; // yellow
break; break;
case ITEM_ARMOR: case ITEM_ARMOR:
item_color = COLOR_ITEM_ARMOR; // blue item_color = (Color){100, 100, 255, 255}; // blue
break; break;
default: default:
item_color = GREEN; item_color = GREEN;
@ -373,12 +373,10 @@ void render_inventory_overlay(const Player *p, int selected) {
// Item name // Item name
const char *name = item_get_name(item); const char *name = item_get_name(item);
if (name) {
Color name_color = (item->type == ITEM_POTION) ? (Color){255, 140, 140, 255} Color name_color = (item->type == ITEM_POTION) ? (Color){255, 140, 140, 255}
: (item->type == ITEM_WEAPON) ? (Color){255, 255, 140, 255} : (item->type == ITEM_WEAPON) ? (Color){255, 255, 140, 255}
: (Color){140, 140, 255, 255}; : (Color){140, 140, 255, 255};
DrawText(name, overlay.x + 45, y_pos + 4, 14, name_color); DrawText(name, overlay.x + 45, y_pos + 4, 14, name_color);
}
// Power // Power
snprintf(slot_text, sizeof(slot_text), "+%d", item->power); snprintf(slot_text, sizeof(slot_text), "+%d", item->power);
@ -405,19 +403,17 @@ void render_inventory_overlay(const Player *p, int selected) {
} }
static Color label_color(FloatingText *ft, int alpha) { static Color label_color(FloatingText *ft, int alpha) {
if (ft->label == LABEL_NONE) if (ft->label[0] == '\0')
return FLOAT_DAMAGE; // numeric damage default 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 // Proc label, color driven by effect_type stored in the struct
switch (ft->effect_type) { switch (ft->effect_type) {
case EFFECT_POISON: case EFFECT_POISON:
@ -431,32 +427,8 @@ static Color label_color(FloatingText *ft, int alpha) {
case EFFECT_WEAKEN: case EFFECT_WEAKEN:
return (Color){120, 120, 120, alpha}; return (Color){120, 120, 120, alpha};
default: default:
return FLOAT_DEFAULT; return (Color){200, 200, 200, alpha};
} }
default:
return FLOAT_DAMAGE;
}
}
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) { void render_floating_texts(FloatingText *texts, int count, int shake_x, int shake_y) {
@ -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; float alpha = (float)texts[i].lifetime / 60.0f;
int a = (int)(255 * alpha); int a = (int)(255 * alpha);
if (texts[i].label != LABEL_NONE) { if (texts[i].label[0] != '\0') {
// Label text (DODGE, BLOCK, CRIT!, SLAIN, or PROC) // Label text (DODGE, BLOCK, CRIT!, proc name, SLAIN)
// CRIT! gets larger font size // Check for "CRIT!" specifically rather than just 'C' prefix
int font_size = label_font_size(texts[i].label); int font_size = (strcmp(texts[i].label, "CRIT!") == 0) ? 16 : 14;
Color color = label_color(&texts[i], a); Color color = label_color(&texts[i], a);
const char *text = label_text(texts[i].label); int text_w = MeasureText(texts[i].label, font_size);
int text_w = MeasureText(text, font_size); DrawText(texts[i].label, x - text_w / 2, y, font_size, color);
DrawText(text, x - text_w / 2, y, font_size, color);
} else { } else {
// Numeric damage // Numeric damage
Color color = texts[i].is_critical ? (Color){255, 200, 50, a} : (Color){255, 100, 100, a}; Color color = texts[i].is_critical ? (Color){255, 200, 50, a} : (Color){255, 100, 100, a};

View file

@ -3,76 +3,6 @@
#include "common.h" #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 // Render the map tiles
void render_map(const Map *map); void render_map(const Map *map);

View file

@ -52,15 +52,4 @@
#define PLAYER_BASE_DODGE 5 #define PLAYER_BASE_DODGE 5
#define PLAYER_BASE_BLOCK 0 #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 #endif // SETTINGS_H