From c2412ac4b1f73fefb0327a688638d2a6e9287bae Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Thu, 9 Apr 2026 09:35:52 +0300 Subject: [PATCH 01/13] various: implement fog of war; make enemy AI slightly more intelligent Signed-off-by: NotAShelf Change-Id: I3e22dbc5e10690871255980c52a24c226a6a6964 --- src/common.h | 2 ++ src/enemy.c | 47 +++++++++++++++++++++++++++-------- src/main.c | 11 +++++++-- src/map.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/map.h | 6 +++++ src/render.c | 31 ++++++++++++++++++------ src/render.h | 4 +-- src/settings.h | 5 ++++ 8 files changed, 151 insertions(+), 21 deletions(-) diff --git a/src/common.h b/src/common.h index 61ab044..2f4021f 100644 --- a/src/common.h +++ b/src/common.h @@ -30,6 +30,8 @@ typedef struct { TileType tiles[MAP_HEIGHT][MAP_WIDTH]; Room rooms[MAX_ROOMS]; int room_count; + unsigned char visible[MAP_HEIGHT][MAP_WIDTH]; + unsigned char remembered[MAP_HEIGHT][MAP_WIDTH]; } Map; // Dungeon diff --git a/src/enemy.c b/src/enemy.c index 9f72670..764d2d9 100644 --- a/src/enemy.c +++ b/src/enemy.c @@ -3,6 +3,7 @@ #include "common.h" #include "map.h" #include "rng.h" +#include "settings.h" #include // Forward declaration @@ -134,11 +135,9 @@ int is_enemy_at(const Enemy *enemies, int count, int x, int y) { return 0; } -// Check if enemy can see player (adjacent) -static int can_see_player(Enemy *e, Player *p) { - int dx = p->x - e->x; - int dy = p->y - e->y; - return (dx >= -1 && dx <= 1 && dy >= -1 && dy <= 1); +// Check if enemy can see player (within view range and line of sight) +static int can_see_player(Enemy *e, Player *p, Map *map) { + return can_see_entity(map, e->x, e->y, p->x, p->y, ENEMY_VIEW_RANGE); } // Check if position is occupied by player @@ -178,7 +177,27 @@ static void enemy_move_toward_player(Enemy *e, Player *p, Map *map, Enemy *all_e } } -// Perform a single action for an enemy (attack if adjacent, otherwise move) +// Move enemy in a random direction (patrol) +static void enemy_patrol(Enemy *e, Map *map, Enemy *all_enemies, int enemy_count) { + if (rng_int(0, 100) > ENEMY_PATROL_MOVE_CHANCE) + return; + + int dx = rng_int(-1, 1); + int dy = rng_int(-1, 1); + + if (dx == 0 && dy == 0) + return; + + int new_x = e->x + dx; + int new_y = e->y + dy; + + if (is_floor(map, new_x, new_y) && !is_enemy_at(all_enemies, enemy_count, new_x, new_y)) { + e->x = new_x; + e->y = new_y; + } +} + +// Perform a single action for an enemy (attack if visible, otherwise patrol) void enemy_act(Enemy *e, Player *p, Map *map, Enemy *all_enemies, int enemy_count) { if (!e->alive) return; @@ -187,14 +206,22 @@ void enemy_act(Enemy *e, Player *p, Map *map, Enemy *all_enemies, int enemy_coun if (combat_has_effect(e->effects, e->effect_count, EFFECT_STUN)) return; - // Check if adjacent to player - attack - if (can_see_player(e, p)) { + int can_see = can_see_player(e, p, map); + + // Attack if adjacent to player + if (can_see && can_see_entity(map, e->x, e->y, p->x, p->y, 1)) { combat_enemy_attack(e, p); return; } - // Otherwise, move toward player - enemy_move_toward_player(e, p, map, all_enemies, enemy_count); + // Move toward player if visible + if (can_see) { + enemy_move_toward_player(e, p, map, all_enemies, enemy_count); + return; + } + + // Player not visible - patrol randomly + enemy_patrol(e, map, all_enemies, enemy_count); } void enemy_update_all(Enemy enemies[], int count, Player *p, Map *map) { diff --git a/src/main.c b/src/main.c index d450d75..aa367ab 100644 --- a/src/main.c +++ b/src/main.c @@ -122,6 +122,9 @@ static void init_floor(GameState *gs, int floor_num) { } gs->player.floor = floor_num; + // Calculate initial visibility + calculate_visibility(&gs->map, gs->player.x, gs->player.y); + // Spawn enemies enemy_spawn(gs->enemies, &gs->enemy_count, &gs->map, &gs->player, floor_num); @@ -215,6 +218,9 @@ static void post_action(GameState *gs, Enemy *attacked_enemy) { } } + // Update visibility based on player's new position + calculate_visibility(&gs->map, gs->player.x, gs->player.y); + // Enemy turns - uses speed/cooldown system enemy_update_all(gs->enemies, gs->enemy_count, &gs->player, &gs->map); @@ -246,6 +252,7 @@ static int handle_stun_turn(GameState *gs) { if (gs->game_over) return 1; enemy_update_all(gs->enemies, gs->enemy_count, &gs->player, &gs->map); + calculate_visibility(&gs->map, gs->player.x, gs->player.y); if (gs->player.hp <= 0) gs->game_over = 1; gs->last_message = "You are stunned!"; @@ -541,8 +548,8 @@ static void game_loop(void) { cam.offset = (Vector2){(float)gs.shake_x, (float)gs.shake_y}; BeginMode2D(cam); render_map(&gs.map); - render_items(gs.items, gs.item_count); - render_enemies(gs.enemies, gs.enemy_count); + render_items(gs.items, gs.item_count, gs.map.visible); + render_enemies(gs.enemies, gs.enemy_count, gs.map.visible); render_player(&gs.player); EndMode2D(); diff --git a/src/map.c b/src/map.c index 24016c9..a29889b 100644 --- a/src/map.c +++ b/src/map.c @@ -1,6 +1,8 @@ #include "map.h" #include "rng.h" +#include "settings.h" #include "utils.h" +#include #include void map_init(Map *map) { @@ -186,3 +188,67 @@ void dungeon_generate(Dungeon *d, Map *map, int floor_num) { d->room_count = map->room_count; memcpy(d->rooms, map->rooms, sizeof(Room) * map->room_count); } + +int is_in_view_range(int x, int y, int view_x, int view_y, int range) { + int dx = x - view_x; + int dy = y - view_y; + return (dx * dx + dy * dy) <= (range * range); +} + +static int trace_line_of_sight(const Map *map, int x1, int y1, int x2, int y2) { + int dx = abs(x2 - x1); + int dy = abs(y2 - y1); + int sx = (x1 < x2) ? 1 : -1; + int sy = (y1 < y2) ? 1 : -1; + int err = dx - dy; + int x = x1; + int y = y1; + + while (1) { + if (!in_bounds(x, y, MAP_WIDTH, MAP_HEIGHT)) + return 0; + + if (x == x2 && y == y2) + return 1; + + if (map->tiles[y][x] == TILE_WALL && !(x == x1 && y == y1)) + return 0; + + int e2 = 2 * err; + if (e2 > -dy) { + err -= dy; + x += sx; + } + if (e2 < dx) { + err += dx; + y += sy; + } + } +} + +int has_line_of_sight(const Map *map, int x1, int y1, int x2, int y2) { + if (!in_bounds(x1, y1, MAP_WIDTH, MAP_HEIGHT) || !in_bounds(x2, y2, MAP_WIDTH, MAP_HEIGHT)) + return 0; + return trace_line_of_sight(map, x1, y1, x2, y2); +} + +int can_see_entity(const Map *map, int from_x, int from_y, int to_x, int to_y, int range) { + if (!is_in_view_range(to_x, to_y, from_x, from_y, range)) + return 0; + return has_line_of_sight(map, from_x, from_y, to_x, to_y); +} + +void calculate_visibility(Map *map, int x, int y) { + memset(map->visible, 0, sizeof(map->visible)); + + for (int ty = 0; ty < MAP_HEIGHT; ty++) { + for (int tx = 0; tx < MAP_WIDTH; tx++) { + if (is_in_view_range(tx, ty, x, y, PLAYER_VIEW_RANGE)) { + if (has_line_of_sight(map, x, y, tx, ty)) { + map->visible[ty][tx] = 1; + map->remembered[ty][tx] = 1; + } + } + } + } +} diff --git a/src/map.h b/src/map.h index 3cf6337..84f1119 100644 --- a/src/map.h +++ b/src/map.h @@ -18,4 +18,10 @@ void map_init(Map *map); // Get a random floor tile position void get_random_floor_tile(Map *map, int *x, int *y, int attempts); +// Visibility / Fog of War +int is_in_view_range(int x, int y, int view_x, int view_y, int range); +int has_line_of_sight(const Map *map, int x1, int y1, int x2, int y2); +void calculate_visibility(Map *map, int x, int y); +int can_see_entity(const Map *map, int from_x, int from_y, int to_x, int to_y, int range); + #endif // MAP_H diff --git a/src/render.c b/src/render.c index 3c24e15..206843d 100644 --- a/src/render.c +++ b/src/render.c @@ -11,18 +11,31 @@ void render_map(const Map *map) { for (int y = 0; y < MAP_HEIGHT; y++) { for (int x = 0; x < MAP_WIDTH; x++) { Rectangle rect = {(float)(x * TILE_SIZE), (float)(y * TILE_SIZE), (float)TILE_SIZE, (float)TILE_SIZE}; + int visible = map->visible[y][x]; + int remembered = map->remembered[y][x]; + + if (!visible && !remembered) { + DrawRectangleRec(rect, (Color){5, 5, 10, 255}); + continue; + } + + Color wall_color = visible ? DARKGRAY : (Color){25, 25, 30, 255}; + Color floor_color = visible ? BLACK : (Color){15, 15, 20, 255}; + Color stairs_color = visible ? (Color){100, 100, 100, 255} : (Color){40, 40, 45, 255}; switch (map->tiles[y][x]) { case TILE_WALL: - DrawRectangleRec(rect, DARKGRAY); + DrawRectangleRec(rect, wall_color); break; case TILE_FLOOR: - DrawRectangleRec(rect, BLACK); + DrawRectangleRec(rect, floor_color); break; case TILE_STAIRS: - DrawRectangleRec(rect, (Color){100, 100, 100, 255}); - // Draw stairs marker - DrawText(">", x * TILE_SIZE + 4, y * TILE_SIZE + 2, 12, WHITE); + DrawRectangleRec(rect, stairs_color); + if (visible) + DrawText(">", x * TILE_SIZE + 4, y * TILE_SIZE + 2, 12, WHITE); + else + DrawText(">", x * TILE_SIZE + 4, y * TILE_SIZE + 2, 12, (Color){60, 60, 65, 255}); break; } } @@ -34,10 +47,12 @@ void render_player(const Player *p) { DrawRectangleRec(rect, BLUE); } -void render_enemies(const Enemy *enemies, int count) { +void render_enemies(const Enemy *enemies, int count, const unsigned char visible[MAP_HEIGHT][MAP_WIDTH]) { for (int i = 0; i < count; i++) { if (!enemies[i].alive) continue; + if (!visible[enemies[i].y][enemies[i].x]) + continue; Rectangle rect = {(float)(enemies[i].x * TILE_SIZE), (float)(enemies[i].y * TILE_SIZE), (float)TILE_SIZE, (float)TILE_SIZE}; @@ -79,10 +94,12 @@ void render_enemies(const Enemy *enemies, int count) { } } -void render_items(const Item *items, int count) { +void render_items(const Item *items, int count, const unsigned char visible[MAP_HEIGHT][MAP_WIDTH]) { for (int i = 0; i < count; i++) { if (items[i].picked_up) continue; + if (!visible[items[i].y][items[i].x]) + continue; Rectangle rect = {(float)(items[i].x * TILE_SIZE), (float)(items[i].y * TILE_SIZE), (float)TILE_SIZE, (float)TILE_SIZE}; diff --git a/src/render.h b/src/render.h index 016291f..d934ffc 100644 --- a/src/render.h +++ b/src/render.h @@ -80,10 +80,10 @@ void render_map(const Map *map); void render_player(const Player *p); // Render all enemies -void render_enemies(const Enemy *enemies, int count); +void render_enemies(const Enemy *enemies, int count, const unsigned char visible[MAP_HEIGHT][MAP_WIDTH]); // Render all items -void render_items(const Item *items, int count); +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); diff --git a/src/settings.h b/src/settings.h index 8b8ac54..1b96056 100644 --- a/src/settings.h +++ b/src/settings.h @@ -63,4 +63,9 @@ // Message timer #define MESSAGE_TIMER_DURATION 60 +// Visibility / Fog of War +#define PLAYER_VIEW_RANGE 8 +#define ENEMY_VIEW_RANGE 6 +#define ENEMY_PATROL_MOVE_CHANCE 30 + #endif // SETTINGS_H From dbf8d4886c671912b547f62e145177c1ed542516 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Thu, 9 Apr 2026 12:31:52 +0300 Subject: [PATCH 02/13] enemy: add alert memory; vision variance based on type Signed-off-by: NotAShelf Change-Id: I2f5c7cac72c8772e5871b99026d106b46a6a6964 --- src/common.h | 5 +++ src/enemy.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++-- src/map.c | 2 ++ 3 files changed, 90 insertions(+), 3 deletions(-) diff --git a/src/common.h b/src/common.h index 2f4021f..08761c0 100644 --- a/src/common.h +++ b/src/common.h @@ -100,6 +100,11 @@ typedef struct { int status_chance; int crit_chance; // crit chance percentage (0-100) int crit_mult; // crit damage multiplier percentage (e.g. 150 = 1.5x) + // vision + int vision_range; + int alert; // 1 = aware of player, searching + int last_known_x; // last position where enemy saw player + int last_known_y; // status effects StatusEffect effects[MAX_EFFECTS]; int effect_count; diff --git a/src/enemy.c b/src/enemy.c index 764d2d9..0169096 100644 --- a/src/enemy.c +++ b/src/enemy.c @@ -69,6 +69,7 @@ void enemy_spawn(Enemy enemies[], int *count, Map *map, Player *p, int floor) { e.resistance[DMG_PIERCE] = 0; e.resistance[DMG_FIRE] = -25; e.resistance[DMG_POISON] = 50; + e.vision_range = 7; break; case ENEMY_SKELETON: e.max_hp = ENEMY_BASE_HP + floor + 2; @@ -86,6 +87,7 @@ void enemy_spawn(Enemy enemies[], int *count, Map *map, Player *p, int floor) { e.resistance[DMG_PIERCE] = 50; e.resistance[DMG_FIRE] = 25; e.resistance[DMG_POISON] = 75; + e.vision_range = 6; break; case ENEMY_ORC: e.max_hp = ENEMY_BASE_HP + floor + 4; @@ -103,6 +105,7 @@ void enemy_spawn(Enemy enemies[], int *count, Map *map, Player *p, int floor) { e.resistance[DMG_PIERCE] = -25; e.resistance[DMG_FIRE] = 0; e.resistance[DMG_POISON] = 0; + e.vision_range = 5; break; default: e.max_hp = ENEMY_BASE_HP; @@ -116,6 +119,7 @@ void enemy_spawn(Enemy enemies[], int *count, Map *map, Player *p, int floor) { e.crit_chance = ENEMY_CRIT_CHANCE; e.crit_mult = ENEMY_CRIT_MULT; memset(e.resistance, 0, sizeof(e.resistance)); + e.vision_range = ENEMY_VIEW_RANGE; break; } e.cooldown = e.speed; @@ -137,7 +141,7 @@ int is_enemy_at(const Enemy *enemies, int count, int x, int y) { // Check if enemy can see player (within view range and line of sight) static int can_see_player(Enemy *e, Player *p, Map *map) { - return can_see_entity(map, e->x, e->y, p->x, p->y, ENEMY_VIEW_RANGE); + return can_see_entity(map, e->x, e->y, p->x, p->y, e->vision_range); } // Check if position is occupied by player @@ -197,7 +201,68 @@ static void enemy_patrol(Enemy *e, Map *map, Enemy *all_enemies, int enemy_count } } -// Perform a single action for an enemy (attack if visible, otherwise patrol) +// Move enemy toward last known player position +static void enemy_move_to_last_known(Enemy *e, Map *map, Enemy *all_enemies, int enemy_count) { + int dx = 0, dy = 0; + + if (e->last_known_x > e->x) + dx = 1; + else if (e->last_known_x < e->x) + dx = -1; + + if (e->last_known_y > e->y) + dy = 1; + else if (e->last_known_y < e->y) + dy = -1; + + int new_x = e->x + dx; + int new_y = e->y; + + if (dx != 0 && is_floor(map, new_x, new_y) && !is_enemy_at(all_enemies, enemy_count, new_x, new_y)) { + e->x = new_x; + } else if (dy != 0) { + new_x = e->x; + new_y = e->y + dy; + if (is_floor(map, new_x, new_y) && !is_enemy_at(all_enemies, enemy_count, new_x, new_y)) { + e->x = new_x; + e->y = new_y; + } + } + + if (e->x == e->last_known_x && e->y == e->last_known_y) + e->alert = 0; +} + +// Check if position is within alert radius of another enemy +static int is_nearby_enemy(const Enemy *enemies, int count, int x, int y, int radius) { + for (int i = 0; i < count; i++) { + if (!enemies[i].alive) + continue; + int dx = enemies[i].x - x; + int dy = enemies[i].y - y; + if (dx * dx + dy * dy <= radius * radius) + return 1; + } + return 0; +} + +// Propagate alert to nearby enemies +static void propagate_alert(Enemy *trigger_enemy, Enemy *all_enemies, int enemy_count) { + for (int i = 0; i < enemy_count; i++) { + Enemy *e = &all_enemies[i]; + if (!e->alive || e == trigger_enemy) + continue; + if (e->alert) + continue; + if (is_nearby_enemy(all_enemies, enemy_count, e->x, e->y, 5)) { + e->alert = 1; + e->last_known_x = trigger_enemy->last_known_x; + e->last_known_y = trigger_enemy->last_known_y; + } + } +} + +// Perform a single action for an enemy (attack if visible, otherwise patrol or search) void enemy_act(Enemy *e, Player *p, Map *map, Enemy *all_enemies, int enemy_count) { if (!e->alive) return; @@ -208,19 +273,34 @@ void enemy_act(Enemy *e, Player *p, Map *map, Enemy *all_enemies, int enemy_coun int can_see = can_see_player(e, p, map); + // If we can see the player, update alert state and last known position + if (can_see) { + e->alert = 1; + e->last_known_x = p->x; + e->last_known_y = p->y; + } + // Attack if adjacent to player if (can_see && can_see_entity(map, e->x, e->y, p->x, p->y, 1)) { combat_enemy_attack(e, p); + propagate_alert(e, all_enemies, enemy_count); return; } // Move toward player if visible if (can_see) { enemy_move_toward_player(e, p, map, all_enemies, enemy_count); + propagate_alert(e, all_enemies, enemy_count); return; } - // Player not visible - patrol randomly + // If alert but can't see player, move toward last known position + if (e->alert) { + enemy_move_to_last_known(e, map, all_enemies, enemy_count); + return; + } + + // Not alert - patrol randomly enemy_patrol(e, map, all_enemies, enemy_count); } diff --git a/src/map.c b/src/map.c index a29889b..b9d5bbc 100644 --- a/src/map.c +++ b/src/map.c @@ -12,6 +12,8 @@ void map_init(Map *map) { map->tiles[y][x] = TILE_WALL; } } + memset(map->visible, 0, sizeof(map->visible)); + memset(map->remembered, 0, sizeof(map->remembered)); map->room_count = 0; } From 4dfe52ae729776dfd08408b926a6b33f37c5ec44 Mon Sep 17 00:00:00 2001 From: Squirrel Modeller Date: Thu, 9 Apr 2026 14:11:46 +0000 Subject: [PATCH 03/13] movement: generalize; use vectors (#16) Generalized movement, so that all entities move the same. Reviewed-on: https://git.frzn.dev/NotAShelf/rogged/pulls/16 Reviewed-by: raf Co-authored-by: Squirrel Modeller Co-committed-by: Squirrel Modeller --- build.zig | 1 + src/common.h | 8 ++++-- src/enemy.c | 49 +++++++++++++++-------------------- src/main.c | 69 ++++++++++++++++++++++++++++---------------------- src/movement.c | 29 +++++++++++++++++++++ src/movement.h | 17 +++++++++++++ src/player.c | 31 +++++------------------ src/player.h | 4 +-- src/render.c | 11 ++++---- 9 files changed, 127 insertions(+), 92 deletions(-) create mode 100644 src/movement.c create mode 100644 src/movement.h diff --git a/build.zig b/build.zig index 7109941..4d6f6b2 100644 --- a/build.zig +++ b/build.zig @@ -25,6 +25,7 @@ pub fn build(b: *std.Build) void { "src/main.c", "src/map.c", "src/player.c", + "src/movement.c", "src/render.c", "src/rng.c", "src/settings.c", diff --git a/src/common.h b/src/common.h index 61ab044..52fe274 100644 --- a/src/common.h +++ b/src/common.h @@ -4,6 +4,10 @@ #include "settings.h" #include +typedef struct { + int x, y; +} Vec2; + // Tile types typedef enum { TILE_WALL, TILE_FLOOR, TILE_STAIRS } TileType; @@ -57,7 +61,7 @@ typedef struct { // Player typedef struct { - int x, y; + Vec2 position; int hp, max_hp; int attack; int defense; @@ -83,7 +87,7 @@ typedef enum { ENEMY_GOBLIN, ENEMY_SKELETON, ENEMY_ORC } EnemyType; // Enemy typedef struct { - int x, y; + Vec2 position; int hp; int max_hp; int attack; diff --git a/src/enemy.c b/src/enemy.c index 9f72670..a9d4909 100644 --- a/src/enemy.c +++ b/src/enemy.c @@ -2,6 +2,7 @@ #include "combat.h" #include "common.h" #include "map.h" +#include "movement.h" #include "rng.h" #include @@ -29,7 +30,7 @@ void enemy_spawn(Enemy enemies[], int *count, Map *map, Player *p, int floor) { get_random_floor_tile(map, &ex, &ey, 50); // Don't spawn on player position - if (ex == p->x && ey == p->y) { + if (ex == p->position.x && ey == p->position.y) { continue; } @@ -41,8 +42,8 @@ void enemy_spawn(Enemy enemies[], int *count, Map *map, Player *p, int floor) { // Create enemy Enemy e; memset(&e, 0, sizeof(Enemy)); - e.x = ex; - e.y = ey; + e.position.x = ex; + e.position.y = ey; e.alive = 1; e.type = rng_int(ENEMY_GOBLIN, max_type); e.effect_count = 0; @@ -127,7 +128,7 @@ void enemy_spawn(Enemy enemies[], int *count, Map *map, Player *p, int floor) { // Check if position has an enemy int is_enemy_at(const Enemy *enemies, int count, int x, int y) { for (int i = 0; i < count; i++) { - if (enemies[i].alive && enemies[i].x == x && enemies[i].y == y) { + if (enemies[i].alive && enemies[i].position.x == x && enemies[i].position.y == y) { return 1; } } @@ -136,45 +137,37 @@ int is_enemy_at(const Enemy *enemies, int count, int x, int y) { // Check if enemy can see player (adjacent) static int can_see_player(Enemy *e, Player *p) { - int dx = p->x - e->x; - int dy = p->y - e->y; + int dx = p->position.x - e->position.x; + int dy = p->position.y - e->position.y; return (dx >= -1 && dx <= 1 && dy >= -1 && dy <= 1); } -// Check if position is occupied by player -static int is_player_at(Player *p, int x, int y) { - return (p->x == x && p->y == y); -} // Move enemy toward player static void enemy_move_toward_player(Enemy *e, Player *p, Map *map, Enemy *all_enemies, int enemy_count) { int dx = 0, dy = 0; - if (p->x > e->x) + if (p->position.x > e->position.x) dx = 1; - else if (p->x < e->x) + else if (p->position.x < e->position.x) dx = -1; - if (p->y > e->y) + if (p->position.y > e->position.y) dy = 1; - else if (p->y < e->y) + else if (p->position.y < e->position.y) dy = -1; - // Try horizontal first, then vertical - int new_x = e->x + dx; - int new_y = e->y; + Vec2 dir = {dx, 0}; + if (dx != 0) { + MoveResult r = try_move_entity(&e->position, dir, map, p, all_enemies, enemy_count, false); + if (r == MOVE_RESULT_MOVED) + return; + } - if (dx != 0 && is_floor(map, new_x, new_y) && !is_enemy_at(all_enemies, enemy_count, new_x, new_y) && - !is_player_at(p, new_x, new_y)) { - e->x = new_x; - } else if (dy != 0) { - new_x = e->x; - new_y = e->y + dy; - if (is_floor(map, new_x, new_y) && !is_enemy_at(all_enemies, enemy_count, new_x, new_y) && - !is_player_at(p, new_x, new_y)) { - e->x = new_x; - e->y = new_y; - } + dir.x = 0; + dir.y = dy; + if (dy != 0) { + try_move_entity(&e->position, dir, map, p, all_enemies, enemy_count, false); } } diff --git a/src/main.c b/src/main.c index d450d75..d90487b 100644 --- a/src/main.c +++ b/src/main.c @@ -4,6 +4,7 @@ #include "enemy.h" #include "items.h" #include "map.h" +#include "movement.h" #include "player.h" #include "raylib.h" #include "render.h" @@ -117,8 +118,8 @@ static void init_floor(GameState *gs, int floor_num) { gs->floors_reached = 1; } else { // Move player to new floor position - gs->player.x = start_x; - gs->player.y = start_y; + gs->player.position.x = start_x; + gs->player.position.y = start_y; } gs->player.floor = floor_num; @@ -137,7 +138,8 @@ static void tick_all_effects(GameState *gs) { // Player effects 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); + spawn_floating_text(gs, gs->player.position.x * TILE_SIZE + 8, gs->player.position.y * TILE_SIZE, player_effect_dmg, + 0); gs->screen_shake = SHAKE_EFFECT_DURATION; } @@ -155,7 +157,7 @@ static void tick_all_effects(GameState *gs) { continue; int enemy_effect_dmg = combat_tick_enemy_effects(e); if (enemy_effect_dmg > 0) { - spawn_floating_text(gs, e->x * TILE_SIZE + 8, e->y * TILE_SIZE, enemy_effect_dmg, 0); + spawn_floating_text(gs, e->position.x * TILE_SIZE + 8, e->position.y * TILE_SIZE, enemy_effect_dmg, 0); } if (!e->alive) { add_log(gs, "Enemy died from effects!"); @@ -173,7 +175,7 @@ static void post_action(GameState *gs, Enemy *attacked_enemy) { return; // Check if stepped on stairs - if (gs->map.tiles[gs->player.y][gs->player.x] == TILE_STAIRS) { + if (gs->map.tiles[gs->player.position.y][gs->player.position.x] == TILE_STAIRS) { gs->awaiting_descend = 1; gs->last_message = "Descend to next floor? (Y/N)"; gs->message_timer = 120; @@ -182,8 +184,8 @@ static void post_action(GameState *gs, Enemy *attacked_enemy) { // combat feedback - player attacked an enemy this turn if (attacked_enemy != NULL) { - int ex = attacked_enemy->x * TILE_SIZE + 8; - int ey = attacked_enemy->y * TILE_SIZE; + int ex = attacked_enemy->position.x * TILE_SIZE + 8; + int ey = attacked_enemy->position.y * TILE_SIZE; if (combat_was_dodged()) { spawn_floating_label(gs, ex, ey, LABEL_DODGE, EFFECT_NONE); @@ -224,8 +226,8 @@ static void post_action(GameState *gs, Enemy *attacked_enemy) { gs->screen_shake = SHAKE_PLAYER_DAMAGE_DURATION; 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(), - combat_was_critical()); + spawn_floating_text(gs, gs->player.position.x * TILE_SIZE + 8, gs->player.position.y * TILE_SIZE, + combat_get_last_damage(), combat_was_critical()); } // Set message and check game over @@ -388,7 +390,7 @@ static int handle_movement_input(GameState *gs) { // Check for manual item pickup (G key) if (IsKeyPressed(KEY_G)) { - Item *item = get_item_at_floor(gs->items, gs->item_count, gs->player.x, gs->player.y); + Item *item = get_item_at_floor(gs->items, gs->item_count, gs->player.position.x, gs->player.position.y); if (item != NULL) { if (player_pickup(&gs->player, item)) { gs->items_collected++; @@ -417,38 +419,45 @@ static int handle_movement_input(GameState *gs) { } } - // Movement: use IsKeyDown for held-key repeat - int dx = 0, dy = 0; - if (IsKeyDown(KEY_W) || IsKeyDown(KEY_UP)) - dy = -1; - else if (IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN)) - dy = 1; - else if (IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT)) - dx = -1; - else if (IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT)) - dx = 1; - if (dx == 0 && dy == 0) + Vec2 direction = {0, 0}; + if (IsKeyDown(KEY_W) || IsKeyDown(KEY_UP)) + direction.y = -1; + else if (IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN)) + direction.y = 1; + else if (IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT)) + direction.x = -1; + else if (IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT)) + direction.x = 1; + + if (direction.x == 0 && direction.y == 0) return 0; // Reset combat event before player acts combat_reset_event(); - int new_x = gs->player.x + dx; - int new_y = gs->player.y + dy; + + int new_x = gs->player.position.x + direction.x; + int new_y = gs->player.position.y + direction.y; + + Enemy *target = NULL; int action = 0; - // Attack enemy at target tile, or move into it - Enemy *target = player_find_enemy_at(gs->enemies, gs->enemy_count, new_x, new_y); - if (target != NULL) { - player_attack(&gs->player, target); + MoveResult result = + try_move_entity(&gs->player.position, direction, &gs->map, &gs->player, gs->enemies, gs->enemy_count, true); + if (result == MOVE_RESULT_MOVED) { + player_on_move(&gs->player); action = 1; - } else { - action = player_move(&gs->player, dx, dy, &gs->map); + } else if (result == MOVE_RESULT_BLOCKED_ENEMY) { + target = player_find_enemy_at(gs->enemies, gs->enemy_count, new_x, new_y); + if (target != NULL) { + player_attack(&gs->player, target); + action = 1; + } } if (action) - post_action(gs, target); // target is NULL on move, enemy ptr on attack + post_action(gs, target); return action; } diff --git a/src/movement.c b/src/movement.c new file mode 100644 index 0000000..0f7fd8f --- /dev/null +++ b/src/movement.c @@ -0,0 +1,29 @@ +#include "movement.h" +#include "enemy.h" +#include "map.h" +#include + +// Check if position is occupied by player +static int is_player_at(Player *p, int x, int y) { + return (p->position.x == x && p->position.y == y); +} + +MoveResult try_move_entity(Vec2 *p, Vec2 direction, Map *map, Player *player, Enemy *enemies, int enemy_count, + bool moving_is_player) { + int new_x = p->x + direction.x; + int new_y = p->y + direction.y; + + if (!is_floor(map, new_x, new_y)) + return MOVE_RESULT_BLOCKED_WALL; + + if (is_enemy_at(enemies, enemy_count, new_x, new_y)) + return MOVE_RESULT_BLOCKED_ENEMY; + if (!moving_is_player) { + if (is_player_at(player, new_x, new_y)) + return MOVE_RESULT_BLOCKED_PLAYER; + } + + p->x = new_x; + p->y = new_y; + return MOVE_RESULT_MOVED; +} diff --git a/src/movement.h b/src/movement.h new file mode 100644 index 0000000..e73a9a0 --- /dev/null +++ b/src/movement.h @@ -0,0 +1,17 @@ +#ifndef MOVEMENT_H +#define MOVEMENT_H + +#include "common.h" + +typedef enum { + MOVE_RESULT_MOVED, + MOVE_RESULT_BLOCKED_WALL, + MOVE_RESULT_BLOCKED_PLAYER, + MOVE_RESULT_BLOCKED_ENEMY +} MoveResult; + +// Attempts to move entity in a given direction. Returns outcome of action. +MoveResult try_move_entity(Vec2 *p, Vec2 direction, Map *map, Player *player, Enemy *enemies, int enemy_count, + bool moving_is_player); + +#endif // MOVEMENT_H diff --git a/src/player.c b/src/player.c index 3bb4c4d..ecc9967 100644 --- a/src/player.c +++ b/src/player.c @@ -2,15 +2,12 @@ #include "combat.h" #include "common.h" #include "items.h" -#include "map.h" #include "settings.h" -#include "utils.h" -#include #include void player_init(Player *p, int x, int y) { - p->x = x; - p->y = y; + p->position.x = x; + p->position.y = y; p->hp = PLAYER_BASE_HP; p->max_hp = PLAYER_BASE_HP; p->attack = PLAYER_BASE_ATTACK; @@ -43,36 +40,20 @@ Enemy *player_find_enemy_at(Enemy *enemies, int count, int x, int y) { if (count > MAX_ENEMIES) count = MAX_ENEMIES; for (int i = 0; i < count; i++) { - if (enemies[i].alive && enemies[i].x == x && enemies[i].y == y) + if (enemies[i].alive && enemies[i].position.x == x && enemies[i].position.y == y) return &enemies[i]; } return NULL; } -int player_move(Player *p, int dx, int dy, Map *map) { - int new_x = p->x + dx; - int new_y = p->y + dy; - - // Check bounds - if (!in_bounds(new_x, new_y, MAP_WIDTH, MAP_HEIGHT)) - return 0; - - // Check if walkable - if (!is_floor(map, new_x, new_y)) - return 0; - - // Move player - p->x = new_x; - p->y = new_y; +void player_on_move(Player *p) { p->step_count += 1; - // Regen suppressed while poisoned, bleeding, or burning if (p->step_count % REGEN_STEP_INTERVAL == 0 && p->hp < p->max_hp && !combat_has_effect(p->effects, p->effect_count, EFFECT_POISON) && !combat_has_effect(p->effects, p->effect_count, EFFECT_BLEED) && !combat_has_effect(p->effects, p->effect_count, EFFECT_BURN)) { p->hp += 1; } - return 1; } void player_attack(Player *p, Enemy *e) { @@ -228,8 +209,8 @@ int player_drop_item(Player *p, int inv_index, Item *items, int item_count) { if (items[i].picked_up) { // Place dropped item at this position items[i] = *item; - items[i].x = p->x; - items[i].y = p->y; + items[i].x = p->position.x; + items[i].y = p->position.y; items[i].picked_up = 0; // Remove from inventory player_remove_inventory_item(p, inv_index); diff --git a/src/player.h b/src/player.h index ca91abf..290806a 100644 --- a/src/player.h +++ b/src/player.h @@ -6,8 +6,8 @@ // Initialize player at position void player_init(Player *p, int x, int y); -// Move player to (x+dx, y+dy); returns 1 if moved, 0 if blocked -int player_move(Player *p, int dx, int dy, Map *map); +// Apply status effects, healing, etc +void player_on_move(Player *p); // Find a living enemy at tile (x, y); returns NULL if none Enemy *player_find_enemy_at(Enemy *enemies, int count, int x, int y); diff --git a/src/render.c b/src/render.c index 3c24e15..99267a8 100644 --- a/src/render.c +++ b/src/render.c @@ -30,7 +30,8 @@ void render_map(const Map *map) { } void render_player(const Player *p) { - Rectangle rect = {(float)(p->x * TILE_SIZE), (float)(p->y * TILE_SIZE), (float)TILE_SIZE, (float)TILE_SIZE}; + Rectangle rect = {(float)(p->position.x * TILE_SIZE), (float)(p->position.y * TILE_SIZE), (float)TILE_SIZE, + (float)TILE_SIZE}; DrawRectangleRec(rect, BLUE); } @@ -39,8 +40,8 @@ void render_enemies(const Enemy *enemies, int count) { if (!enemies[i].alive) continue; - Rectangle rect = {(float)(enemies[i].x * TILE_SIZE), (float)(enemies[i].y * TILE_SIZE), (float)TILE_SIZE, - (float)TILE_SIZE}; + Rectangle rect = {(float)(enemies[i].position.x * TILE_SIZE), (float)(enemies[i].position.y * TILE_SIZE), + (float)TILE_SIZE, (float)TILE_SIZE}; // Different colors based on enemy type Color enemy_color; @@ -72,8 +73,8 @@ void render_enemies(const Enemy *enemies, int count) { bar_color = (Color){200, 180, 40, 255}; // yellow else bar_color = (Color){200, 60, 60, 255}; // red - Rectangle hp_bar = {(float)(enemies[i].x * TILE_SIZE), (float)(enemies[i].y * TILE_SIZE - 4), (float)hp_pixels, - 3}; + Rectangle hp_bar = {(float)(enemies[i].position.x * TILE_SIZE), (float)(enemies[i].position.y * TILE_SIZE - 4), + (float)hp_pixels, 3}; DrawRectangleRec(hp_bar, bar_color); } } From f85d28e932ec2b10df98baa7611f32197453e12e Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Thu, 9 Apr 2026 09:35:52 +0300 Subject: [PATCH 04/13] various: implement fog of war; make enemy AI slightly more intelligent Signed-off-by: NotAShelf Change-Id: I3e22dbc5e10690871255980c52a24c226a6a6964 --- src/common.h | 2 ++ src/enemy.c | 47 +++++++++++++++++++++++++++-------- src/main.c | 11 +++++++-- src/map.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/map.h | 6 +++++ src/render.c | 31 ++++++++++++++++++------ src/render.h | 4 +-- src/settings.h | 5 ++++ 8 files changed, 151 insertions(+), 21 deletions(-) diff --git a/src/common.h b/src/common.h index 52fe274..b8dd19f 100644 --- a/src/common.h +++ b/src/common.h @@ -34,6 +34,8 @@ typedef struct { TileType tiles[MAP_HEIGHT][MAP_WIDTH]; Room rooms[MAX_ROOMS]; int room_count; + unsigned char visible[MAP_HEIGHT][MAP_WIDTH]; + unsigned char remembered[MAP_HEIGHT][MAP_WIDTH]; } Map; // Dungeon diff --git a/src/enemy.c b/src/enemy.c index a9d4909..33308c5 100644 --- a/src/enemy.c +++ b/src/enemy.c @@ -4,6 +4,7 @@ #include "map.h" #include "movement.h" #include "rng.h" +#include "settings.h" #include // Forward declaration @@ -135,11 +136,9 @@ int is_enemy_at(const Enemy *enemies, int count, int x, int y) { return 0; } -// Check if enemy can see player (adjacent) -static int can_see_player(Enemy *e, Player *p) { - int dx = p->position.x - e->position.x; - int dy = p->position.y - e->position.y; - return (dx >= -1 && dx <= 1 && dy >= -1 && dy <= 1); +// Check if enemy can see player (within view range and line of sight) +static int can_see_player(Enemy *e, Player *p, Map *map) { + return can_see_entity(map, e->position.x, e->position.y, p->position.x, p->position.y, ENEMY_VIEW_RANGE); } @@ -171,7 +170,27 @@ static void enemy_move_toward_player(Enemy *e, Player *p, Map *map, Enemy *all_e } } -// Perform a single action for an enemy (attack if adjacent, otherwise move) +// Move enemy in a random direction (patrol) +static void enemy_patrol(Enemy *e, Map *map, Enemy *all_enemies, int enemy_count) { + if (rng_int(0, 100) > ENEMY_PATROL_MOVE_CHANCE) + return; + + int dx = rng_int(-1, 1); + int dy = rng_int(-1, 1); + + if (dx == 0 && dy == 0) + return; + + int new_x = e->position.x + dx; + int new_y = e->position.y + dy; + + if (is_floor(map, new_x, new_y) && !is_enemy_at(all_enemies, enemy_count, new_x, new_y)) { + e->position.x = new_x; + e->position.y = new_y; + } +} + +// Perform a single action for an enemy (attack if visible, otherwise patrol) void enemy_act(Enemy *e, Player *p, Map *map, Enemy *all_enemies, int enemy_count) { if (!e->alive) return; @@ -180,14 +199,22 @@ void enemy_act(Enemy *e, Player *p, Map *map, Enemy *all_enemies, int enemy_coun if (combat_has_effect(e->effects, e->effect_count, EFFECT_STUN)) return; - // Check if adjacent to player - attack - if (can_see_player(e, p)) { + int can_see = can_see_player(e, p, map); + + // Attack if adjacent to player + if (can_see && can_see_entity(map, e->position.x, e->position.y, p->position.x, p->position.y, 1)) { combat_enemy_attack(e, p); return; } - // Otherwise, move toward player - enemy_move_toward_player(e, p, map, all_enemies, enemy_count); + // Move toward player if visible + if (can_see) { + enemy_move_toward_player(e, p, map, all_enemies, enemy_count); + return; + } + + // Player not visible - patrol randomly + enemy_patrol(e, map, all_enemies, enemy_count); } void enemy_update_all(Enemy enemies[], int count, Player *p, Map *map) { diff --git a/src/main.c b/src/main.c index d90487b..ed3cb51 100644 --- a/src/main.c +++ b/src/main.c @@ -123,6 +123,9 @@ static void init_floor(GameState *gs, int floor_num) { } gs->player.floor = floor_num; + // Calculate initial visibility + calculate_visibility(&gs->map, gs->player.position.x, gs->player.position.y); + // Spawn enemies enemy_spawn(gs->enemies, &gs->enemy_count, &gs->map, &gs->player, floor_num); @@ -217,6 +220,9 @@ static void post_action(GameState *gs, Enemy *attacked_enemy) { } } + // Update visibility based on player's new position + calculate_visibility(&gs->map, gs->player.position.x, gs->player.position.y); + // Enemy turns - uses speed/cooldown system enemy_update_all(gs->enemies, gs->enemy_count, &gs->player, &gs->map); @@ -248,6 +254,7 @@ static int handle_stun_turn(GameState *gs) { if (gs->game_over) return 1; enemy_update_all(gs->enemies, gs->enemy_count, &gs->player, &gs->map); + calculate_visibility(&gs->map, gs->player.position.x, gs->player.position.y); if (gs->player.hp <= 0) gs->game_over = 1; gs->last_message = "You are stunned!"; @@ -550,8 +557,8 @@ static void game_loop(void) { cam.offset = (Vector2){(float)gs.shake_x, (float)gs.shake_y}; BeginMode2D(cam); render_map(&gs.map); - render_items(gs.items, gs.item_count); - render_enemies(gs.enemies, gs.enemy_count); + render_items(gs.items, gs.item_count, gs.map.visible); + render_enemies(gs.enemies, gs.enemy_count, gs.map.visible); render_player(&gs.player); EndMode2D(); diff --git a/src/map.c b/src/map.c index 24016c9..a29889b 100644 --- a/src/map.c +++ b/src/map.c @@ -1,6 +1,8 @@ #include "map.h" #include "rng.h" +#include "settings.h" #include "utils.h" +#include #include void map_init(Map *map) { @@ -186,3 +188,67 @@ void dungeon_generate(Dungeon *d, Map *map, int floor_num) { d->room_count = map->room_count; memcpy(d->rooms, map->rooms, sizeof(Room) * map->room_count); } + +int is_in_view_range(int x, int y, int view_x, int view_y, int range) { + int dx = x - view_x; + int dy = y - view_y; + return (dx * dx + dy * dy) <= (range * range); +} + +static int trace_line_of_sight(const Map *map, int x1, int y1, int x2, int y2) { + int dx = abs(x2 - x1); + int dy = abs(y2 - y1); + int sx = (x1 < x2) ? 1 : -1; + int sy = (y1 < y2) ? 1 : -1; + int err = dx - dy; + int x = x1; + int y = y1; + + while (1) { + if (!in_bounds(x, y, MAP_WIDTH, MAP_HEIGHT)) + return 0; + + if (x == x2 && y == y2) + return 1; + + if (map->tiles[y][x] == TILE_WALL && !(x == x1 && y == y1)) + return 0; + + int e2 = 2 * err; + if (e2 > -dy) { + err -= dy; + x += sx; + } + if (e2 < dx) { + err += dx; + y += sy; + } + } +} + +int has_line_of_sight(const Map *map, int x1, int y1, int x2, int y2) { + if (!in_bounds(x1, y1, MAP_WIDTH, MAP_HEIGHT) || !in_bounds(x2, y2, MAP_WIDTH, MAP_HEIGHT)) + return 0; + return trace_line_of_sight(map, x1, y1, x2, y2); +} + +int can_see_entity(const Map *map, int from_x, int from_y, int to_x, int to_y, int range) { + if (!is_in_view_range(to_x, to_y, from_x, from_y, range)) + return 0; + return has_line_of_sight(map, from_x, from_y, to_x, to_y); +} + +void calculate_visibility(Map *map, int x, int y) { + memset(map->visible, 0, sizeof(map->visible)); + + for (int ty = 0; ty < MAP_HEIGHT; ty++) { + for (int tx = 0; tx < MAP_WIDTH; tx++) { + if (is_in_view_range(tx, ty, x, y, PLAYER_VIEW_RANGE)) { + if (has_line_of_sight(map, x, y, tx, ty)) { + map->visible[ty][tx] = 1; + map->remembered[ty][tx] = 1; + } + } + } + } +} diff --git a/src/map.h b/src/map.h index 3cf6337..84f1119 100644 --- a/src/map.h +++ b/src/map.h @@ -18,4 +18,10 @@ void map_init(Map *map); // Get a random floor tile position void get_random_floor_tile(Map *map, int *x, int *y, int attempts); +// Visibility / Fog of War +int is_in_view_range(int x, int y, int view_x, int view_y, int range); +int has_line_of_sight(const Map *map, int x1, int y1, int x2, int y2); +void calculate_visibility(Map *map, int x, int y); +int can_see_entity(const Map *map, int from_x, int from_y, int to_x, int to_y, int range); + #endif // MAP_H diff --git a/src/render.c b/src/render.c index 99267a8..430b3e3 100644 --- a/src/render.c +++ b/src/render.c @@ -11,18 +11,31 @@ void render_map(const Map *map) { for (int y = 0; y < MAP_HEIGHT; y++) { for (int x = 0; x < MAP_WIDTH; x++) { Rectangle rect = {(float)(x * TILE_SIZE), (float)(y * TILE_SIZE), (float)TILE_SIZE, (float)TILE_SIZE}; + int visible = map->visible[y][x]; + int remembered = map->remembered[y][x]; + + if (!visible && !remembered) { + DrawRectangleRec(rect, (Color){5, 5, 10, 255}); + continue; + } + + Color wall_color = visible ? DARKGRAY : (Color){25, 25, 30, 255}; + Color floor_color = visible ? BLACK : (Color){15, 15, 20, 255}; + Color stairs_color = visible ? (Color){100, 100, 100, 255} : (Color){40, 40, 45, 255}; switch (map->tiles[y][x]) { case TILE_WALL: - DrawRectangleRec(rect, DARKGRAY); + DrawRectangleRec(rect, wall_color); break; case TILE_FLOOR: - DrawRectangleRec(rect, BLACK); + DrawRectangleRec(rect, floor_color); break; case TILE_STAIRS: - DrawRectangleRec(rect, (Color){100, 100, 100, 255}); - // Draw stairs marker - DrawText(">", x * TILE_SIZE + 4, y * TILE_SIZE + 2, 12, WHITE); + DrawRectangleRec(rect, stairs_color); + if (visible) + DrawText(">", x * TILE_SIZE + 4, y * TILE_SIZE + 2, 12, WHITE); + else + DrawText(">", x * TILE_SIZE + 4, y * TILE_SIZE + 2, 12, (Color){60, 60, 65, 255}); break; } } @@ -35,10 +48,12 @@ void render_player(const Player *p) { DrawRectangleRec(rect, BLUE); } -void render_enemies(const Enemy *enemies, int count) { +void render_enemies(const Enemy *enemies, int count, const unsigned char visible[MAP_HEIGHT][MAP_WIDTH]) { for (int i = 0; i < count; i++) { if (!enemies[i].alive) continue; + if (!visible[enemies[i].position.y][enemies[i].position.x]) + continue; Rectangle rect = {(float)(enemies[i].position.x * TILE_SIZE), (float)(enemies[i].position.y * TILE_SIZE), (float)TILE_SIZE, (float)TILE_SIZE}; @@ -80,10 +95,12 @@ void render_enemies(const Enemy *enemies, int count) { } } -void render_items(const Item *items, int count) { +void render_items(const Item *items, int count, const unsigned char visible[MAP_HEIGHT][MAP_WIDTH]) { for (int i = 0; i < count; i++) { if (items[i].picked_up) continue; + if (!visible[items[i].y][items[i].x]) + continue; Rectangle rect = {(float)(items[i].x * TILE_SIZE), (float)(items[i].y * TILE_SIZE), (float)TILE_SIZE, (float)TILE_SIZE}; diff --git a/src/render.h b/src/render.h index 016291f..d934ffc 100644 --- a/src/render.h +++ b/src/render.h @@ -80,10 +80,10 @@ void render_map(const Map *map); void render_player(const Player *p); // Render all enemies -void render_enemies(const Enemy *enemies, int count); +void render_enemies(const Enemy *enemies, int count, const unsigned char visible[MAP_HEIGHT][MAP_WIDTH]); // Render all items -void render_items(const Item *items, int count); +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); diff --git a/src/settings.h b/src/settings.h index 8b8ac54..1b96056 100644 --- a/src/settings.h +++ b/src/settings.h @@ -63,4 +63,9 @@ // Message timer #define MESSAGE_TIMER_DURATION 60 +// Visibility / Fog of War +#define PLAYER_VIEW_RANGE 8 +#define ENEMY_VIEW_RANGE 6 +#define ENEMY_PATROL_MOVE_CHANCE 30 + #endif // SETTINGS_H From 71343311ebd827066fadafb1d4a7870dc706869d Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Thu, 9 Apr 2026 12:31:52 +0300 Subject: [PATCH 05/13] enemy: add alert memory; vision variance based on type Signed-off-by: NotAShelf Change-Id: I2f5c7cac72c8772e5871b99026d106b46a6a6964 --- src/common.h | 5 +++ src/enemy.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++-- src/map.c | 2 ++ 3 files changed, 90 insertions(+), 3 deletions(-) diff --git a/src/common.h b/src/common.h index b8dd19f..474e133 100644 --- a/src/common.h +++ b/src/common.h @@ -104,6 +104,11 @@ typedef struct { int status_chance; int crit_chance; // crit chance percentage (0-100) int crit_mult; // crit damage multiplier percentage (e.g. 150 = 1.5x) + // vision + int vision_range; + int alert; // 1 = aware of player, searching + int last_known_x; // last position where enemy saw player + int last_known_y; // status effects StatusEffect effects[MAX_EFFECTS]; int effect_count; diff --git a/src/enemy.c b/src/enemy.c index 33308c5..57eff34 100644 --- a/src/enemy.c +++ b/src/enemy.c @@ -70,6 +70,7 @@ void enemy_spawn(Enemy enemies[], int *count, Map *map, Player *p, int floor) { e.resistance[DMG_PIERCE] = 0; e.resistance[DMG_FIRE] = -25; e.resistance[DMG_POISON] = 50; + e.vision_range = 7; break; case ENEMY_SKELETON: e.max_hp = ENEMY_BASE_HP + floor + 2; @@ -87,6 +88,7 @@ void enemy_spawn(Enemy enemies[], int *count, Map *map, Player *p, int floor) { e.resistance[DMG_PIERCE] = 50; e.resistance[DMG_FIRE] = 25; e.resistance[DMG_POISON] = 75; + e.vision_range = 6; break; case ENEMY_ORC: e.max_hp = ENEMY_BASE_HP + floor + 4; @@ -104,6 +106,7 @@ void enemy_spawn(Enemy enemies[], int *count, Map *map, Player *p, int floor) { e.resistance[DMG_PIERCE] = -25; e.resistance[DMG_FIRE] = 0; e.resistance[DMG_POISON] = 0; + e.vision_range = 5; break; default: e.max_hp = ENEMY_BASE_HP; @@ -117,6 +120,7 @@ void enemy_spawn(Enemy enemies[], int *count, Map *map, Player *p, int floor) { e.crit_chance = ENEMY_CRIT_CHANCE; e.crit_mult = ENEMY_CRIT_MULT; memset(e.resistance, 0, sizeof(e.resistance)); + e.vision_range = ENEMY_VIEW_RANGE; break; } e.cooldown = e.speed; @@ -138,7 +142,7 @@ int is_enemy_at(const Enemy *enemies, int count, int x, int y) { // Check if enemy can see player (within view range and line of sight) static int can_see_player(Enemy *e, Player *p, Map *map) { - return can_see_entity(map, e->position.x, e->position.y, p->position.x, p->position.y, ENEMY_VIEW_RANGE); + return can_see_entity(map, e->position.x, e->position.y, p->position.x, p->position.y, e->vision_range); } @@ -190,7 +194,68 @@ static void enemy_patrol(Enemy *e, Map *map, Enemy *all_enemies, int enemy_count } } -// Perform a single action for an enemy (attack if visible, otherwise patrol) +// Move enemy toward last known player position +static void enemy_move_to_last_known(Enemy *e, Map *map, Enemy *all_enemies, int enemy_count) { + int dx = 0, dy = 0; + + if (e->last_known_x > e->position.x) + dx = 1; + else if (e->last_known_x < e->position.x) + dx = -1; + + if (e->last_known_y > e->position.y) + dy = 1; + else if (e->last_known_y < e->position.y) + dy = -1; + + int new_x = e->position.x + dx; + int new_y = e->position.y; + + if (dx != 0 && is_floor(map, new_x, new_y) && !is_enemy_at(all_enemies, enemy_count, new_x, new_y)) { + e->position.x = new_x; + } else if (dy != 0) { + new_x = e->position.x; + new_y = e->position.y + dy; + if (is_floor(map, new_x, new_y) && !is_enemy_at(all_enemies, enemy_count, new_x, new_y)) { + e->position.x = new_x; + e->position.y = new_y; + } + } + + if (e->position.x == e->last_known_x && e->position.y == e->last_known_y) + e->alert = 0; +} + +// Check if position is within alert radius of another enemy +static int is_nearby_enemy(const Enemy *enemies, int count, int x, int y, int radius) { + for (int i = 0; i < count; i++) { + if (!enemies[i].alive) + continue; + int dx = enemies[i].position.x - x; + int dy = enemies[i].position.y - y; + if (dx * dx + dy * dy <= radius * radius) + return 1; + } + return 0; +} + +// Propagate alert to nearby enemies +static void propagate_alert(Enemy *trigger_enemy, Enemy *all_enemies, int enemy_count) { + for (int i = 0; i < enemy_count; i++) { + Enemy *e = &all_enemies[i]; + if (!e->alive || e == trigger_enemy) + continue; + if (e->alert) + continue; + if (is_nearby_enemy(all_enemies, enemy_count, e->position.x, e->position.y, 5)) { + e->alert = 1; + e->last_known_x = trigger_enemy->last_known_x; + e->last_known_y = trigger_enemy->last_known_y; + } + } +} + +// Perform a single action for an enemy (attack if visible, otherwise patrol or search) void enemy_act(Enemy *e, Player *p, Map *map, Enemy *all_enemies, int enemy_count) { if (!e->alive) return; @@ -201,19 +266,34 @@ void enemy_act(Enemy *e, Player *p, Map *map, Enemy *all_enemies, int enemy_coun int can_see = can_see_player(e, p, map); + // If we can see the player, update alert state and last known position + if (can_see) { + e->alert = 1; + e->last_known_x = p->position.x; + e->last_known_y = p->position.y; + } + // Attack if adjacent to player if (can_see && can_see_entity(map, e->position.x, e->position.y, p->position.x, p->position.y, 1)) { combat_enemy_attack(e, p); + propagate_alert(e, all_enemies, enemy_count); return; } // Move toward player if visible if (can_see) { enemy_move_toward_player(e, p, map, all_enemies, enemy_count); + propagate_alert(e, all_enemies, enemy_count); return; } - // Player not visible - patrol randomly + // If alert but can't see player, move toward last known position + if (e->alert) { + enemy_move_to_last_known(e, map, all_enemies, enemy_count); + return; + } + + // Not alert - patrol randomly enemy_patrol(e, map, all_enemies, enemy_count); } diff --git a/src/map.c b/src/map.c index a29889b..b9d5bbc 100644 --- a/src/map.c +++ b/src/map.c @@ -12,6 +12,8 @@ void map_init(Map *map) { map->tiles[y][x] = TILE_WALL; } } + memset(map->visible, 0, sizeof(map->visible)); + memset(map->remembered, 0, sizeof(map->remembered)); map->room_count = 0; } From f51b754e7634d6cee4602b3d84555deaecfde9be Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Fri, 10 Apr 2026 10:43:03 +0300 Subject: [PATCH 06/13] map: implement seeded generation; allow passing custom seed Signed-off-by: NotAShelf Change-Id: I742b7e59c7ca872539d4ebfe3a03b44f6a6a6964 --- src/common.h | 2 ++ src/main.c | 35 ++++++++++++++++++++++++++++++----- src/map.c | 3 --- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/common.h b/src/common.h index 474e133..9fda4d3 100644 --- a/src/common.h +++ b/src/common.h @@ -173,6 +173,8 @@ typedef struct { int potions_used; int floors_reached; int final_score; + // Seed for this run + unsigned int run_seed; } GameState; diff --git a/src/main.c b/src/main.c index ed3cb51..ea90e6c 100644 --- a/src/main.c +++ b/src/main.c @@ -12,7 +12,9 @@ #include "settings.h" #include #include +#include #include +#include // Add message to action log static void add_log(GameState *gs, const char *msg) { @@ -102,11 +104,14 @@ 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(floor_num * 54321); + rng_seed(gs->run_seed + floor_num * 98765); // Find spawn position int start_x, start_y; @@ -477,7 +482,9 @@ static int handle_input(GameState *gs) { // Check for restart (works during game over) if (IsKeyPressed(KEY_R) && gs->game_over) { + unsigned int saved_seed = gs->run_seed; memset(gs, 0, sizeof(GameState)); + gs->run_seed = saved_seed; init_floor(gs, 1); return 0; } @@ -508,12 +515,12 @@ void load_audio_assets(GameState *gs) { } // Main game loop -static void game_loop(void) { +static void game_loop(unsigned int run_seed) { GameState gs; memset(&gs, 0, sizeof(GameState)); + gs.run_seed = run_seed; load_audio_assets(&gs); // Initialize first floor - rng_seed(12345); init_floor(&gs, 1); // Disable esc to exit @@ -532,7 +539,9 @@ static void game_loop(void) { if (IsKeyPressed(KEY_Q)) break; if (IsKeyPressed(KEY_R)) { + unsigned int saved_seed = gs.run_seed; memset(&gs, 0, sizeof(GameState)); + gs.run_seed = saved_seed; gs.game_over = 0; gs.game_won = 0; load_audio_assets(&gs); @@ -599,7 +608,23 @@ static void game_loop(void) { } } -int main(void) { +int main(int argc, char **argv) { + // Parse command-line arguments + unsigned int run_seed = 0; + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "--seed") == 0 && i + 1 < argc) { + run_seed = (unsigned int)atoi(argv[i + 1]); + i++; // Skip the value + } + } + + // If no seed provided, generate random seed from time + if (run_seed == 0) { + run_seed = (unsigned int)time(NULL); + } + + printf("Starting game with seed: %u\n", run_seed); + // Initialize audio audio_init(); // Initialize random number generator @@ -609,7 +634,7 @@ int main(void) { SetTargetFPS(60); // Run game - game_loop(); + game_loop(run_seed); // Cleanup CloseWindow(); diff --git a/src/map.c b/src/map.c index b9d5bbc..011c06b 100644 --- a/src/map.c +++ b/src/map.c @@ -170,9 +170,6 @@ 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); From 8bbca55b78946c2aecb86a4847caad20faa168f3 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Fri, 10 Apr 2026 11:22:17 +0300 Subject: [PATCH 07/13] rogged: re-seed on game-over; display seed in game-end screen Signed-off-by: NotAShelf Change-Id: I0178392036902a87b64fde63f5a5f56a6a6a6964 --- src/main.c | 10 +++++----- src/render.c | 11 +++++++++-- src/render.h | 3 ++- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/main.c b/src/main.c index ea90e6c..c7332a8 100644 --- a/src/main.c +++ b/src/main.c @@ -482,9 +482,9 @@ static int handle_input(GameState *gs) { // Check for restart (works during game over) if (IsKeyPressed(KEY_R) && gs->game_over) { - unsigned int saved_seed = gs->run_seed; memset(gs, 0, sizeof(GameState)); - gs->run_seed = saved_seed; + // Generate a new random seed for the new run + gs->run_seed = (unsigned int)time(NULL); init_floor(gs, 1); return 0; } @@ -539,9 +539,9 @@ static void game_loop(unsigned int run_seed) { if (IsKeyPressed(KEY_Q)) break; if (IsKeyPressed(KEY_R)) { - unsigned int saved_seed = gs.run_seed; memset(&gs, 0, sizeof(GameState)); - gs.run_seed = saved_seed; + // 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); @@ -598,7 +598,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.final_score, gs.run_seed); } EndDrawing(); diff --git a/src/render.c b/src/render.c index 430b3e3..2badee3 100644 --- a/src/render.c +++ b/src/render.c @@ -507,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) { + int times_hit, int potions, int floors, int turns, int score, unsigned int seed) { // Semi-transparent overlay Rectangle overlay = {0, 0, (float)SCREEN_WIDTH, (float)SCREEN_HEIGHT}; DrawRectangleRec(overlay, (Color){0, 0, 0, 210}); @@ -523,7 +523,7 @@ void render_end_screen(int is_victory, int kills, int items, int damage_dealt, i int box_x = SCREEN_WIDTH / 2 - 200; int box_y = 110; int box_w = 400; - int box_h = 320; + int box_h = 350; 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}); @@ -589,7 +589,14 @@ void render_end_screen(int is_victory, int kills, int items, int damage_dealt, i DrawText("SCORE:", col1_x, row_y, 22, GOLD); snprintf(line, sizeof(line), "%d", score); DrawText(line, col1_x + 90, row_y, 22, GOLD); + row_y += 35; + // Seed display + DrawText("SEED:", col1_x, row_y, 18, label_color); + snprintf(line, sizeof(line), "%u", seed); + DrawText(line, col1_x + 60, row_y, 18, END_SEED); + + // Instructions if (is_victory) { const char *subtitle = "Press R to play again or Q to quit"; int sub_width = MeasureText(subtitle, 20); diff --git a/src/render.h b/src/render.h index d934ffc..c571f19 100644 --- a/src/render.h +++ b/src/render.h @@ -68,6 +68,7 @@ #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 @@ -99,7 +100,7 @@ void render_floating_texts(FloatingText *texts, int count, int shake_x, int shak // 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); + int times_hit, int potions, int floors, int turns, int score, unsigned int seed); // Render a message popup void render_message(const char *message); From 4475e6c2762e968e0199db46981cc43dac42e311 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Fri, 10 Apr 2026 13:53:05 +0300 Subject: [PATCH 08/13] various: persist seed display; fix seed 0 handling Signed-off-by: NotAShelf Change-Id: I349ed28a792d4de685f8468eddd33a136a6a6964 --- src/main.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++----- src/render.c | 15 ++++++++++++++ src/render.h | 4 ++++ 3 files changed, 70 insertions(+), 5 deletions(-) diff --git a/src/main.c b/src/main.c index c7332a8..6bd5351 100644 --- a/src/main.c +++ b/src/main.c @@ -10,6 +10,7 @@ #include "render.h" #include "rng.h" #include "settings.h" +#include #include #include #include @@ -486,6 +487,10 @@ static int handle_input(GameState *gs) { // 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; } @@ -546,6 +551,10 @@ static void game_loop(unsigned int run_seed) { 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); } } @@ -588,6 +597,9 @@ static void game_loop(unsigned int run_seed) { 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 @@ -608,18 +620,50 @@ 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 && i + 1 < argc) { - run_seed = (unsigned int)atoi(argv[i + 1]); + if (strcmp(argv[i], "--seed") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "Error: --seed requires a value\n"); + fprintf(stderr, "Usage: %s [--seed ]\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 (run_seed == 0) { + if (!seed_provided) { run_seed = (unsigned int)time(NULL); } @@ -629,8 +673,10 @@ int main(int argc, char **argv) { audio_init(); // Initialize random number generator SetRandomSeed(88435); - // Initialize window - InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT + 60, "Roguelike"); + // 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); SetTargetFPS(60); // Run game diff --git a/src/render.c b/src/render.c index 2badee3..8d7bbaa 100644 --- a/src/render.c +++ b/src/render.c @@ -679,3 +679,18 @@ void render_message(const char *message) { DrawText(message, text_x, text_y, font_size, 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); +} diff --git a/src/render.h b/src/render.h index c571f19..aa5c181 100644 --- a/src/render.h +++ b/src/render.h @@ -1,3 +1,4 @@ + #ifndef RENDER_H #define RENDER_H @@ -105,4 +106,7 @@ void render_end_screen(int is_victory, int kills, int items, int damage_dealt, i // Render a message popup void render_message(const char *message); +// Render seed display at top right of screen +void render_seed_display(unsigned int seed); + #endif // RENDER_H From 26aa295f821e21d3c114d9ab978996ee0bb5f348 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Fri, 10 Apr 2026 15:17:39 +0300 Subject: [PATCH 09/13] build: move map & rng logic to their own libraries Signed-off-by: NotAShelf Change-Id: I1802469f3baff4576f61accfb5a197d86a6a6964 --- build.zig | 67 +++++++++++++++++++++++++++++-------- {src => libs/map}/map.c | 2 +- {src => libs/map}/map.h | 0 {src => libs/map}/utils.c | 0 {src => libs/map}/utils.h | 0 {src => libs/rng}/rng.c | 0 {src => libs/rng}/rng.h | 0 src/audio.c | 2 -- src/audio.h | 2 +- src/common.h | 65 +----------------------------------- src/enemy.c | 4 +-- src/game_state.h | 70 +++++++++++++++++++++++++++++++++++++++ src/items.c | 4 +-- src/main.c | 7 ++-- src/movement.c | 2 +- src/render.c | 2 -- src/render.h | 2 +- 17 files changed, 136 insertions(+), 93 deletions(-) rename {src => libs/map}/map.c (99%) rename {src => libs/map}/map.h (100%) rename {src => libs/map}/utils.c (100%) rename {src => libs/map}/utils.h (100%) rename {src => libs/rng}/rng.c (100%) rename {src => libs/rng}/rng.h (100%) create mode 100644 src/game_state.h diff --git a/build.zig b/build.zig index 4d6f6b2..807f0af 100644 --- a/build.zig +++ b/build.zig @@ -4,6 +4,51 @@ 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 const combat_lib = b.addLibrary(.{ .name = "combat", @@ -14,29 +59,20 @@ 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_lib.addIncludePath(b.path("src")); - combat_lib.linkSystemLibrary("raylib"); + combat_lib.addIncludePath(b.path("libs/rng")); - // C sources (everything except combat, which is now Zig) + // C sources remaining in src/ const c_sources = [_][]const u8{ "src/audio.c", "src/enemy.c", "src/items.c", "src/main.c", - "src/map.c", - "src/player.c", "src/movement.c", + "src/player.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 @@ -54,7 +90,12 @@ 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.linkLibrary(combat_lib); exe.linkSystemLibrary("raylib"); exe.linkSystemLibrary("m"); diff --git a/src/map.c b/libs/map/map.c similarity index 99% rename from src/map.c rename to libs/map/map.c index 011c06b..911d487 100644 --- a/src/map.c +++ b/libs/map/map.c @@ -1,5 +1,5 @@ #include "map.h" -#include "rng.h" +#include "rng/rng.h" #include "settings.h" #include "utils.h" #include diff --git a/src/map.h b/libs/map/map.h similarity index 100% rename from src/map.h rename to libs/map/map.h diff --git a/src/utils.c b/libs/map/utils.c similarity index 100% rename from src/utils.c rename to libs/map/utils.c diff --git a/src/utils.h b/libs/map/utils.h similarity index 100% rename from src/utils.h rename to libs/map/utils.h diff --git a/src/rng.c b/libs/rng/rng.c similarity index 100% rename from src/rng.c rename to libs/rng/rng.c diff --git a/src/rng.h b/libs/rng/rng.h similarity index 100% rename from src/rng.h rename to libs/rng/rng.h diff --git a/src/audio.c b/src/audio.c index 36db553..93114c2 100644 --- a/src/audio.c +++ b/src/audio.c @@ -1,6 +1,4 @@ #include "audio.h" -#include "raylib.h" -#include "common.h" #include #include diff --git a/src/audio.h b/src/audio.h index 56ecd92..ff71fa6 100644 --- a/src/audio.h +++ b/src/audio.h @@ -1,6 +1,6 @@ #ifndef AUDIO_H #define AUDIO_H -#include "common.h" +#include "game_state.h" // Initialize audio system void audio_init(void); diff --git a/src/common.h b/src/common.h index 9fda4d3..fe87edf 100644 --- a/src/common.h +++ b/src/common.h @@ -2,7 +2,7 @@ #define COMMON_H #include "settings.h" -#include +#include typedef struct { int x, y; @@ -114,68 +114,5 @@ 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; - // Seed for this run - unsigned int run_seed; -} GameState; - #endif // COMMON_H diff --git a/src/enemy.c b/src/enemy.c index 57eff34..6832386 100644 --- a/src/enemy.c +++ b/src/enemy.c @@ -1,9 +1,9 @@ #include "enemy.h" #include "combat.h" #include "common.h" -#include "map.h" +#include "map/map.h" #include "movement.h" -#include "rng.h" +#include "rng/rng.h" #include "settings.h" #include diff --git a/src/game_state.h b/src/game_state.h new file mode 100644 index 0000000..e0d7711 --- /dev/null +++ b/src/game_state.h @@ -0,0 +1,70 @@ +#ifndef GAME_STATE_H +#define GAME_STATE_H + +#include "common.h" +#include + +// 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 diff --git a/src/items.c b/src/items.c index e5bf212..27f343d 100644 --- a/src/items.c +++ b/src/items.c @@ -1,6 +1,6 @@ #include "common.h" -#include "map.h" -#include "rng.h" +#include "map/map.h" +#include "rng/rng.h" #include "settings.h" #include diff --git a/src/main.c b/src/main.c index 6bd5351..c521eff 100644 --- a/src/main.c +++ b/src/main.c @@ -1,14 +1,13 @@ #include "audio.h" #include "combat.h" -#include "common.h" +#include "game_state.h" #include "enemy.h" #include "items.h" -#include "map.h" +#include "map/map.h" #include "movement.h" #include "player.h" -#include "raylib.h" #include "render.h" -#include "rng.h" +#include "rng/rng.h" #include "settings.h" #include #include diff --git a/src/movement.c b/src/movement.c index 0f7fd8f..89e74e1 100644 --- a/src/movement.c +++ b/src/movement.c @@ -1,6 +1,6 @@ #include "movement.h" #include "enemy.h" -#include "map.h" +#include "map/map.h" #include // Check if position is occupied by player diff --git a/src/render.c b/src/render.c index 8d7bbaa..b0f3a0a 100644 --- a/src/render.c +++ b/src/render.c @@ -1,7 +1,5 @@ #include "render.h" -#include "common.h" #include "items.h" -#include "raylib.h" #include "settings.h" #include #include diff --git a/src/render.h b/src/render.h index aa5c181..a51b019 100644 --- a/src/render.h +++ b/src/render.h @@ -2,7 +2,7 @@ #ifndef RENDER_H #define RENDER_H -#include "common.h" +#include "game_state.h" // HUD colors #define HUD_BG (Color){25, 20, 15, 255} From 3ab42c3f65e889c70024a59cc6dd6d94ab72e616 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Fri, 10 Apr 2026 20:04:22 +0300 Subject: [PATCH 10/13] build: don't archive libcombat Signed-off-by: NotAShelf Change-Id: I39a9ff0e253cf277bf4959a16e05fcff6a6a6964 --- build.zig | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/build.zig b/build.zig index 807f0af..76a85b1 100644 --- a/build.zig +++ b/build.zig @@ -49,8 +49,10 @@ pub fn build(b: *std.Build) void { // utils.h is co-located with map.c map_lib.addIncludePath(b.path("libs/map")); - // Zig combat library - const combat_lib = b.addLibrary(.{ + // 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(.{ .name = "combat", .root_module = b.createModule(.{ .root_source_file = b.path("libs/combat/combat.zig"), @@ -60,8 +62,8 @@ pub fn build(b: *std.Build) void { }), }); // common.h and settings.h live in src/; rng.h exposed bare from libs/rng - combat_lib.addIncludePath(b.path("src")); - combat_lib.addIncludePath(b.path("libs/rng")); + combat_obj.addIncludePath(b.path("src")); + combat_obj.addIncludePath(b.path("libs/rng")); // C sources remaining in src/ const c_sources = [_][]const u8{ @@ -96,7 +98,7 @@ pub fn build(b: *std.Build) void { exe.linkLibrary(rng_lib); exe.linkLibrary(map_lib); - exe.linkLibrary(combat_lib); + exe.addObject(combat_obj); exe.linkSystemLibrary("raylib"); exe.linkSystemLibrary("m"); exe.linkSystemLibrary("pthread"); From 2500fffe84639b6b5c2fbdc5c72cc1c31507c990 Mon Sep 17 00:00:00 2001 From: "A.M. Rowsell" Date: Fri, 10 Apr 2026 08:12:53 -0400 Subject: [PATCH 11/13] font: squash commit into main Squashed commit of the following: commit a53942249c82ae8c17bd2f89271430b16a0f9412 Author: A.M. Rowsell Date: Thu Apr 9 23:59:13 2026 -0400 font: extensive tweaks, looks much better commit 64205e137c8e390f309b59c06f97cbdfd722adb0 Author: A.M. Rowsell Date: Thu Apr 9 12:13:00 2026 -0400 font: fully implemented font changes to UI, size/spacing need tweaking commit 901f063696b37700065cc094f7bc22b040f6f682 Author: A.M. Rowsell Date: Wed Apr 8 09:36:03 2026 -0400 font: tweak sizes of stats commit 20f8c71fdf49a2da357081889c46d011e08d0726 Author: A.M. Rowsell Date: Wed Apr 8 09:28:22 2026 -0400 render: implement experimental font change, needs work --- assets/fonts/Royal_Decree.ttf | Bin 0 -> 599104 bytes assets/fonts/Royal_Decree_Bold.ttf | Bin 0 -> 898656 bytes assets/fonts/Tomorrow_Night.ttf | Bin 0 -> 399403 bytes src/main.c | 17 +++- src/render.c | 141 ++++++++++++++++------------- src/render.h | 10 +- src/settings.h | 15 +++ 7 files changed, 108 insertions(+), 75 deletions(-) create mode 100644 assets/fonts/Royal_Decree.ttf create mode 100644 assets/fonts/Royal_Decree_Bold.ttf create mode 100644 assets/fonts/Tomorrow_Night.ttf diff --git a/assets/fonts/Royal_Decree.ttf b/assets/fonts/Royal_Decree.ttf new file mode 100644 index 0000000000000000000000000000000000000000..56511e4bd4e892fd9b1e23c99f05d1e46b0268b5 GIT binary patch literal 599104 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 @`;d^=XbAIS&iFajgTjoW@$NZ~2 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 literal 0 HcmV?d00001 diff --git a/src/main.c b/src/main.c index c521eff..260222c 100644 --- a/src/main.c +++ b/src/main.c @@ -522,8 +522,15 @@ void load_audio_assets(GameState *gs) { static void game_loop(unsigned int run_seed) { GameState gs; memset(&gs, 0, sizeof(GameState)); +<<<<<<< HEAD gs.run_seed = run_seed; +======= + // load external assets + // sound +>>>>>>> acdbc9c (font: squash commit into main) load_audio_assets(&gs); + // font + Font fontTTF = LoadFontEx("./assets/fonts/Tomorrow_Night.ttf", 24, NULL, 0); // Initialize first floor init_floor(&gs, 1); @@ -581,19 +588,19 @@ 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); + render_ui(&gs.player, &fontTTF); // Draw action log - render_action_log(gs.action_log, gs.log_count, gs.log_head); + render_action_log(gs.action_log, gs.log_count, gs.log_head, &fontTTF); // Draw inventory overlay if active if (gs.show_inventory) { - render_inventory_overlay(&gs.player, gs.inv_selected); + render_inventory_overlay(&gs.player, gs.inv_selected, &fontTTF); } // Draw message if any if (gs.last_message != NULL && gs.message_timer > 0) { - render_message(gs.last_message); + render_message(gs.last_message, &fontTTF); } // Draw persistent seed display in top right @@ -609,7 +616,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); + gs.final_score, gs.run_seed, &fontTTF); } EndDrawing(); diff --git a/src/render.c b/src/render.c index b0f3a0a..5a4c434 100644 --- a/src/render.c +++ b/src/render.c @@ -124,7 +124,7 @@ void render_items(const Item *items, int count, const unsigned char visible[MAP_ } } -void render_ui(const Player *p) { +void render_ui(const Player *p, Font *font) { // HUD Panel const int hud_y = MAP_HEIGHT * TILE_SIZE; const int hud_height = 60; @@ -168,7 +168,7 @@ void render_ui(const Player *p) { int bar_height = 16; // HP Label, above bar - DrawText("HP", bar_x, bar_y - 11, 9, text_dim); + DrawTextEx(*font, "HP", (Vector2){bar_x, bar_y - 17}, BIG_FONT, NAR_CHAR_SPACE, text_dim); // HP Bar background DrawRectangle(bar_x, bar_y, bar_width, bar_height, (Color){20, 15, 15, 255}); @@ -193,8 +193,9 @@ void render_ui(const Player *p) { // 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, 10); - DrawText(hp_text, bar_x + (bar_width - hp_text_w) / 2, bar_y + 2, 10, WHITE); + 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); // Status effects int effect_x = bar_x; @@ -229,7 +230,7 @@ void render_ui(const Player *p) { if (p->effects[i].duration > 0) { char eff_text[16]; snprintf(eff_text, sizeof(eff_text), "%s%d", eff_label, p->effects[i].duration); - DrawText(eff_text, effect_x, effect_y, 9, eff_color); + DrawTextEx(*font, eff_text, (Vector2){effect_x, effect_y}, SMALL_FONT, NAR_CHAR_SPACE, eff_color); effect_x += 28; } } @@ -241,65 +242,68 @@ void render_ui(const Player *p) { // Floor char floor_text[16]; snprintf(floor_text, sizeof(floor_text), "F%d", p->floor); - DrawText(floor_text, stats_x, stats_y, 14, text_bright); - DrawText("Floor", stats_x, stats_y + 16, 9, text_dim); + 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); // ATK char atk_text[16]; snprintf(atk_text, sizeof(atk_text), "%d", p->attack); - DrawText(atk_text, stats_x + stat_spacing, stats_y, 14, YELLOW); - DrawText("ATK", stats_x + stat_spacing, stats_y + 16, 9, text_dim); + 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); // DEF char def_text[16]; snprintf(def_text, sizeof(def_text), "%d", p->defense); - 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); + 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); int equip_x = section2_end + 15; int equip_y = hud_y + 8; // Weapon slot - DrawText("WEAPON", equip_x, equip_y, 9, text_dim); + DrawTextEx(*font, "WEAPON", (Vector2){equip_x, equip_y}, MEDIUM_FONT, NAR_CHAR_SPACE, 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)); - DrawText(weapon_text, equip_x, equip_y + 11, 10, (Color){255, 220, 100, 255}); + DrawTextEx(*font, weapon_text, (Vector2){equip_x, equip_y + 11}, 10, NAR_CHAR_SPACE, (Color){255, 220, 100, 255}); } } else { - DrawText("None [IMP]", equip_x, equip_y + 11, 10, (Color){80, 75, 70, 255}); + DrawTextEx(*font, "None [IMP]", (Vector2){equip_x, equip_y + 11}, 10, NAR_CHAR_SPACE, (Color){80, 75, 70, 255}); } // Armor slot - DrawText("ARMOR", equip_x, equip_y + 26, 9, text_dim); + DrawTextEx(*font, "ARMOR", (Vector2){equip_x, equip_y + 26}, MEDIUM_FONT, NAR_CHAR_SPACE, 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); - DrawText(armor_text, equip_x, equip_y + 37, 10, (Color){100, 150, 255, 255}); + DrawTextEx(*font, armor_text, (Vector2){equip_x, equip_y + 37}, 10, NAR_CHAR_SPACE, (Color){100, 150, 255, 255}); } } else { - DrawText("None", equip_x, equip_y + 37, 10, (Color){80, 75, 70, 255}); + DrawTextEx(*font, "None", (Vector2){equip_x, equip_y + 37}, 10, NAR_CHAR_SPACE, (Color){80, 75, 70, 255}); } int ctrl_x = section3_end + 20; int ctrl_y = hud_y + 14; - 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}); + 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}); // 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); - DrawText(inv_text, SCREEN_WIDTH - inv_width - 10, hud_y + 5, 10, GREEN); + DrawTextEx(*font, inv_text, (Vector2){SCREEN_WIDTH - inv_width - 10, hud_y + 5}, 10, NAR_CHAR_SPACE, GREEN); } -void render_action_log(const char log[5][128], int count, int head) { +void render_action_log(const char log[5][128], int count, int head, Font *font) { // Roguelike scroll/log panel styling const int log_width = 250; const int log_height = 90; @@ -321,7 +325,8 @@ void render_action_log(const char log[5][128], int count, int head) { // Title bar DrawRectangle(log_x + 4, log_y + 4, log_width - 8, 16, (Color){30, 25, 20, 255}); - DrawText("MESSAGE LOG", log_x + 8, log_y + 6, 10, (Color){180, 160, 130, 255}); + DrawTextEx(*font, "MESSAGE LOG", (Vector2){log_x + 8, log_y + 6}, MEDIUM_FONT, NAR_CHAR_SPACE, + (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); @@ -346,15 +351,15 @@ void render_action_log(const char log[5][128], int count, int head) { } else { text_color = (Color){120, 110, 100, 200}; // oldest: dim } - DrawText(log[idx], text_x, text_start_y + i * line_height, 10, text_color); + DrawTextEx(*font, log[idx], (Vector2){text_x, text_start_y + i * line_height}, 10, NAR_CHAR_SPACE, text_color); } } } -void render_inventory_overlay(const Player *p, int selected) { +void render_inventory_overlay(const Player *p, int selected, Font *font) { // Overlay dimensions int ov_width = 360; - int ov_height = 300; + int ov_height = 320; 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}); @@ -363,12 +368,14 @@ void render_inventory_overlay(const Player *p, int selected) { // Title const char *title = "INVENTORY"; int title_w = MeasureText(title, 24); - DrawText(title, overlay.x + (overlay.width - title_w) / 2, overlay.y + 12, 24, WHITE); + 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); // Draw each inventory slot char slot_text[64]; int row_height = 26; - int start_y = overlay.y + 50; + int start_y = overlay.y + 40; for (int i = 0; i < MAX_INVENTORY; i++) { int y_pos = start_y + (i * row_height); @@ -385,7 +392,8 @@ void render_inventory_overlay(const Player *p, int selected) { // Slot number snprintf(slot_text, sizeof(slot_text), "%d.", i + 1); - DrawText(slot_text, overlay.x + 16, y_pos + 4, 14, (Color){80, 80, 80, 255}); + DrawTextEx(*font, slot_text, (Vector2){overlay.x + 16, y_pos + 4}, MEDIUM_FONT, SMALL_CHAR_SPACE, + (Color){80, 80, 80, 255}); // Item name const char *name = item_get_name(item); @@ -393,31 +401,31 @@ void render_inventory_overlay(const Player *p, int selected) { 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); + DrawTextEx(*font, name, (Vector2){overlay.x + 45, y_pos + 4}, 14, NORM_CHAR_SPACE, name_color); } - // Power snprintf(slot_text, sizeof(slot_text), "+%d", item->power); - DrawText(slot_text, overlay.x + 150, y_pos + 4, 14, YELLOW); + DrawTextEx(*font, slot_text, (Vector2){overlay.x + 150, y_pos + 4}, 14, NORM_CHAR_SPACE, YELLOW); // Action if (item->type == ITEM_POTION) { - DrawText("[U]se", overlay.x + 200, y_pos + 4, 14, GREEN); + DrawTextEx(*font, "[U]se", (Vector2){overlay.x + 200, y_pos + 4}, 14, NORM_CHAR_SPACE, GREEN); } else { - DrawText("[E]quip [D]rop", overlay.x + 200, y_pos + 4, 14, GOLD); + DrawTextEx(*font, "[E]quip [D]rop", (Vector2){overlay.x + 200, y_pos + 4}, 14, NORM_CHAR_SPACE, GOLD); } } else { // Empty slot snprintf(slot_text, sizeof(slot_text), "%d.", i + 1); - DrawText(slot_text, overlay.x + 16, y_pos + 4, 14, (Color){40, 40, 40, 255}); + DrawTextEx(*font, slot_text, (Vector2){overlay.x + 16, y_pos + 4}, MEDIUM_FONT, SMALL_CHAR_SPACE, + (Color){40, 40, 40, 255}); } } // Instructions at bottom const char *hint = "[1-0] Select [E] Equip [U] Use [D] Drop [I/ESC] Close"; - 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}); + Vector2 hint_w = MeasureTextEx(*font, hint, NORM_FONT, NAR_CHAR_SPACE); + DrawTextEx(*font, hint, (Vector2){overlay.x + (overlay.width - hint_w.x) / 2, overlay.y + overlay.height - 22}, + NORM_FONT, NAR_CHAR_SPACE, (Color){80, 80, 80, 255}); } static Color label_color(FloatingText *ft, int alpha) { @@ -505,7 +513,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) { + int times_hit, int potions, int floors, int turns, int score, unsigned int seed, Font *font) { // Semi-transparent overlay Rectangle overlay = {0, 0, (float)SCREEN_WIDTH, (float)SCREEN_HEIGHT}; DrawRectangleRec(overlay, (Color){0, 0, 0, 210}); @@ -515,7 +523,8 @@ 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); - DrawText(title, (SCREEN_WIDTH - title_width) / 2, 30, title_font_size, title_color); + DrawTextEx(*font, title, (Vector2){(SCREEN_WIDTH - title_width) / 2.0f, 30}, title_font_size, NORM_CHAR_SPACE, + title_color); // Stats box int box_x = SCREEN_WIDTH / 2 - 200; @@ -535,78 +544,80 @@ void render_end_screen(int is_victory, int kills, int items, int damage_dealt, i Color value_color = WHITE; // Column 1 - DrawText("Kills:", col1_x, row_y, 18, label_color); + DrawTextEx(*font, "Kills:", (Vector2){col1_x, row_y}, 18, NORM_CHAR_SPACE, label_color); snprintf(line, sizeof(line), "%d", kills); - DrawText(line, col1_x + 80, row_y, 18, value_color); + DrawTextEx(*font, line, (Vector2){col1_x + 80, row_y}, 18, NORM_CHAR_SPACE, value_color); row_y += line_height; - DrawText("Items:", col1_x, row_y, 18, label_color); + DrawTextEx(*font, "Items:", (Vector2){col1_x, row_y}, 18, NORM_CHAR_SPACE, label_color); snprintf(line, sizeof(line), "%d", items); - DrawText(line, col1_x + 80, row_y, 18, value_color); + DrawTextEx(*font, line, (Vector2){col1_x + 80, row_y}, 18, NORM_CHAR_SPACE, value_color); row_y += line_height; - DrawText("Damage Dealt:", col1_x, row_y, 18, label_color); + DrawTextEx(*font, "Damage Dealt:", (Vector2){col1_x, row_y}, 18, NORM_CHAR_SPACE, label_color); snprintf(line, sizeof(line), "%d", damage_dealt); - DrawText(line, col1_x + 140, row_y, 18, value_color); + DrawTextEx(*font, line, (Vector2){col1_x + 140, row_y}, 18, NORM_CHAR_SPACE, value_color); row_y += line_height; - DrawText("Damage Taken:", col1_x, row_y, 18, label_color); + DrawTextEx(*font, "Damage Taken:", (Vector2){col1_x, row_y}, 18, NORM_CHAR_SPACE, label_color); snprintf(line, sizeof(line), "%d", damage_taken); - DrawText(line, col1_x + 140, row_y, 18, value_color); + DrawTextEx(*font, line, (Vector2){col1_x + 140, row_y}, 18, NORM_CHAR_SPACE, value_color); row_y += line_height; - DrawText("Crits:", col1_x, row_y, 18, label_color); + DrawTextEx(*font, "Crits:", (Vector2){col1_x, row_y}, 18, NORM_CHAR_SPACE, label_color); snprintf(line, sizeof(line), "%d", crits); - DrawText(line, col1_x + 80, row_y, 18, value_color); + DrawTextEx(*font, line, (Vector2){col1_x + 80, row_y}, 18, NORM_CHAR_SPACE, value_color); row_y += line_height; - DrawText("Times Hit:", col1_x, row_y, 18, label_color); + DrawTextEx(*font, "Times Hit:", (Vector2){col1_x, row_y}, 18, NORM_CHAR_SPACE, label_color); snprintf(line, sizeof(line), "%d", times_hit); - DrawText(line, col1_x + 80, row_y, 18, value_color); + DrawTextEx(*font, line, (Vector2){col1_x + 80, row_y}, 18, NORM_CHAR_SPACE, value_color); row_y += line_height; // Column 2 int col2_row_y = box_y + 20; - DrawText("Potions:", col2_x, col2_row_y, 18, label_color); + DrawTextEx(*font, "Potions:", (Vector2){col2_x, col2_row_y}, 18, NORM_CHAR_SPACE, label_color); snprintf(line, sizeof(line), "%d", potions); - DrawText(line, col2_x + 80, col2_row_y, 18, value_color); + DrawTextEx(*font, line, (Vector2){col2_x + 80, col2_row_y}, 18, NORM_CHAR_SPACE, value_color); col2_row_y += line_height; - DrawText("Floors:", col2_x, col2_row_y, 18, label_color); + DrawTextEx(*font, "Floors:", (Vector2){col2_x, col2_row_y}, 18, NORM_CHAR_SPACE, label_color); snprintf(line, sizeof(line), "%d", floors); - DrawText(line, col2_x + 80, col2_row_y, 18, value_color); + DrawTextEx(*font, line, (Vector2){col2_x + 80, col2_row_y}, 18, NORM_CHAR_SPACE, value_color); col2_row_y += line_height; - DrawText("Turns:", col2_x, col2_row_y, 18, label_color); + DrawTextEx(*font, "Turns:", (Vector2){col2_x, col2_row_y}, 18, NORM_CHAR_SPACE, label_color); snprintf(line, sizeof(line), "%d", turns); - DrawText(line, col2_x + 80, col2_row_y, 18, value_color); + DrawTextEx(*font, line, (Vector2){col2_x + 80, col2_row_y}, 18, NORM_CHAR_SPACE, 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; - DrawText("SCORE:", col1_x, row_y, 22, GOLD); + DrawTextEx(*font, "SCORE:", (Vector2){col1_x, row_y}, 22, NORM_CHAR_SPACE, GOLD); snprintf(line, sizeof(line), "%d", score); - DrawText(line, col1_x + 90, row_y, 22, GOLD); + DrawTextEx(*font, line, (Vector2){col1_x + 90, col2_row_y}, 22, NORM_CHAR_SPACE, GOLD); row_y += 35; // Seed display - DrawText("SEED:", col1_x, row_y, 18, label_color); + DrawTextEx(*font, "SEED:", (Vector2){col1_x, row_y}, 18, SMALL_CHAR_SPACE, label_color); snprintf(line, sizeof(line), "%u", seed); - DrawText(line, col1_x + 60, row_y, 18, END_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); - DrawText(subtitle, (SCREEN_WIDTH - sub_width) / 2, SCREEN_HEIGHT - 50, 20, LIGHTGRAY); + DrawTextEx(*font, subtitle, (Vector2){(SCREEN_WIDTH - sub_width) / 2.0f, SCREEN_HEIGHT - 50}, 20, NORM_CHAR_SPACE, + LIGHTGRAY); } else { const char *subtitle = "Press R to restart or Q to quit"; int sub_width = MeasureText(subtitle, 20); - DrawText(subtitle, (SCREEN_WIDTH - sub_width) / 2, SCREEN_HEIGHT - 50, 20, LIGHTGRAY); + DrawTextEx(*font, subtitle, (Vector2){(SCREEN_WIDTH - sub_width) / 2.0f, SCREEN_HEIGHT - 50}, 20, NORM_CHAR_SPACE, + LIGHTGRAY); } } -void render_message(const char *message) { +void render_message(const char *message, Font *font) { if (message == NULL) return; @@ -675,7 +686,7 @@ void render_message(const char *message) { text_y = (int)box_y + padding_y; } - DrawText(message, text_x, text_y, font_size, WHITE); + DrawTextEx(*font, message, (Vector2){text_x, text_y}, font_size, NORM_CHAR_SPACE, WHITE); } void render_seed_display(unsigned int seed) { diff --git a/src/render.h b/src/render.h index a51b019..3dd376a 100644 --- a/src/render.h +++ b/src/render.h @@ -88,23 +88,23 @@ 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); +void render_ui(const Player *p, Font *font); // Render action log (bottom left corner) -void render_action_log(const char log[5][128], int count, int head); +void render_action_log(const char log[5][128], int count, int head, Font *font); // Render inventory selection overlay -void render_inventory_overlay(const Player *p, int selected); +void render_inventory_overlay(const Player *p, int selected, Font *font); // 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); + int times_hit, int potions, int floors, int turns, int score, unsigned int seed, Font *font); // Render a message popup -void render_message(const char *message); +void render_message(const char *message, Font *font); // Render seed display at top right of screen void render_seed_display(unsigned int seed); diff --git a/src/settings.h b/src/settings.h index 1b96056..3408a13 100644 --- a/src/settings.h +++ b/src/settings.h @@ -8,6 +8,21 @@ #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 From 23e98772adf216dac4e7fbed7894741253f666ea Mon Sep 17 00:00:00 2001 From: "A.M. Rowsell" Date: Fri, 10 Apr 2026 08:35:45 -0400 Subject: [PATCH 12/13] font: pick a generic temporary font, fix leftover conflict --- assets/fonts/spartan_500.ttf | Bin 0 -> 38320 bytes src/main.c | 5 +---- 2 files changed, 1 insertion(+), 4 deletions(-) create mode 100644 assets/fonts/spartan_500.ttf diff --git a/assets/fonts/spartan_500.ttf b/assets/fonts/spartan_500.ttf new file mode 100644 index 0000000000000000000000000000000000000000..94b22ecc08c4b22217a3432aaead02a55cc7a4fe GIT binary patch literal 38320 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 OnaXf!gSYIn-Twf#5&=j6 literal 0 HcmV?d00001 diff --git a/src/main.c b/src/main.c index 260222c..404eaa9 100644 --- a/src/main.c +++ b/src/main.c @@ -522,15 +522,12 @@ void load_audio_assets(GameState *gs) { static void game_loop(unsigned int run_seed) { GameState gs; memset(&gs, 0, sizeof(GameState)); -<<<<<<< HEAD gs.run_seed = run_seed; -======= // load external assets // sound ->>>>>>> acdbc9c (font: squash commit into main) load_audio_assets(&gs); // font - Font fontTTF = LoadFontEx("./assets/fonts/Tomorrow_Night.ttf", 24, NULL, 0); + Font fontTTF = LoadFontEx("./assets/fonts/spartan_500.ttf", 36, NULL, 0); // Initialize first floor init_floor(&gs, 1); From e39f4552db8039cad4812668d5d9c3dfe24bd369 Mon Sep 17 00:00:00 2001 From: "A.M. Rowsell" Date: Fri, 10 Apr 2026 17:07:49 -0400 Subject: [PATCH 13/13] font: fix the popup messages with the new font handling --- src/render.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/render.c b/src/render.c index 5a4c434..6a5882a 100644 --- a/src/render.c +++ b/src/render.c @@ -168,6 +168,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); // HP Bar background @@ -351,7 +352,8 @@ 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}, 10, NAR_CHAR_SPACE, text_color); + DrawTextEx(*font, log[idx], (Vector2){text_x, text_start_y + i * line_height}, NORM_FONT, SMALL_CHAR_SPACE, + text_color); } } } @@ -367,7 +369,7 @@ void render_inventory_overlay(const Player *p, int selected, Font *font) { // Title const char *title = "INVENTORY"; - int title_w = MeasureText(title, 24); + // 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); @@ -401,17 +403,17 @@ 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}, 14, NORM_CHAR_SPACE, name_color); + DrawTextEx(*font, name, (Vector2){overlay.x + 45, y_pos + 4}, NORM_FONT, SMALL_CHAR_SPACE, name_color); } // Power snprintf(slot_text, sizeof(slot_text), "+%d", item->power); - DrawTextEx(*font, slot_text, (Vector2){overlay.x + 150, y_pos + 4}, 14, NORM_CHAR_SPACE, YELLOW); + DrawTextEx(*font, slot_text, (Vector2){overlay.x + 150, y_pos + 4}, NORM_FONT, SMALL_CHAR_SPACE, YELLOW); // Action if (item->type == ITEM_POTION) { - DrawTextEx(*font, "[U]se", (Vector2){overlay.x + 200, y_pos + 4}, 14, NORM_CHAR_SPACE, GREEN); + DrawTextEx(*font, "[U]se", (Vector2){overlay.x + 200, y_pos + 4}, NORM_FONT, SMALL_CHAR_SPACE, GREEN); } else { - DrawTextEx(*font, "[E]quip [D]rop", (Vector2){overlay.x + 200, y_pos + 4}, 14, NORM_CHAR_SPACE, GOLD); + DrawTextEx(*font, "[E]quip [D]rop", (Vector2){overlay.x + 200, y_pos + 4}, NORM_FONT, SMALL_CHAR_SPACE, GOLD); } } else { // Empty slot @@ -423,9 +425,9 @@ void render_inventory_overlay(const Player *p, int selected, Font *font) { // Instructions at bottom const char *hint = "[1-0] Select [E] Equip [U] Use [D] Drop [I/ESC] Close"; - Vector2 hint_w = MeasureTextEx(*font, hint, NORM_FONT, NAR_CHAR_SPACE); - DrawTextEx(*font, hint, (Vector2){overlay.x + (overlay.width - hint_w.x) / 2, overlay.y + overlay.height - 22}, - NORM_FONT, NAR_CHAR_SPACE, (Color){80, 80, 80, 255}); + 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}); } static Color label_color(FloatingText *ft, int alpha) { @@ -652,8 +654,8 @@ void render_message(const char *message, Font *font) { longest_line_width = current_line_width; // Measure full message - int total_msg_width = MeasureText(message, font_size); - int box_width = total_msg_width + (padding_x * 2); + Vector2 total_msg_width = MeasureTextEx(*font, message, font_size, NORM_CHAR_SPACE); + int box_width = total_msg_width.x + (padding_x * 2); // If message is too long, use wrapped width if (box_width > max_box_width) { @@ -677,7 +679,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) / 2; + int text_x = (SCREEN_WIDTH - total_msg_width.x) / 2; int text_y = (SCREEN_HEIGHT - font_size) / 2; // For wrapped text, draw at box center with padding