1
0
Fork 0
forked from NotAShelf/rogged

Compare commits

..

1 commit

Author SHA1 Message Date
5a5663afca
render: add player sprite. welcome to dude man 2026-04-08 10:54:25 -04:00
10 changed files with 121 additions and 165 deletions

View file

@ -1,13 +1,11 @@
# Rogged # Rogged
Turn-based, infinite roguelike dungeon crawler built with C99 and with a dash of Turn-based roguelike dungeon crawler, built in C99 and with a dash of Zig to
Zig. Meant to serve primarily, but not exclusively, as a learning opportunity. serve as a learning opportunity. Rogged is basically a classic roguelike where
Rogged is basically a classic roguelike where you descend through floors of a you descend through floors of a procedurally generated dungeon, fighting
procedurally generated dungeon, fighting enemies, managing inventory, and trying enemies, managing inventory, and trying to reach the bottom alive.
to reach the bottom alive or die chasing a highscore!
The game itself, be it the code or mechanics, is _heavily_ in development. For A non-exhaustive list of its (current) features:
now, a non-exhaustive list of its (current) features are as follows:
- Turn-based combat with damage variance, critical hits, dodge, and block - Turn-based combat with damage variance, critical hits, dodge, and block
mechanics mechanics
@ -20,27 +18,33 @@ now, a non-exhaustive list of its (current) features are as follows:
- Procedural audio via raylib - Procedural audio via raylib
- ASCII-inspired tile rendering, with HP bars and floating damage text - ASCII-inspired tile rendering, with HP bars and floating damage text
There are still some features lacking polish, or lacking _any_ kind of attention **Controls:**
to be viable. For a semi-complete list of things that need to be done, see the
[future plans section](#future-plans). | Key | Action |
| ------------- | ----------------------------------- |
| WASD / Arrows | Move or attack |
| G | Pick up item |
| I | Open inventory |
| U | Use a potion |
| E | Equip item from inventory |
| D | Drop item |
| Y / N | Confirm / decline descending stairs |
| R | Restart (on game over) |
| Q | Quit |
## Build Instructions ## Build Instructions
Rogged is built on a relatively simple stack. It uses C99 for the main game Rogged is built with C99 and Zig. Besides `raylib` and `pkg-config` you will
logic, and Zig for the combat library. Besides `raylib` and `pkg-config`, you need a C compiler and basic Zig tooling. For now, we use C99 and Zig 0.15.2.
only need the core Zig tooling. For now the required Zig version is 0.15.2, but Those might change in the future.
this might change in the future. Additionally, you will need `clang-format` and
`just` for common development tasks in the case you plan to contribute. For Additionally you will need `clang-format` and `just` for the developer workflow
building, Zig is enough. if you plan to contribute.
### Using Nix (Recommended) ### Using Nix (Recommended)
The _recommended_ way of developing this project is using The recommended developer tooling is [Nix](https://nixos.org). This provides a
[Nix](https://nixos.org) and relying on devshells for pure, reproducible pure, reproducible devshell across all machines.
developer environment across all machines.
If you are a [Direnv](https://direnv.net) user, you may simply run
`direnv allow` or you may use `nix develop` to enter the default shell.
```sh ```sh
# Enter the development shell # Enter the development shell
@ -52,9 +56,6 @@ $ just dev
### Manual Build ### Manual Build
If you are allergic to good tooling and would rather use your system Zig, you
may simply invoke `zig build` after acquiring Zig 0.15.2.
```sh ```sh
# Full build # Full build
$ zig build $ zig build
@ -66,8 +67,6 @@ $ zig build run
$ just dev $ just dev
``` ```
The Justfile provides commands that work across both methods.
### Task Runner Commands ### Task Runner Commands
There's a `Justfile` designed to make common tasks somewhat easier. For now, There's a `Justfile` designed to make common tasks somewhat easier. For now,
@ -85,14 +84,10 @@ If the project gets more complicated, new tasks might be added.
## Future Plans ## Future Plans
The game is currently **playable end-to-end**, but it lacks a fair bit of polish The game is currently **playable end-to-end** but it lacks _serious_ polish to
to claim its place as a fun, engaging roguelike you can just boot up and play. claim its place as a fun roguelike. Some of the features I'd like to introduce,
Some of the features that are planned for the future, in no particular order, in no particular order, are as follows:
are as follows:
- [ ] **Better enemy AI** - The current AI is very simple.
- [ ] **Fog of War** - Instead of loading the entire map, let the player
discover the rooms
- [ ] **Save / Load system** - Persist and restore game state between sessions - [ ] **Save / Load system** - Persist and restore game state between sessions
- [ ] **More enemy variety** - Additional enemy types with unique abilities - [ ] **More enemy variety** - Additional enemy types with unique abilities
- [ ] **More item variety** - Rings, wands, scrolls, and cursed items - [ ] **More item variety** - Rings, wands, scrolls, and cursed items
@ -105,9 +100,8 @@ are as follows:
- [ ] **UI polish** - Better message log history, item descriptions, death - [ ] **UI polish** - Better message log history, item descriptions, death
screen screen
Later down the line it might be an interesting choice to provide a scripting In addition, it might be interesting to allow customizing the "world state" by
API, likely with Lua, to allow customizing the game state and events. Though, as scripting API. Though, that is for much later.
that is for much later.
## Attributions ## Attributions
@ -124,6 +118,4 @@ Additionally, _huge_ thanks to [Raylib] for how easy it made graphics and audio.
This was perhaps my best experience in developing a graphical application, and This was perhaps my best experience in developing a graphical application, and
CERTAINLY the most ergonomic when it comes to writing a game. CERTAINLY the most ergonomic when it comes to writing a game.
---
_I got rogged :/_ _I got rogged :/_

View file

@ -25,7 +25,6 @@ pub fn build(b: *std.Build) void {
"src/main.c", "src/main.c",
"src/map.c", "src/map.c",
"src/player.c", "src/player.c",
"src/movement.c",
"src/render.c", "src/render.c",
"src/rng.c", "src/rng.c",
"src/settings.c", "src/settings.c",

View file

@ -4,10 +4,6 @@
#include "settings.h" #include "settings.h"
#include <raylib.h> #include <raylib.h>
typedef struct {
int x, y;
} Vec2;
// Tile types // Tile types
typedef enum { TILE_WALL, TILE_FLOOR, TILE_STAIRS } TileType; typedef enum { TILE_WALL, TILE_FLOOR, TILE_STAIRS } TileType;
@ -61,7 +57,7 @@ typedef struct {
// Player // Player
typedef struct { typedef struct {
Vec2 position; int x, y;
int hp, max_hp; int hp, max_hp;
int attack; int attack;
int defense; int defense;
@ -87,7 +83,7 @@ typedef enum { ENEMY_GOBLIN, ENEMY_SKELETON, ENEMY_ORC } EnemyType;
// Enemy // Enemy
typedef struct { typedef struct {
Vec2 position; int x, y;
int hp; int hp;
int max_hp; int max_hp;
int attack; int attack;

View file

@ -2,7 +2,6 @@
#include "combat.h" #include "combat.h"
#include "common.h" #include "common.h"
#include "map.h" #include "map.h"
#include "movement.h"
#include "rng.h" #include "rng.h"
#include <string.h> #include <string.h>
@ -30,7 +29,7 @@ void enemy_spawn(Enemy enemies[], int *count, Map *map, Player *p, int floor) {
get_random_floor_tile(map, &ex, &ey, 50); get_random_floor_tile(map, &ex, &ey, 50);
// Don't spawn on player position // Don't spawn on player position
if (ex == p->position.x && ey == p->position.y) { if (ex == p->x && ey == p->y) {
continue; continue;
} }
@ -42,8 +41,8 @@ void enemy_spawn(Enemy enemies[], int *count, Map *map, Player *p, int floor) {
// Create enemy // Create enemy
Enemy e; Enemy e;
memset(&e, 0, sizeof(Enemy)); memset(&e, 0, sizeof(Enemy));
e.position.x = ex; e.x = ex;
e.position.y = ey; e.y = ey;
e.alive = 1; e.alive = 1;
e.type = rng_int(ENEMY_GOBLIN, max_type); e.type = rng_int(ENEMY_GOBLIN, max_type);
e.effect_count = 0; e.effect_count = 0;
@ -128,7 +127,7 @@ void enemy_spawn(Enemy enemies[], int *count, Map *map, Player *p, int floor) {
// Check if position has an enemy // Check if position has an enemy
int is_enemy_at(const Enemy *enemies, int count, int x, int y) { int is_enemy_at(const Enemy *enemies, int count, int x, int y) {
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
if (enemies[i].alive && enemies[i].position.x == x && enemies[i].position.y == y) { if (enemies[i].alive && enemies[i].x == x && enemies[i].y == y) {
return 1; return 1;
} }
} }
@ -137,37 +136,45 @@ int is_enemy_at(const Enemy *enemies, int count, int x, int y) {
// Check if enemy can see player (adjacent) // Check if enemy can see player (adjacent)
static int can_see_player(Enemy *e, Player *p) { static int can_see_player(Enemy *e, Player *p) {
int dx = p->position.x - e->position.x; int dx = p->x - e->x;
int dy = p->position.y - e->position.y; int dy = p->y - e->y;
return (dx >= -1 && dx <= 1 && dy >= -1 && dy <= 1); 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 // Move enemy toward player
static void enemy_move_toward_player(Enemy *e, Player *p, Map *map, Enemy *all_enemies, int enemy_count) { static void enemy_move_toward_player(Enemy *e, Player *p, Map *map, Enemy *all_enemies, int enemy_count) {
int dx = 0, dy = 0; int dx = 0, dy = 0;
if (p->position.x > e->position.x) if (p->x > e->x)
dx = 1; dx = 1;
else if (p->position.x < e->position.x) else if (p->x < e->x)
dx = -1; dx = -1;
if (p->position.y > e->position.y) if (p->y > e->y)
dy = 1; dy = 1;
else if (p->position.y < e->position.y) else if (p->y < e->y)
dy = -1; dy = -1;
Vec2 dir = {dx, 0}; // Try horizontal first, then vertical
if (dx != 0) { int new_x = e->x + dx;
MoveResult r = try_move_entity(&e->position, dir, map, p, all_enemies, enemy_count, false); int new_y = e->y;
if (r == MOVE_RESULT_MOVED)
return;
}
dir.x = 0; if (dx != 0 && is_floor(map, new_x, new_y) && !is_enemy_at(all_enemies, enemy_count, new_x, new_y) &&
dir.y = dy; !is_player_at(p, new_x, new_y)) {
if (dy != 0) { e->x = new_x;
try_move_entity(&e->position, dir, map, p, all_enemies, enemy_count, false); } 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;
}
} }
} }

View file

@ -4,7 +4,6 @@
#include "enemy.h" #include "enemy.h"
#include "items.h" #include "items.h"
#include "map.h" #include "map.h"
#include "movement.h"
#include "player.h" #include "player.h"
#include "raylib.h" #include "raylib.h"
#include "render.h" #include "render.h"
@ -118,8 +117,8 @@ static void init_floor(GameState *gs, int floor_num) {
gs->floors_reached = 1; gs->floors_reached = 1;
} else { } else {
// Move player to new floor position // Move player to new floor position
gs->player.position.x = start_x; gs->player.x = start_x;
gs->player.position.y = start_y; gs->player.y = start_y;
} }
gs->player.floor = floor_num; gs->player.floor = floor_num;
@ -138,8 +137,7 @@ static void tick_all_effects(GameState *gs) {
// Player effects // Player effects
int player_effect_dmg = combat_tick_effects(&gs->player); int player_effect_dmg = combat_tick_effects(&gs->player);
if (player_effect_dmg > 0) { if (player_effect_dmg > 0) {
spawn_floating_text(gs, gs->player.position.x * TILE_SIZE + 8, gs->player.position.y * TILE_SIZE, player_effect_dmg, spawn_floating_text(gs, gs->player.x * TILE_SIZE + 8, gs->player.y * TILE_SIZE, player_effect_dmg, 0);
0);
gs->screen_shake = SHAKE_EFFECT_DURATION; gs->screen_shake = SHAKE_EFFECT_DURATION;
} }
@ -157,7 +155,7 @@ static void tick_all_effects(GameState *gs) {
continue; continue;
int enemy_effect_dmg = combat_tick_enemy_effects(e); int enemy_effect_dmg = combat_tick_enemy_effects(e);
if (enemy_effect_dmg > 0) { if (enemy_effect_dmg > 0) {
spawn_floating_text(gs, e->position.x * TILE_SIZE + 8, e->position.y * TILE_SIZE, enemy_effect_dmg, 0); spawn_floating_text(gs, e->x * TILE_SIZE + 8, e->y * TILE_SIZE, enemy_effect_dmg, 0);
} }
if (!e->alive) { if (!e->alive) {
add_log(gs, "Enemy died from effects!"); add_log(gs, "Enemy died from effects!");
@ -175,7 +173,7 @@ static void post_action(GameState *gs, Enemy *attacked_enemy) {
return; return;
// Check if stepped on stairs // Check if stepped on stairs
if (gs->map.tiles[gs->player.position.y][gs->player.position.x] == TILE_STAIRS) { if (gs->map.tiles[gs->player.y][gs->player.x] == TILE_STAIRS) {
gs->awaiting_descend = 1; gs->awaiting_descend = 1;
gs->last_message = "Descend to next floor? (Y/N)"; gs->last_message = "Descend to next floor? (Y/N)";
gs->message_timer = 120; gs->message_timer = 120;
@ -184,8 +182,8 @@ static void post_action(GameState *gs, Enemy *attacked_enemy) {
// combat feedback - player attacked an enemy this turn // combat feedback - player attacked an enemy this turn
if (attacked_enemy != NULL) { if (attacked_enemy != NULL) {
int ex = attacked_enemy->position.x * TILE_SIZE + 8; int ex = attacked_enemy->x * TILE_SIZE + 8;
int ey = attacked_enemy->position.y * TILE_SIZE; int ey = attacked_enemy->y * TILE_SIZE;
if (combat_was_dodged()) { if (combat_was_dodged()) {
spawn_floating_label(gs, ex, ey, LABEL_DODGE, EFFECT_NONE); spawn_floating_label(gs, ex, ey, LABEL_DODGE, EFFECT_NONE);
@ -226,8 +224,8 @@ static void post_action(GameState *gs, Enemy *attacked_enemy) {
gs->screen_shake = SHAKE_PLAYER_DAMAGE_DURATION; gs->screen_shake = SHAKE_PLAYER_DAMAGE_DURATION;
gs->damage_taken += combat_get_last_damage(); gs->damage_taken += combat_get_last_damage();
gs->times_hit++; gs->times_hit++;
spawn_floating_text(gs, gs->player.position.x * TILE_SIZE + 8, gs->player.position.y * TILE_SIZE, spawn_floating_text(gs, gs->player.x * TILE_SIZE + 8, gs->player.y * TILE_SIZE, combat_get_last_damage(),
combat_get_last_damage(), combat_was_critical()); combat_was_critical());
} }
// Set message and check game over // Set message and check game over
@ -390,7 +388,7 @@ static int handle_movement_input(GameState *gs) {
// Check for manual item pickup (G key) // Check for manual item pickup (G key)
if (IsKeyPressed(KEY_G)) { if (IsKeyPressed(KEY_G)) {
Item *item = get_item_at_floor(gs->items, gs->item_count, gs->player.position.x, gs->player.position.y); Item *item = get_item_at_floor(gs->items, gs->item_count, gs->player.x, gs->player.y);
if (item != NULL) { if (item != NULL) {
if (player_pickup(&gs->player, item)) { if (player_pickup(&gs->player, item)) {
gs->items_collected++; gs->items_collected++;
@ -419,45 +417,38 @@ static int handle_movement_input(GameState *gs) {
} }
} }
// Movement: use IsKeyDown for held-key repeat
Vec2 direction = {0, 0}; int dx = 0, dy = 0;
if (IsKeyDown(KEY_W) || IsKeyDown(KEY_UP)) if (IsKeyDown(KEY_W) || IsKeyDown(KEY_UP))
direction.y = -1; dy = -1;
else if (IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN)) else if (IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN))
direction.y = 1; dy = 1;
else if (IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT)) else if (IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT))
direction.x = -1; dx = -1;
else if (IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT)) else if (IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT))
direction.x = 1; dx = 1;
if (direction.x == 0 && direction.y == 0) if (dx == 0 && dy == 0)
return 0; return 0;
// Reset combat event before player acts // Reset combat event before player acts
combat_reset_event(); combat_reset_event();
int new_x = gs->player.x + dx;
int new_x = gs->player.position.x + direction.x; int new_y = gs->player.y + dy;
int new_y = gs->player.position.y + direction.y;
Enemy *target = NULL;
int action = 0; int action = 0;
MoveResult result = // Attack enemy at target tile, or move into it
try_move_entity(&gs->player.position, direction, &gs->map, &gs->player, gs->enemies, gs->enemy_count, true); Enemy *target = player_find_enemy_at(gs->enemies, gs->enemy_count, new_x, new_y);
if (result == MOVE_RESULT_MOVED) { if (target != NULL) {
player_on_move(&gs->player); player_attack(&gs->player, target);
action = 1; action = 1;
} else if (result == MOVE_RESULT_BLOCKED_ENEMY) { } else {
target = player_find_enemy_at(gs->enemies, gs->enemy_count, new_x, new_y); action = player_move(&gs->player, dx, dy, &gs->map);
if (target != NULL) {
player_attack(&gs->player, target);
action = 1;
}
} }
if (action) if (action)
post_action(gs, target); post_action(gs, target); // target is NULL on move, enemy ptr on attack
return action; return action;
} }

View file

@ -1,29 +0,0 @@
#include "movement.h"
#include "enemy.h"
#include "map.h"
#include <stdbool.h>
// 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;
}

View file

@ -1,17 +0,0 @@
#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

View file

@ -2,12 +2,15 @@
#include "combat.h" #include "combat.h"
#include "common.h" #include "common.h"
#include "items.h" #include "items.h"
#include "map.h"
#include "settings.h" #include "settings.h"
#include "utils.h"
#include <stdlib.h>
#include <string.h> #include <string.h>
void player_init(Player *p, int x, int y) { void player_init(Player *p, int x, int y) {
p->position.x = x; p->x = x;
p->position.y = y; p->y = y;
p->hp = PLAYER_BASE_HP; p->hp = PLAYER_BASE_HP;
p->max_hp = PLAYER_BASE_HP; p->max_hp = PLAYER_BASE_HP;
p->attack = PLAYER_BASE_ATTACK; p->attack = PLAYER_BASE_ATTACK;
@ -40,20 +43,36 @@ Enemy *player_find_enemy_at(Enemy *enemies, int count, int x, int y) {
if (count > MAX_ENEMIES) if (count > MAX_ENEMIES)
count = MAX_ENEMIES; count = MAX_ENEMIES;
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
if (enemies[i].alive && enemies[i].position.x == x && enemies[i].position.y == y) if (enemies[i].alive && enemies[i].x == x && enemies[i].y == y)
return &enemies[i]; return &enemies[i];
} }
return NULL; return NULL;
} }
void player_on_move(Player *p) { 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;
p->step_count += 1; p->step_count += 1;
// Regen suppressed while poisoned, bleeding, or burning
if (p->step_count % REGEN_STEP_INTERVAL == 0 && p->hp < p->max_hp && 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_POISON) &&
!combat_has_effect(p->effects, p->effect_count, EFFECT_BLEED) && !combat_has_effect(p->effects, p->effect_count, EFFECT_BLEED) &&
!combat_has_effect(p->effects, p->effect_count, EFFECT_BURN)) { !combat_has_effect(p->effects, p->effect_count, EFFECT_BURN)) {
p->hp += 1; p->hp += 1;
} }
return 1;
} }
void player_attack(Player *p, Enemy *e) { void player_attack(Player *p, Enemy *e) {
@ -209,8 +228,8 @@ int player_drop_item(Player *p, int inv_index, Item *items, int item_count) {
if (items[i].picked_up) { if (items[i].picked_up) {
// Place dropped item at this position // Place dropped item at this position
items[i] = *item; items[i] = *item;
items[i].x = p->position.x; items[i].x = p->x;
items[i].y = p->position.y; items[i].y = p->y;
items[i].picked_up = 0; items[i].picked_up = 0;
// Remove from inventory // Remove from inventory
player_remove_inventory_item(p, inv_index); player_remove_inventory_item(p, inv_index);

View file

@ -6,8 +6,8 @@
// Initialize player at position // Initialize player at position
void player_init(Player *p, int x, int y); void player_init(Player *p, int x, int y);
// Apply status effects, healing, etc // Move player to (x+dx, y+dy); returns 1 if moved, 0 if blocked
void player_on_move(Player *p); int player_move(Player *p, int dx, int dy, Map *map);
// Find a living enemy at tile (x, y); returns NULL if none // 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); Enemy *player_find_enemy_at(Enemy *enemies, int count, int x, int y);

View file

@ -30,10 +30,8 @@ void render_map(const Map *map) {
} }
void render_player(const Player *p, Texture2D *ptile) { void render_player(const Player *p, Texture2D *ptile) {
//Rectangle rect = {(float)(p->position.x * TILE_SIZE), (float)(p->position.y * TILE_SIZE), (float)TILE_SIZE, // Rectangle rect = {(float)(p->x * TILE_SIZE), (float)(p->y * TILE_SIZE), (float)TILE_SIZE, (float)TILE_SIZE};
// (float)TILE_SIZE}; // DrawRectangleRec(rect, BLUE);
//DrawRectangleRec(rect, BLUE);
DrawTexture(*ptile, (float)(p->x * TILE_SIZE), (float)(p->y * TILE_SIZE), (Color){255, 255, 255, 255}); DrawTexture(*ptile, (float)(p->x * TILE_SIZE), (float)(p->y * TILE_SIZE), (Color){255, 255, 255, 255});
} }
@ -42,8 +40,8 @@ void render_enemies(const Enemy *enemies, int count) {
if (!enemies[i].alive) if (!enemies[i].alive)
continue; continue;
Rectangle rect = {(float)(enemies[i].position.x * TILE_SIZE), (float)(enemies[i].position.y * TILE_SIZE), Rectangle rect = {(float)(enemies[i].x * TILE_SIZE), (float)(enemies[i].y * TILE_SIZE), (float)TILE_SIZE,
(float)TILE_SIZE, (float)TILE_SIZE}; (float)TILE_SIZE};
// Different colors based on enemy type // Different colors based on enemy type
Color enemy_color; Color enemy_color;
@ -75,8 +73,8 @@ void render_enemies(const Enemy *enemies, int count) {
bar_color = (Color){200, 180, 40, 255}; // yellow bar_color = (Color){200, 180, 40, 255}; // yellow
else else
bar_color = (Color){200, 60, 60, 255}; // red bar_color = (Color){200, 60, 60, 255}; // red
Rectangle hp_bar = {(float)(enemies[i].position.x * TILE_SIZE), (float)(enemies[i].position.y * TILE_SIZE - 4), Rectangle hp_bar = {(float)(enemies[i].x * TILE_SIZE), (float)(enemies[i].y * TILE_SIZE - 4), (float)hp_pixels,
(float)hp_pixels, 3}; 3};
DrawRectangleRec(hp_bar, bar_color); DrawRectangleRec(hp_bar, bar_color);
} }
} }