Compare commits
No commits in common. "main" and "main" have entirely different histories.
22 changed files with 183 additions and 357 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
77
build.zig
77
build.zig
|
|
@ -4,55 +4,8 @@ pub fn build(b: *std.Build) void {
|
|||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const c_flags = [_][]const u8{
|
||||
"-std=c99",
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-O2",
|
||||
};
|
||||
|
||||
// RNG library
|
||||
const rng_lib = b.addLibrary(.{
|
||||
.name = "rng",
|
||||
.root_module = b.createModule(.{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.link_libc = true,
|
||||
}),
|
||||
});
|
||||
rng_lib.addCSourceFiles(.{
|
||||
.files = &[_][]const u8{"libs/rng/rng.c"},
|
||||
.flags = &c_flags,
|
||||
});
|
||||
rng_lib.addIncludePath(b.path("libs/rng"));
|
||||
|
||||
// Map library
|
||||
const map_lib = b.addLibrary(.{
|
||||
.name = "map",
|
||||
.root_module = b.createModule(.{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.link_libc = true,
|
||||
}),
|
||||
});
|
||||
map_lib.addCSourceFiles(.{
|
||||
.files = &[_][]const u8{
|
||||
"libs/map/map.c",
|
||||
"libs/map/utils.c",
|
||||
},
|
||||
.flags = &c_flags,
|
||||
});
|
||||
// map.h includes common.h and settings.h which live in src/
|
||||
map_lib.addIncludePath(b.path("src"));
|
||||
// map.c includes rng/rng.h via libs/ root
|
||||
map_lib.addIncludePath(b.path("libs"));
|
||||
// utils.h is co-located with map.c
|
||||
map_lib.addIncludePath(b.path("libs/map"));
|
||||
|
||||
// Zig combat library. This must be compiled as an object and linked
|
||||
// directly to bypassing the archive step, or it yields a corrupt
|
||||
// archive that forces the user to clear the cache each time.
|
||||
const combat_obj = b.addObject(.{
|
||||
// Zig combat library
|
||||
const combat_lib = b.addLibrary(.{
|
||||
.name = "combat",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("libs/combat/combat.zig"),
|
||||
|
|
@ -61,20 +14,29 @@ pub fn build(b: *std.Build) void {
|
|||
.link_libc = true,
|
||||
}),
|
||||
});
|
||||
// common.h and settings.h live in src/; rng.h exposed bare from libs/rng
|
||||
combat_obj.addIncludePath(b.path("src"));
|
||||
combat_obj.addIncludePath(b.path("libs/rng"));
|
||||
combat_lib.addIncludePath(b.path("src"));
|
||||
combat_lib.linkSystemLibrary("raylib");
|
||||
|
||||
// C sources remaining in src/
|
||||
// C sources (everything except combat, which is now Zig)
|
||||
const c_sources = [_][]const u8{
|
||||
"src/audio.c",
|
||||
"src/enemy.c",
|
||||
"src/items.c",
|
||||
"src/main.c",
|
||||
"src/movement.c",
|
||||
"src/map.c",
|
||||
"src/player.c",
|
||||
"src/movement.c",
|
||||
"src/render.c",
|
||||
"src/rng.c",
|
||||
"src/settings.c",
|
||||
"src/utils.c",
|
||||
};
|
||||
|
||||
const c_flags = [_][]const u8{
|
||||
"-std=c99",
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-O2",
|
||||
};
|
||||
|
||||
// Main executable
|
||||
|
|
@ -92,13 +54,8 @@ pub fn build(b: *std.Build) void {
|
|||
.flags = &c_flags,
|
||||
});
|
||||
|
||||
// src/ for own headers; libs/ so "rng/rng.h" and "map/map.h" resolve
|
||||
exe.addIncludePath(b.path("src"));
|
||||
exe.addIncludePath(b.path("libs"));
|
||||
|
||||
exe.linkLibrary(rng_lib);
|
||||
exe.linkLibrary(map_lib);
|
||||
exe.addObject(combat_obj);
|
||||
exe.linkLibrary(combat_lib);
|
||||
exe.linkSystemLibrary("raylib");
|
||||
exe.linkSystemLibrary("m");
|
||||
exe.linkSystemLibrary("pthread");
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
#include "audio.h"
|
||||
#include "raylib.h"
|
||||
#include "common.h"
|
||||
#include <math.h>
|
||||
#include <stddef.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#ifndef AUDIO_H
|
||||
#define AUDIO_H
|
||||
#include "game_state.h"
|
||||
#include "common.h"
|
||||
|
||||
// Initialize audio system
|
||||
void audio_init(void);
|
||||
|
|
|
|||
63
src/common.h
63
src/common.h
|
|
@ -2,7 +2,7 @@
|
|||
#define COMMON_H
|
||||
|
||||
#include "settings.h"
|
||||
#include <stdbool.h>
|
||||
#include <raylib.h>
|
||||
|
||||
typedef struct {
|
||||
int x, y;
|
||||
|
|
@ -114,5 +114,66 @@ typedef struct {
|
|||
int effect_count;
|
||||
} 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
|
||||
StatusEffectType effect_type; // used to pick color for proc labels
|
||||
} FloatingText;
|
||||
|
||||
// AudioAssets
|
||||
typedef struct {
|
||||
Sound attack1, attack2, attack3;
|
||||
Sound pickup;
|
||||
Sound staircase;
|
||||
Sound dodge1, dodge2, dodge3;
|
||||
Sound crit;
|
||||
} AudioAssets;
|
||||
|
||||
// GameState - encapsulates all game state for testability and save/load
|
||||
typedef struct {
|
||||
Player player;
|
||||
Map map;
|
||||
Dungeon dungeon;
|
||||
Enemy enemies[MAX_ENEMIES];
|
||||
int enemy_count;
|
||||
Item items[MAX_ITEMS];
|
||||
int item_count;
|
||||
int game_over;
|
||||
int game_won;
|
||||
const char *last_message;
|
||||
int message_timer;
|
||||
int turn_count;
|
||||
int awaiting_descend; // 0 = normal, 1 = waiting for Y/N
|
||||
int show_inventory; // 0 = hidden, 1 = show overlay
|
||||
int inv_selected; // currently selected inventory index
|
||||
// action log
|
||||
char action_log[5][128];
|
||||
int log_count;
|
||||
int log_head;
|
||||
// visual effects
|
||||
FloatingText floating_texts[8];
|
||||
int floating_count;
|
||||
int screen_shake; // frames of screen shake remaining
|
||||
int shake_x;
|
||||
int shake_y;
|
||||
AudioAssets sounds;
|
||||
// Statistics
|
||||
int total_kills;
|
||||
int items_collected;
|
||||
int damage_dealt;
|
||||
int damage_taken;
|
||||
int crits_landed;
|
||||
int times_hit;
|
||||
int potions_used;
|
||||
int floors_reached;
|
||||
int final_score;
|
||||
} GameState;
|
||||
|
||||
|
||||
#endif // COMMON_H
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
#include "enemy.h"
|
||||
#include "combat.h"
|
||||
#include "common.h"
|
||||
#include "map/map.h"
|
||||
#include "map.h"
|
||||
#include "movement.h"
|
||||
#include "rng/rng.h"
|
||||
#include "rng.h"
|
||||
#include "settings.h"
|
||||
#include <string.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,70 +0,0 @@
|
|||
#ifndef GAME_STATE_H
|
||||
#define GAME_STATE_H
|
||||
|
||||
#include "common.h"
|
||||
#include <raylib.h>
|
||||
|
||||
// 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
|
||||
StatusEffectType effect_type; // used to pick color for proc labels
|
||||
} FloatingText;
|
||||
|
||||
// AudioAssets
|
||||
typedef struct {
|
||||
Sound attack1, attack2, attack3;
|
||||
Sound pickup;
|
||||
Sound staircase;
|
||||
Sound dodge1, dodge2, dodge3;
|
||||
Sound crit;
|
||||
} AudioAssets;
|
||||
|
||||
// GameState - encapsulates all game state for testability and save/load
|
||||
typedef struct {
|
||||
Player player;
|
||||
Map map;
|
||||
Dungeon dungeon;
|
||||
Enemy enemies[MAX_ENEMIES];
|
||||
int enemy_count;
|
||||
Item items[MAX_ITEMS];
|
||||
int item_count;
|
||||
int game_over;
|
||||
int game_won;
|
||||
const char *last_message;
|
||||
int message_timer;
|
||||
int turn_count;
|
||||
int awaiting_descend; // 0 = normal, 1 = waiting for Y/N
|
||||
int show_inventory; // 0 = hidden, 1 = show overlay
|
||||
int inv_selected; // currently selected inventory index
|
||||
// action log
|
||||
char action_log[5][128];
|
||||
int log_count;
|
||||
int log_head;
|
||||
// visual effects
|
||||
FloatingText floating_texts[8];
|
||||
int floating_count;
|
||||
int screen_shake; // frames of screen shake remaining
|
||||
int shake_x;
|
||||
int shake_y;
|
||||
AudioAssets sounds;
|
||||
// Statistics
|
||||
int total_kills;
|
||||
int items_collected;
|
||||
int damage_dealt;
|
||||
int damage_taken;
|
||||
int crits_landed;
|
||||
int times_hit;
|
||||
int potions_used;
|
||||
int floors_reached;
|
||||
int final_score;
|
||||
// Seed for this run
|
||||
unsigned int run_seed;
|
||||
} GameState;
|
||||
|
||||
#endif // GAME_STATE_H
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
#include "common.h"
|
||||
#include "map/map.h"
|
||||
#include "rng/rng.h"
|
||||
#include "map.h"
|
||||
#include "rng.h"
|
||||
#include "settings.h"
|
||||
#include <stddef.h>
|
||||
|
||||
|
|
|
|||
106
src/main.c
106
src/main.c
|
|
@ -1,20 +1,18 @@
|
|||
#include "audio.h"
|
||||
#include "combat.h"
|
||||
#include "game_state.h"
|
||||
#include "common.h"
|
||||
#include "enemy.h"
|
||||
#include "items.h"
|
||||
#include "map/map.h"
|
||||
#include "map.h"
|
||||
#include "movement.h"
|
||||
#include "player.h"
|
||||
#include "raylib.h"
|
||||
#include "render.h"
|
||||
#include "rng/rng.h"
|
||||
#include "rng.h"
|
||||
#include "settings.h"
|
||||
#include <ctype.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
// Add message to action log
|
||||
static void add_log(GameState *gs, const char *msg) {
|
||||
|
|
@ -104,14 +102,11 @@ static void update_effects(GameState *gs) {
|
|||
|
||||
// Initialize a new floor
|
||||
static void init_floor(GameState *gs, int floor_num) {
|
||||
// Seed RNG with run seed combined with floor number for deterministic generation
|
||||
rng_seed(gs->run_seed + floor_num * 54321);
|
||||
|
||||
// Generate dungeon
|
||||
dungeon_generate(&gs->dungeon, &gs->map, floor_num);
|
||||
|
||||
// Seed rng for this floor's content
|
||||
rng_seed(gs->run_seed + floor_num * 98765);
|
||||
rng_seed(floor_num * 54321);
|
||||
|
||||
// Find spawn position
|
||||
int start_x, start_y;
|
||||
|
|
@ -483,13 +478,7 @@ static int handle_input(GameState *gs) {
|
|||
// Check for restart (works during game over)
|
||||
if (IsKeyPressed(KEY_R) && gs->game_over) {
|
||||
memset(gs, 0, sizeof(GameState));
|
||||
// Generate a new random seed for the new run
|
||||
gs->run_seed = (unsigned int)time(NULL);
|
||||
init_floor(gs, 1);
|
||||
// Update window title with new seed
|
||||
char title[128];
|
||||
snprintf(title, sizeof(title), "Roguelike - Seed: %u", gs->run_seed);
|
||||
SetWindowTitle(title);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -519,16 +508,12 @@ void load_audio_assets(GameState *gs) {
|
|||
}
|
||||
|
||||
// Main game loop
|
||||
static void game_loop(unsigned int run_seed) {
|
||||
static void game_loop(void) {
|
||||
GameState gs;
|
||||
memset(&gs, 0, sizeof(GameState));
|
||||
gs.run_seed = run_seed;
|
||||
// load external assets
|
||||
// sound
|
||||
load_audio_assets(&gs);
|
||||
// font
|
||||
Font fontTTF = LoadFontEx("./assets/fonts/spartan_500.ttf", 36, NULL, 0);
|
||||
// Initialize first floor
|
||||
rng_seed(12345);
|
||||
init_floor(&gs, 1);
|
||||
|
||||
// Disable esc to exit
|
||||
|
|
@ -548,16 +533,10 @@ static void game_loop(unsigned int run_seed) {
|
|||
break;
|
||||
if (IsKeyPressed(KEY_R)) {
|
||||
memset(&gs, 0, sizeof(GameState));
|
||||
// Generate a new random seed for the new run
|
||||
gs.run_seed = (unsigned int)time(NULL);
|
||||
gs.game_over = 0;
|
||||
gs.game_won = 0;
|
||||
load_audio_assets(&gs);
|
||||
init_floor(&gs, 1);
|
||||
// Update window title with new seed
|
||||
char title[128];
|
||||
snprintf(title, sizeof(title), "Roguelike - Seed: %u", gs.run_seed);
|
||||
SetWindowTitle(title);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -585,24 +564,21 @@ static void game_loop(unsigned int run_seed) {
|
|||
|
||||
// Floating texts follow world shake
|
||||
render_floating_texts(gs.floating_texts, gs.floating_count, gs.shake_x, gs.shake_y);
|
||||
render_ui(&gs.player, &fontTTF);
|
||||
render_ui(&gs.player);
|
||||
|
||||
// Draw action log
|
||||
render_action_log(gs.action_log, gs.log_count, gs.log_head, &fontTTF);
|
||||
render_action_log(gs.action_log, gs.log_count, gs.log_head);
|
||||
|
||||
// Draw inventory overlay if active
|
||||
if (gs.show_inventory) {
|
||||
render_inventory_overlay(&gs.player, gs.inv_selected, &fontTTF);
|
||||
render_inventory_overlay(&gs.player, gs.inv_selected);
|
||||
}
|
||||
|
||||
// Draw message if any
|
||||
if (gs.last_message != NULL && gs.message_timer > 0) {
|
||||
render_message(gs.last_message, &fontTTF);
|
||||
render_message(gs.last_message);
|
||||
}
|
||||
|
||||
// Draw persistent seed display in top right
|
||||
render_seed_display(gs.run_seed);
|
||||
|
||||
// Draw game over screen
|
||||
if (gs.game_over) {
|
||||
// Compute final score
|
||||
|
|
@ -613,7 +589,7 @@ static void game_loop(unsigned int run_seed) {
|
|||
}
|
||||
render_end_screen(gs.game_won, gs.total_kills, gs.items_collected, gs.damage_dealt, gs.damage_taken,
|
||||
gs.crits_landed, gs.times_hit, gs.potions_used, gs.floors_reached, gs.turn_count,
|
||||
gs.final_score, gs.run_seed, &fontTTF);
|
||||
gs.final_score);
|
||||
}
|
||||
|
||||
EndDrawing();
|
||||
|
|
@ -623,67 +599,17 @@ static void game_loop(unsigned int run_seed) {
|
|||
}
|
||||
}
|
||||
|
||||
// Check if a string is a valid unsigned integer
|
||||
static int is_valid_uint(const char *str) {
|
||||
if (str == NULL || *str == '\0')
|
||||
return 0;
|
||||
// Check for optional leading +
|
||||
if (*str == '+')
|
||||
str++;
|
||||
// Must have at least one digit
|
||||
if (*str == '\0')
|
||||
return 0;
|
||||
// All characters must be digits
|
||||
for (const char *p = str; *p != '\0'; p++) {
|
||||
if (!isdigit((unsigned char)*p))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
// Parse command-line arguments
|
||||
unsigned int run_seed = 0;
|
||||
int seed_provided = 0;
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "--seed") == 0) {
|
||||
if (i + 1 >= argc) {
|
||||
fprintf(stderr, "Error: --seed requires a value\n");
|
||||
fprintf(stderr, "Usage: %s [--seed <number>]\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
const char *seed_str = argv[i + 1];
|
||||
if (!is_valid_uint(seed_str)) {
|
||||
fprintf(stderr, "Error: Invalid seed value: %s\n", seed_str);
|
||||
fprintf(stderr, "Seed must be a non-negative integer\n");
|
||||
return 1;
|
||||
}
|
||||
run_seed = (unsigned int)strtoul(seed_str, NULL, 10);
|
||||
seed_provided = 1;
|
||||
i++; // Skip the value
|
||||
}
|
||||
}
|
||||
|
||||
// If no seed provided, generate random seed from time
|
||||
if (!seed_provided) {
|
||||
run_seed = (unsigned int)time(NULL);
|
||||
}
|
||||
|
||||
printf("Starting game with seed: %u\n", run_seed);
|
||||
|
||||
int main(void) {
|
||||
// Initialize audio
|
||||
audio_init();
|
||||
// Initialize random number generator
|
||||
SetRandomSeed(88435);
|
||||
// Initialize window with seed in title
|
||||
char title[128];
|
||||
snprintf(title, sizeof(title), "Roguelike - Seed: %u", run_seed);
|
||||
InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT + 60, title);
|
||||
// Initialize window
|
||||
InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT + 60, "Roguelike");
|
||||
SetTargetFPS(60);
|
||||
|
||||
// Run game
|
||||
game_loop(run_seed);
|
||||
game_loop();
|
||||
|
||||
// Cleanup
|
||||
CloseWindow();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#include "map.h"
|
||||
#include "rng/rng.h"
|
||||
#include "rng.h"
|
||||
#include "settings.h"
|
||||
#include "utils.h"
|
||||
#include <stdlib.h>
|
||||
|
|
@ -170,6 +170,9 @@ void get_random_floor_tile(Map *map, int *x, int *y, int attempts) {
|
|||
}
|
||||
|
||||
void dungeon_generate(Dungeon *d, Map *map, int floor_num) {
|
||||
// Seed RNG with floor number for deterministic generation
|
||||
rng_seed(floor_num * 12345);
|
||||
|
||||
// Initialize map to all walls
|
||||
map_init(map);
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
#include "movement.h"
|
||||
#include "enemy.h"
|
||||
#include "map/map.h"
|
||||
#include "map.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
// Check if position is occupied by player
|
||||
|
|
|
|||
173
src/render.c
173
src/render.c
|
|
@ -1,5 +1,7 @@
|
|||
#include "render.h"
|
||||
#include "common.h"
|
||||
#include "items.h"
|
||||
#include "raylib.h"
|
||||
#include "settings.h"
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
|
@ -124,7 +126,7 @@ void render_items(const Item *items, int count, const unsigned char visible[MAP_
|
|||
}
|
||||
}
|
||||
|
||||
void render_ui(const Player *p, Font *font) {
|
||||
void render_ui(const Player *p) {
|
||||
// HUD Panel
|
||||
const int hud_y = MAP_HEIGHT * TILE_SIZE;
|
||||
const int hud_height = 60;
|
||||
|
|
@ -168,8 +170,7 @@ void render_ui(const Player *p, Font *font) {
|
|||
int bar_height = 16;
|
||||
|
||||
// HP Label, above bar
|
||||
// Vector2 hp_width = MeasureTextEx(*font, "HP", BIG_FONT, NAR_CHAR_SPACE);
|
||||
DrawTextEx(*font, "HP", (Vector2){bar_x, bar_y - 17}, BIG_FONT, NAR_CHAR_SPACE, text_dim);
|
||||
DrawText("HP", bar_x, bar_y - 11, 9, text_dim);
|
||||
|
||||
// HP Bar background
|
||||
DrawRectangle(bar_x, bar_y, bar_width, bar_height, (Color){20, 15, 15, 255});
|
||||
|
|
@ -194,9 +195,8 @@ void render_ui(const Player *p, Font *font) {
|
|||
// HP text, centered in bar
|
||||
char hp_text[32];
|
||||
snprintf(hp_text, sizeof(hp_text), "%d/%d", p->hp, p->max_hp);
|
||||
int hp_text_w = MeasureText(hp_text, 12);
|
||||
DrawTextEx(*font, hp_text, (Vector2){bar_x + (bar_width - hp_text_w) / 2.0f, bar_y + 2}, MEDIUM_FONT,
|
||||
SMALL_CHAR_SPACE, WHITE);
|
||||
int hp_text_w = MeasureText(hp_text, 10);
|
||||
DrawText(hp_text, bar_x + (bar_width - hp_text_w) / 2, bar_y + 2, 10, WHITE);
|
||||
|
||||
// Status effects
|
||||
int effect_x = bar_x;
|
||||
|
|
@ -231,7 +231,7 @@ void render_ui(const Player *p, Font *font) {
|
|||
if (p->effects[i].duration > 0) {
|
||||
char eff_text[16];
|
||||
snprintf(eff_text, sizeof(eff_text), "%s%d", eff_label, p->effects[i].duration);
|
||||
DrawTextEx(*font, eff_text, (Vector2){effect_x, effect_y}, SMALL_FONT, NAR_CHAR_SPACE, eff_color);
|
||||
DrawText(eff_text, effect_x, effect_y, 9, eff_color);
|
||||
effect_x += 28;
|
||||
}
|
||||
}
|
||||
|
|
@ -243,68 +243,65 @@ void render_ui(const Player *p, Font *font) {
|
|||
// Floor
|
||||
char floor_text[16];
|
||||
snprintf(floor_text, sizeof(floor_text), "F%d", p->floor);
|
||||
DrawTextEx(*font, floor_text, (Vector2){stats_x, stats_y}, 16, NORM_CHAR_SPACE, text_bright);
|
||||
DrawTextEx(*font, "Floor", (Vector2){stats_x, stats_y + 16}, 12, NAR_CHAR_SPACE, text_dim);
|
||||
DrawText(floor_text, stats_x, stats_y, 14, text_bright);
|
||||
DrawText("Floor", stats_x, stats_y + 16, 9, text_dim);
|
||||
|
||||
// ATK
|
||||
char atk_text[16];
|
||||
snprintf(atk_text, sizeof(atk_text), "%d", p->attack);
|
||||
DrawTextEx(*font, atk_text, (Vector2){stats_x + stat_spacing, stats_y}, 16, NORM_CHAR_SPACE, YELLOW);
|
||||
DrawTextEx(*font, "ATK", (Vector2){stats_x + stat_spacing, stats_y + 16}, 12, NAR_CHAR_SPACE, text_dim);
|
||||
DrawText(atk_text, stats_x + stat_spacing, stats_y, 14, YELLOW);
|
||||
DrawText("ATK", stats_x + stat_spacing, stats_y + 16, 9, text_dim);
|
||||
|
||||
// DEF
|
||||
char def_text[16];
|
||||
snprintf(def_text, sizeof(def_text), "%d", p->defense);
|
||||
DrawTextEx(*font, def_text, (Vector2){stats_x + stat_spacing * 2, stats_y}, 16, NORM_CHAR_SPACE,
|
||||
(Color){100, 150, 255, 255});
|
||||
DrawTextEx(*font, "DEF", (Vector2){stats_x + stat_spacing * 2, stats_y + 16}, 12, NAR_CHAR_SPACE, text_dim);
|
||||
DrawText(def_text, stats_x + stat_spacing * 2, stats_y, 14, (Color){100, 150, 255, 255});
|
||||
DrawText("DEF", stats_x + stat_spacing * 2, stats_y + 16, 9, text_dim);
|
||||
|
||||
int equip_x = section2_end + 15;
|
||||
int equip_y = hud_y + 8;
|
||||
|
||||
// Weapon slot
|
||||
DrawTextEx(*font, "WEAPON", (Vector2){equip_x, equip_y}, MEDIUM_FONT, NAR_CHAR_SPACE, text_dim);
|
||||
DrawText("WEAPON", equip_x, equip_y, 9, text_dim);
|
||||
if (p->has_weapon) {
|
||||
const char *weapon_name = item_get_name(&p->equipped_weapon);
|
||||
if (weapon_name) {
|
||||
char weapon_text[64];
|
||||
snprintf(weapon_text, sizeof(weapon_text), "%s +%d [%s]", weapon_name, p->equipped_weapon.power,
|
||||
dmg_class_get_short(p->equipped_weapon.dmg_class));
|
||||
DrawTextEx(*font, weapon_text, (Vector2){equip_x, equip_y + 11}, 10, NAR_CHAR_SPACE, (Color){255, 220, 100, 255});
|
||||
DrawText(weapon_text, equip_x, equip_y + 11, 10, (Color){255, 220, 100, 255});
|
||||
}
|
||||
} else {
|
||||
DrawTextEx(*font, "None [IMP]", (Vector2){equip_x, equip_y + 11}, 10, NAR_CHAR_SPACE, (Color){80, 75, 70, 255});
|
||||
DrawText("None [IMP]", equip_x, equip_y + 11, 10, (Color){80, 75, 70, 255});
|
||||
}
|
||||
|
||||
// Armor slot
|
||||
DrawTextEx(*font, "ARMOR", (Vector2){equip_x, equip_y + 26}, MEDIUM_FONT, NAR_CHAR_SPACE, text_dim);
|
||||
DrawText("ARMOR", equip_x, equip_y + 26, 9, text_dim);
|
||||
if (p->has_armor) {
|
||||
const char *armor_name = item_get_name(&p->equipped_armor);
|
||||
if (armor_name) {
|
||||
char armor_text[48];
|
||||
snprintf(armor_text, sizeof(armor_text), "%s +%d", armor_name, p->equipped_armor.power);
|
||||
DrawTextEx(*font, armor_text, (Vector2){equip_x, equip_y + 37}, 10, NAR_CHAR_SPACE, (Color){100, 150, 255, 255});
|
||||
DrawText(armor_text, equip_x, equip_y + 37, 10, (Color){100, 150, 255, 255});
|
||||
}
|
||||
} else {
|
||||
DrawTextEx(*font, "None", (Vector2){equip_x, equip_y + 37}, 10, NAR_CHAR_SPACE, (Color){80, 75, 70, 255});
|
||||
DrawText("None", equip_x, equip_y + 37, 10, (Color){80, 75, 70, 255});
|
||||
}
|
||||
|
||||
int ctrl_x = section3_end + 20;
|
||||
int ctrl_y = hud_y + 14;
|
||||
|
||||
DrawTextEx(*font, "[WASD] Move [G] Pickup [I] Inventory [U] Use", (Vector2){ctrl_x, ctrl_y}, MEDIUM_FONT,
|
||||
MED_CHAR_SPACE, (Color){139, 119, 89, 255});
|
||||
DrawTextEx(*font, "[E] Equip [D] Drop [Q] Quit", (Vector2){ctrl_x, ctrl_y + 16}, MEDIUM_FONT, MED_CHAR_SPACE,
|
||||
(Color){139, 119, 89, 255});
|
||||
DrawText("[WASD] Move [G] Pickup [I] Inventory [U] Use", ctrl_x, ctrl_y, 11, (Color){139, 119, 89, 255});
|
||||
DrawText("[E] Equip [D] Drop [Q] Quit", ctrl_x, ctrl_y + 16, 11, (Color){139, 119, 89, 255});
|
||||
|
||||
// INV count in top-right corner of HUD
|
||||
char inv_text[16];
|
||||
snprintf(inv_text, sizeof(inv_text), "INV: %d/%d", p->inventory_count, MAX_INVENTORY);
|
||||
int inv_width = MeasureText(inv_text, 10);
|
||||
DrawTextEx(*font, inv_text, (Vector2){SCREEN_WIDTH - inv_width - 10, hud_y + 5}, 10, NAR_CHAR_SPACE, GREEN);
|
||||
DrawText(inv_text, SCREEN_WIDTH - inv_width - 10, hud_y + 5, 10, GREEN);
|
||||
}
|
||||
|
||||
void render_action_log(const char log[5][128], int count, int head, Font *font) {
|
||||
void render_action_log(const char log[5][128], int count, int head) {
|
||||
// Roguelike scroll/log panel styling
|
||||
const int log_width = 250;
|
||||
const int log_height = 90;
|
||||
|
|
@ -326,8 +323,7 @@ void render_action_log(const char log[5][128], int count, int head, Font *font)
|
|||
|
||||
// Title bar
|
||||
DrawRectangle(log_x + 4, log_y + 4, log_width - 8, 16, (Color){30, 25, 20, 255});
|
||||
DrawTextEx(*font, "MESSAGE LOG", (Vector2){log_x + 8, log_y + 6}, MEDIUM_FONT, NAR_CHAR_SPACE,
|
||||
(Color){180, 160, 130, 255});
|
||||
DrawText("MESSAGE LOG", log_x + 8, log_y + 6, 10, (Color){180, 160, 130, 255});
|
||||
|
||||
// Separator line under title
|
||||
DrawLine(log_x + 4, log_y + 22, log_x + log_width - 5, log_y + 22, log_border_dark);
|
||||
|
|
@ -352,16 +348,15 @@ void render_action_log(const char log[5][128], int count, int head, Font *font)
|
|||
} else {
|
||||
text_color = (Color){120, 110, 100, 200}; // oldest: dim
|
||||
}
|
||||
DrawTextEx(*font, log[idx], (Vector2){text_x, text_start_y + i * line_height}, NORM_FONT, SMALL_CHAR_SPACE,
|
||||
text_color);
|
||||
DrawText(log[idx], text_x, text_start_y + i * line_height, 10, text_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void render_inventory_overlay(const Player *p, int selected, Font *font) {
|
||||
void render_inventory_overlay(const Player *p, int selected) {
|
||||
// Overlay dimensions
|
||||
int ov_width = 360;
|
||||
int ov_height = 320;
|
||||
int ov_height = 300;
|
||||
Rectangle overlay = {(float)(SCREEN_WIDTH - ov_width) / 2, (float)(SCREEN_HEIGHT - ov_height) / 2 - 60,
|
||||
(float)ov_width, (float)ov_height};
|
||||
DrawRectangleRec(overlay, (Color){12, 12, 12, 252});
|
||||
|
|
@ -369,15 +364,13 @@ void render_inventory_overlay(const Player *p, int selected, Font *font) {
|
|||
|
||||
// Title
|
||||
const char *title = "INVENTORY";
|
||||
// int title_w = MeasureText(title, 24);
|
||||
Vector2 t_w = MeasureTextEx(*font, title, 30, NORM_CHAR_SPACE);
|
||||
DrawTextEx(*font, title, (Vector2){overlay.x + (overlay.width - t_w.x) / 2, overlay.y + 10}, HUGE_FONT,
|
||||
NORM_CHAR_SPACE, WHITE);
|
||||
int title_w = MeasureText(title, 24);
|
||||
DrawText(title, overlay.x + (overlay.width - title_w) / 2, overlay.y + 12, 24, WHITE);
|
||||
|
||||
// Draw each inventory slot
|
||||
char slot_text[64];
|
||||
int row_height = 26;
|
||||
int start_y = overlay.y + 40;
|
||||
int start_y = overlay.y + 50;
|
||||
|
||||
for (int i = 0; i < MAX_INVENTORY; i++) {
|
||||
int y_pos = start_y + (i * row_height);
|
||||
|
|
@ -394,8 +387,7 @@ void render_inventory_overlay(const Player *p, int selected, Font *font) {
|
|||
|
||||
// Slot number
|
||||
snprintf(slot_text, sizeof(slot_text), "%d.", i + 1);
|
||||
DrawTextEx(*font, slot_text, (Vector2){overlay.x + 16, y_pos + 4}, MEDIUM_FONT, SMALL_CHAR_SPACE,
|
||||
(Color){80, 80, 80, 255});
|
||||
DrawText(slot_text, overlay.x + 16, y_pos + 4, 14, (Color){80, 80, 80, 255});
|
||||
|
||||
// Item name
|
||||
const char *name = item_get_name(item);
|
||||
|
|
@ -403,31 +395,31 @@ void render_inventory_overlay(const Player *p, int selected, Font *font) {
|
|||
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};
|
||||
DrawTextEx(*font, name, (Vector2){overlay.x + 45, y_pos + 4}, NORM_FONT, SMALL_CHAR_SPACE, name_color);
|
||||
DrawText(name, overlay.x + 45, y_pos + 4, 14, name_color);
|
||||
}
|
||||
|
||||
// Power
|
||||
snprintf(slot_text, sizeof(slot_text), "+%d", item->power);
|
||||
DrawTextEx(*font, slot_text, (Vector2){overlay.x + 150, y_pos + 4}, NORM_FONT, SMALL_CHAR_SPACE, YELLOW);
|
||||
DrawText(slot_text, overlay.x + 150, y_pos + 4, 14, YELLOW);
|
||||
|
||||
// Action
|
||||
if (item->type == ITEM_POTION) {
|
||||
DrawTextEx(*font, "[U]se", (Vector2){overlay.x + 200, y_pos + 4}, NORM_FONT, SMALL_CHAR_SPACE, GREEN);
|
||||
DrawText("[U]se", overlay.x + 200, y_pos + 4, 14, GREEN);
|
||||
} else {
|
||||
DrawTextEx(*font, "[E]quip [D]rop", (Vector2){overlay.x + 200, y_pos + 4}, NORM_FONT, SMALL_CHAR_SPACE, GOLD);
|
||||
DrawText("[E]quip [D]rop", overlay.x + 200, y_pos + 4, 14, GOLD);
|
||||
}
|
||||
} else {
|
||||
// Empty slot
|
||||
snprintf(slot_text, sizeof(slot_text), "%d.", i + 1);
|
||||
DrawTextEx(*font, slot_text, (Vector2){overlay.x + 16, y_pos + 4}, MEDIUM_FONT, SMALL_CHAR_SPACE,
|
||||
(Color){40, 40, 40, 255});
|
||||
DrawText(slot_text, overlay.x + 16, y_pos + 4, 14, (Color){40, 40, 40, 255});
|
||||
}
|
||||
}
|
||||
|
||||
// Instructions at bottom
|
||||
const char *hint = "[1-0] Select [E] Equip [U] Use [D] Drop [I/ESC] Close";
|
||||
Vector2 hint_w = MeasureTextEx(*font, hint, SMALL_FONT, NAR_CHAR_SPACE);
|
||||
DrawTextEx(*font, hint, (Vector2){overlay.x + (overlay.width - hint_w.x) / 2.0f, overlay.y + overlay.height - 22},
|
||||
SMALL_FONT, NAR_CHAR_SPACE, (Color){80, 80, 80, 255});
|
||||
int hint_w = MeasureText(hint, 12);
|
||||
DrawText(hint, overlay.x + (overlay.width - hint_w) / 2, overlay.y + overlay.height - 22, 12,
|
||||
(Color){65, 65, 65, 255});
|
||||
}
|
||||
|
||||
static Color label_color(FloatingText *ft, int alpha) {
|
||||
|
|
@ -515,7 +507,7 @@ void render_floating_texts(FloatingText *texts, int count, int shake_x, int shak
|
|||
}
|
||||
|
||||
void render_end_screen(int is_victory, int kills, int items, int damage_dealt, int damage_taken, int crits,
|
||||
int times_hit, int potions, int floors, int turns, int score, unsigned int seed, Font *font) {
|
||||
int times_hit, int potions, int floors, int turns, int score) {
|
||||
// Semi-transparent overlay
|
||||
Rectangle overlay = {0, 0, (float)SCREEN_WIDTH, (float)SCREEN_HEIGHT};
|
||||
DrawRectangleRec(overlay, (Color){0, 0, 0, 210});
|
||||
|
|
@ -525,14 +517,13 @@ void render_end_screen(int is_victory, int kills, int items, int damage_dealt, i
|
|||
int title_font_size = 60;
|
||||
Color title_color = is_victory ? GOLD : RED;
|
||||
int title_width = MeasureText(title, title_font_size);
|
||||
DrawTextEx(*font, title, (Vector2){(SCREEN_WIDTH - title_width) / 2.0f, 30}, title_font_size, NORM_CHAR_SPACE,
|
||||
title_color);
|
||||
DrawText(title, (SCREEN_WIDTH - title_width) / 2, 30, title_font_size, title_color);
|
||||
|
||||
// Stats box
|
||||
int box_x = SCREEN_WIDTH / 2 - 200;
|
||||
int box_y = 110;
|
||||
int box_w = 400;
|
||||
int box_h = 350;
|
||||
int box_h = 320;
|
||||
DrawRectangle(box_x, box_y, box_w, box_h, (Color){20, 20, 20, 240});
|
||||
DrawRectangleLines(box_x, box_y, box_w, box_h, (Color){100, 100, 100, 255});
|
||||
|
||||
|
|
@ -546,80 +537,71 @@ void render_end_screen(int is_victory, int kills, int items, int damage_dealt, i
|
|||
Color value_color = WHITE;
|
||||
|
||||
// Column 1
|
||||
DrawTextEx(*font, "Kills:", (Vector2){col1_x, row_y}, 18, NORM_CHAR_SPACE, label_color);
|
||||
DrawText("Kills:", col1_x, row_y, 18, label_color);
|
||||
snprintf(line, sizeof(line), "%d", kills);
|
||||
DrawTextEx(*font, line, (Vector2){col1_x + 80, row_y}, 18, NORM_CHAR_SPACE, value_color);
|
||||
DrawText(line, col1_x + 80, row_y, 18, value_color);
|
||||
row_y += line_height;
|
||||
|
||||
DrawTextEx(*font, "Items:", (Vector2){col1_x, row_y}, 18, NORM_CHAR_SPACE, label_color);
|
||||
DrawText("Items:", col1_x, row_y, 18, label_color);
|
||||
snprintf(line, sizeof(line), "%d", items);
|
||||
DrawTextEx(*font, line, (Vector2){col1_x + 80, row_y}, 18, NORM_CHAR_SPACE, value_color);
|
||||
DrawText(line, col1_x + 80, row_y, 18, value_color);
|
||||
row_y += line_height;
|
||||
|
||||
DrawTextEx(*font, "Damage Dealt:", (Vector2){col1_x, row_y}, 18, NORM_CHAR_SPACE, label_color);
|
||||
DrawText("Damage Dealt:", col1_x, row_y, 18, label_color);
|
||||
snprintf(line, sizeof(line), "%d", damage_dealt);
|
||||
DrawTextEx(*font, line, (Vector2){col1_x + 140, row_y}, 18, NORM_CHAR_SPACE, value_color);
|
||||
DrawText(line, col1_x + 140, row_y, 18, value_color);
|
||||
row_y += line_height;
|
||||
|
||||
DrawTextEx(*font, "Damage Taken:", (Vector2){col1_x, row_y}, 18, NORM_CHAR_SPACE, label_color);
|
||||
DrawText("Damage Taken:", col1_x, row_y, 18, label_color);
|
||||
snprintf(line, sizeof(line), "%d", damage_taken);
|
||||
DrawTextEx(*font, line, (Vector2){col1_x + 140, row_y}, 18, NORM_CHAR_SPACE, value_color);
|
||||
DrawText(line, col1_x + 140, row_y, 18, value_color);
|
||||
row_y += line_height;
|
||||
|
||||
DrawTextEx(*font, "Crits:", (Vector2){col1_x, row_y}, 18, NORM_CHAR_SPACE, label_color);
|
||||
DrawText("Crits:", col1_x, row_y, 18, label_color);
|
||||
snprintf(line, sizeof(line), "%d", crits);
|
||||
DrawTextEx(*font, line, (Vector2){col1_x + 80, row_y}, 18, NORM_CHAR_SPACE, value_color);
|
||||
DrawText(line, col1_x + 80, row_y, 18, value_color);
|
||||
row_y += line_height;
|
||||
|
||||
DrawTextEx(*font, "Times Hit:", (Vector2){col1_x, row_y}, 18, NORM_CHAR_SPACE, label_color);
|
||||
DrawText("Times Hit:", col1_x, row_y, 18, label_color);
|
||||
snprintf(line, sizeof(line), "%d", times_hit);
|
||||
DrawTextEx(*font, line, (Vector2){col1_x + 80, row_y}, 18, NORM_CHAR_SPACE, value_color);
|
||||
DrawText(line, col1_x + 80, row_y, 18, value_color);
|
||||
row_y += line_height;
|
||||
|
||||
// Column 2
|
||||
int col2_row_y = box_y + 20;
|
||||
DrawTextEx(*font, "Potions:", (Vector2){col2_x, col2_row_y}, 18, NORM_CHAR_SPACE, label_color);
|
||||
DrawText("Potions:", col2_x, col2_row_y, 18, label_color);
|
||||
snprintf(line, sizeof(line), "%d", potions);
|
||||
DrawTextEx(*font, line, (Vector2){col2_x + 80, col2_row_y}, 18, NORM_CHAR_SPACE, value_color);
|
||||
DrawText(line, col2_x + 80, col2_row_y, 18, value_color);
|
||||
col2_row_y += line_height;
|
||||
|
||||
DrawTextEx(*font, "Floors:", (Vector2){col2_x, col2_row_y}, 18, NORM_CHAR_SPACE, label_color);
|
||||
DrawText("Floors:", col2_x, col2_row_y, 18, label_color);
|
||||
snprintf(line, sizeof(line), "%d", floors);
|
||||
DrawTextEx(*font, line, (Vector2){col2_x + 80, col2_row_y}, 18, NORM_CHAR_SPACE, value_color);
|
||||
DrawText(line, col2_x + 80, col2_row_y, 18, value_color);
|
||||
col2_row_y += line_height;
|
||||
|
||||
DrawTextEx(*font, "Turns:", (Vector2){col2_x, col2_row_y}, 18, NORM_CHAR_SPACE, label_color);
|
||||
DrawText("Turns:", col2_x, col2_row_y, 18, label_color);
|
||||
snprintf(line, sizeof(line), "%d", turns);
|
||||
DrawTextEx(*font, line, (Vector2){col2_x + 80, col2_row_y}, 18, NORM_CHAR_SPACE, value_color);
|
||||
DrawText(line, col2_x + 80, col2_row_y, 18, value_color);
|
||||
col2_row_y += line_height;
|
||||
|
||||
// Score: placed below the last row of the longer column (6 items, row_y is already there)
|
||||
row_y += 10;
|
||||
DrawTextEx(*font, "SCORE:", (Vector2){col1_x, row_y}, 22, NORM_CHAR_SPACE, GOLD);
|
||||
DrawText("SCORE:", col1_x, row_y, 22, GOLD);
|
||||
snprintf(line, sizeof(line), "%d", score);
|
||||
DrawTextEx(*font, line, (Vector2){col1_x + 90, col2_row_y}, 22, NORM_CHAR_SPACE, GOLD);
|
||||
row_y += 35;
|
||||
DrawText(line, col1_x + 90, row_y, 22, GOLD);
|
||||
|
||||
// Seed display
|
||||
DrawTextEx(*font, "SEED:", (Vector2){col1_x, row_y}, 18, SMALL_CHAR_SPACE, label_color);
|
||||
snprintf(line, sizeof(line), "%u", seed);
|
||||
DrawTextEx(*font, line, (Vector2){col1_x + 60, row_y}, 18, SMALL_CHAR_SPACE, END_SEED);
|
||||
|
||||
// Instructions
|
||||
if (is_victory) {
|
||||
const char *subtitle = "Press R to play again or Q to quit";
|
||||
int sub_width = MeasureText(subtitle, 20);
|
||||
DrawTextEx(*font, subtitle, (Vector2){(SCREEN_WIDTH - sub_width) / 2.0f, SCREEN_HEIGHT - 50}, 20, NORM_CHAR_SPACE,
|
||||
LIGHTGRAY);
|
||||
DrawText(subtitle, (SCREEN_WIDTH - sub_width) / 2, SCREEN_HEIGHT - 50, 20, LIGHTGRAY);
|
||||
} else {
|
||||
const char *subtitle = "Press R to restart or Q to quit";
|
||||
int sub_width = MeasureText(subtitle, 20);
|
||||
DrawTextEx(*font, subtitle, (Vector2){(SCREEN_WIDTH - sub_width) / 2.0f, SCREEN_HEIGHT - 50}, 20, NORM_CHAR_SPACE,
|
||||
LIGHTGRAY);
|
||||
DrawText(subtitle, (SCREEN_WIDTH - sub_width) / 2, SCREEN_HEIGHT - 50, 20, LIGHTGRAY);
|
||||
}
|
||||
}
|
||||
|
||||
void render_message(const char *message, Font *font) {
|
||||
void render_message(const char *message) {
|
||||
if (message == NULL)
|
||||
return;
|
||||
|
||||
|
|
@ -654,8 +636,8 @@ void render_message(const char *message, Font *font) {
|
|||
longest_line_width = current_line_width;
|
||||
|
||||
// Measure full message
|
||||
Vector2 total_msg_width = MeasureTextEx(*font, message, font_size, NORM_CHAR_SPACE);
|
||||
int box_width = total_msg_width.x + (padding_x * 2);
|
||||
int total_msg_width = MeasureText(message, font_size);
|
||||
int box_width = total_msg_width + (padding_x * 2);
|
||||
|
||||
// If message is too long, use wrapped width
|
||||
if (box_width > max_box_width) {
|
||||
|
|
@ -679,7 +661,7 @@ void render_message(const char *message, Font *font) {
|
|||
DrawRectangleLines((int)msg_bg.x, (int)msg_bg.y, (int)msg_bg.width, (int)msg_bg.height, (Color){180, 180, 180, 255});
|
||||
|
||||
// Draw text centered
|
||||
int text_x = (SCREEN_WIDTH - total_msg_width.x) / 2;
|
||||
int text_x = (SCREEN_WIDTH - total_msg_width) / 2;
|
||||
int text_y = (SCREEN_HEIGHT - font_size) / 2;
|
||||
|
||||
// For wrapped text, draw at box center with padding
|
||||
|
|
@ -688,20 +670,5 @@ void render_message(const char *message, Font *font) {
|
|||
text_y = (int)box_y + padding_y;
|
||||
}
|
||||
|
||||
DrawTextEx(*font, message, (Vector2){text_x, text_y}, font_size, NORM_CHAR_SPACE, WHITE);
|
||||
}
|
||||
|
||||
void render_seed_display(unsigned int seed) {
|
||||
char seed_text[64];
|
||||
snprintf(seed_text, sizeof(seed_text), "Seed: %u", seed);
|
||||
|
||||
const int font_size = 14;
|
||||
int text_width = MeasureText(seed_text, font_size);
|
||||
|
||||
// Position at top right with padding
|
||||
int x = SCREEN_WIDTH - text_width - 10;
|
||||
int y = 5;
|
||||
|
||||
// Draw with non-obstructive dim text color
|
||||
DrawText(seed_text, x, y, font_size, TEXT_DIM);
|
||||
DrawText(message, text_x, text_y, font_size, WHITE);
|
||||
}
|
||||
|
|
|
|||
17
src/render.h
17
src/render.h
|
|
@ -1,8 +1,7 @@
|
|||
|
||||
#ifndef RENDER_H
|
||||
#define RENDER_H
|
||||
|
||||
#include "game_state.h"
|
||||
#include "common.h"
|
||||
|
||||
// HUD colors
|
||||
#define HUD_BG (Color){25, 20, 15, 255}
|
||||
|
|
@ -69,7 +68,6 @@
|
|||
#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}
|
||||
#define END_SEED (Color){150, 200, 255, 255}
|
||||
|
||||
// Portrait placeholder
|
||||
// FIXME: remove when player sprites are available
|
||||
|
|
@ -88,25 +86,22 @@ void render_enemies(const Enemy *enemies, int count, const unsigned char visible
|
|||
void render_items(const Item *items, int count, const unsigned char visible[MAP_HEIGHT][MAP_WIDTH]);
|
||||
|
||||
// Render UI overlay
|
||||
void render_ui(const Player *p, Font *font);
|
||||
void render_ui(const Player *p);
|
||||
|
||||
// Render action log (bottom left corner)
|
||||
void render_action_log(const char log[5][128], int count, int head, Font *font);
|
||||
void render_action_log(const char log[5][128], int count, int head);
|
||||
|
||||
// Render inventory selection overlay
|
||||
void render_inventory_overlay(const Player *p, int selected, Font *font);
|
||||
void render_inventory_overlay(const Player *p, int selected);
|
||||
|
||||
// Render floating damage text
|
||||
void render_floating_texts(FloatingText *texts, int count, int shake_x, int shake_y);
|
||||
|
||||
// Render end screen (victory or death) with stats breakdown
|
||||
void render_end_screen(int is_victory, int kills, int items, int damage_dealt, int damage_taken, int crits,
|
||||
int times_hit, int potions, int floors, int turns, int score, unsigned int seed, Font *font);
|
||||
int times_hit, int potions, int floors, int turns, int score);
|
||||
|
||||
// Render a message popup
|
||||
void render_message(const char *message, Font *font);
|
||||
|
||||
// Render seed display at top right of screen
|
||||
void render_seed_display(unsigned int seed);
|
||||
void render_message(const char *message);
|
||||
|
||||
#endif // RENDER_H
|
||||
|
|
|
|||
|
|
@ -8,21 +8,6 @@
|
|||
#define SCREEN_WIDTH (MAP_WIDTH * TILE_SIZE)
|
||||
#define SCREEN_HEIGHT (MAP_HEIGHT * TILE_SIZE)
|
||||
|
||||
// Font constants
|
||||
#define NORM_CHAR_SPACE 4.0f
|
||||
#define MED_CHAR_SPACE 2.5f
|
||||
#define SMALL_CHAR_SPACE 1.6f
|
||||
#define NAR_CHAR_SPACE 1.0f
|
||||
#define CRAMPED_CHAR_SPACE 0.5f
|
||||
|
||||
#define TINY_FONT 8
|
||||
#define SMALL_FONT 10
|
||||
#define NORM_FONT 12
|
||||
#define MEDIUM_FONT 14
|
||||
#define LARGE_FONT 18
|
||||
#define BIG_FONT 22
|
||||
#define HUGE_FONT 30
|
||||
|
||||
// Game Limits
|
||||
#define MAX_ENEMIES 64
|
||||
#define MAX_ITEMS 128
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue