render: extract color palette; convert floating labels to enum
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I29c5feb6099fe321e227e0313282f1546a6a6964
This commit is contained in:
parent
3998fb5259
commit
eed5c3aff3
4 changed files with 151 additions and 53 deletions
|
|
@ -104,12 +104,14 @@ 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;
|
||||||
char label[8]; // non-empty -> show label instead of numeric value
|
FloatingLabel label; // label type instead of string
|
||||||
StatusEffectType effect_type; // used to pick color for proc labels
|
StatusEffectType effect_type; // used to pick color for proc labels
|
||||||
} FloatingText;
|
} FloatingText;
|
||||||
|
|
||||||
|
|
|
||||||
31
src/main.c
31
src/main.c
|
|
@ -44,12 +44,12 @@ static void spawn_floating_text(GameState *gs, int x, int y, int value, int is_c
|
||||||
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 = FLOATING_TEXT_LIFETIME;
|
||||||
gs->floating_texts[slot].is_critical = is_critical;
|
gs->floating_texts[slot].is_critical = is_critical;
|
||||||
gs->floating_texts[slot].label[0] = '\0'; // numeric, no label
|
gs->floating_texts[slot].label = LABEL_NONE;
|
||||||
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, const char *label, StatusEffectType effect_type) {
|
static void spawn_floating_label(GameState *gs, int x, int y, FloatingLabel label, StatusEffectType effect_type) {
|
||||||
int slot = float_slot(gs);
|
int slot = float_slot(gs);
|
||||||
if (slot < 0)
|
if (slot < 0)
|
||||||
return;
|
return;
|
||||||
|
|
@ -58,25 +58,24 @@ static void spawn_floating_label(GameState *gs, int x, int y, const char *label,
|
||||||
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 const char *proc_label_for(StatusEffectType effect) {
|
static FloatingLabel proc_label_for(StatusEffectType effect) {
|
||||||
switch (effect) {
|
switch (effect) {
|
||||||
case EFFECT_POISON:
|
case EFFECT_POISON:
|
||||||
return "POISON!";
|
return LABEL_PROC;
|
||||||
case EFFECT_BLEED:
|
case EFFECT_BLEED:
|
||||||
return "BLEED!";
|
return LABEL_PROC;
|
||||||
case EFFECT_BURN:
|
case EFFECT_BURN:
|
||||||
return "BURN!";
|
return LABEL_PROC;
|
||||||
case EFFECT_STUN:
|
case EFFECT_STUN:
|
||||||
return "STUN!";
|
return LABEL_PROC;
|
||||||
case EFFECT_WEAKEN:
|
case EFFECT_WEAKEN:
|
||||||
return "WEAKEN!";
|
return LABEL_PROC;
|
||||||
default:
|
default:
|
||||||
return "";
|
return LABEL_NONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -187,7 +186,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, "DODGE", EFFECT_NONE);
|
spawn_floating_label(gs, ex, ey, LABEL_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)
|
||||||
|
|
@ -195,21 +194,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, "BLOCK", EFFECT_NONE);
|
spawn_floating_label(gs, ex, ey - 10, LABEL_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, "CRIT!", EFFECT_NONE);
|
spawn_floating_label(gs, ex + 8, ey - 10, LABEL_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, proc_label_for(applied), applied);
|
spawn_floating_label(gs, ex, ey - 20, LABEL_PROC, applied);
|
||||||
audio_play_proc();
|
audio_play_proc();
|
||||||
}
|
}
|
||||||
if (!attacked_enemy->alive) {
|
if (!attacked_enemy->alive) {
|
||||||
spawn_floating_label(gs, ex, ey - 20, "SLAIN", EFFECT_NONE);
|
spawn_floating_label(gs, ex, ey - 20, LABEL_SLAIN, EFFECT_NONE);
|
||||||
audio_play_enemy_death(gs);
|
audio_play_enemy_death(gs);
|
||||||
gs->total_kills++;
|
gs->total_kills++;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
73
src/render.c
73
src/render.c
|
|
@ -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){150, 50, 50, 255}; // dark red
|
enemy_color = COLOR_ENEMY_GOBLIN; // dark red
|
||||||
break;
|
break;
|
||||||
case ENEMY_SKELETON:
|
case ENEMY_SKELETON:
|
||||||
enemy_color = (Color){200, 200, 200, 255}; // light gray
|
enemy_color = COLOR_ENEMY_SKELETON; // light gray
|
||||||
break;
|
break;
|
||||||
case ENEMY_ORC:
|
case ENEMY_ORC:
|
||||||
enemy_color = (Color){50, 150, 50, 255}; // dark green
|
enemy_color = COLOR_ENEMY_ORC; // 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){255, 100, 100, 255}; // red/pink
|
item_color = COLOR_ITEM_POTION; // red/pink
|
||||||
break;
|
break;
|
||||||
case ITEM_WEAPON:
|
case ITEM_WEAPON:
|
||||||
item_color = (Color){255, 255, 100, 255}; // yellow
|
item_color = COLOR_ITEM_WEAPON; // yellow
|
||||||
break;
|
break;
|
||||||
case ITEM_ARMOR:
|
case ITEM_ARMOR:
|
||||||
item_color = (Color){100, 100, 255, 255}; // blue
|
item_color = COLOR_ITEM_ARMOR; // blue
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
item_color = GREEN;
|
item_color = GREEN;
|
||||||
|
|
@ -405,17 +405,19 @@ 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[0] == '\0')
|
if (ft->label == LABEL_NONE)
|
||||||
return (Color){255, 100, 100, alpha}; // numeric damage default
|
return FLOAT_DAMAGE; // 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:
|
||||||
|
|
@ -429,8 +431,32 @@ 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 (Color){200, 200, 200, alpha};
|
return FLOAT_DEFAULT;
|
||||||
}
|
}
|
||||||
|
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) {
|
||||||
|
|
@ -443,13 +469,14 @@ 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[0] != '\0') {
|
if (texts[i].label != LABEL_NONE) {
|
||||||
// Label text (DODGE, BLOCK, CRIT!, proc name, SLAIN)
|
// Label text (DODGE, BLOCK, CRIT!, SLAIN, or PROC)
|
||||||
// Check for "CRIT!" specifically rather than just 'C' prefix
|
// CRIT! gets larger font size
|
||||||
int font_size = (strcmp(texts[i].label, "CRIT!") == 0) ? 16 : 14;
|
int font_size = label_font_size(texts[i].label);
|
||||||
Color color = label_color(&texts[i], a);
|
Color color = label_color(&texts[i], a);
|
||||||
int text_w = MeasureText(texts[i].label, font_size);
|
const char *text = label_text(texts[i].label);
|
||||||
DrawText(texts[i].label, x - text_w / 2, y, font_size, color);
|
int text_w = MeasureText(text, font_size);
|
||||||
|
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};
|
||||||
|
|
|
||||||
70
src/render.h
70
src/render.h
|
|
@ -3,6 +3,76 @@
|
||||||
|
|
||||||
#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);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue