various: fixup doors

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: Id81f32d86f70a7df99c2ad3d478646416a6a6964
This commit is contained in:
raf 2026-04-28 16:31:57 +03:00
commit d8b49054d5
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
6 changed files with 345 additions and 87 deletions

View file

@ -15,6 +15,9 @@ void map_init(Map *map) {
}
memset(map->light_map, 0, sizeof(map->light_map));
memset(map->remembered, 0, sizeof(map->remembered));
memset(map->door_open_from, 255, sizeof(map->door_open_from));
memset(map->door_anim_timer, 0, sizeof(map->door_anim_timer));
memset(map->door_anim_target, 0, sizeof(map->door_anim_target));
map->room_count = 0;
}
@ -22,7 +25,7 @@ int is_floor(const Map *map, int x, int y) {
if (!in_bounds(x, y, MAP_WIDTH, MAP_HEIGHT))
return 0;
return map->tiles[y][x] == TILE_FLOOR || map->tiles[y][x] == TILE_STAIRS || map->tiles[y][x] == TILE_DOOR_OPEN ||
map->tiles[y][x] == TILE_DOOR_RUINED;
map->tiles[y][x] == TILE_DOOR_RUINED || map->tiles[y][x] == TILE_DOOR_CLOSED;
}
void get_room_center(Room *room, int *cx, int *cy) {
@ -128,13 +131,59 @@ static int is_room_boundary(Map *map, int x, int y) {
return 0;
}
// Place doors at corridor-room junctions
// DISABLED: Door placement removed per user request
// A corridor tile is a narrow floor passage with walls on at least 2 sides.
static int is_corridor_tile(const Map *map, int x, int y) {
if (map->tiles[y][x] != TILE_FLOOR)
return 0;
int walls = 0;
if (y > 0 && map->tiles[y - 1][x] == TILE_WALL)
walls++;
if (y < MAP_HEIGHT - 1 && map->tiles[y + 1][x] == TILE_WALL)
walls++;
if (x > 0 && map->tiles[y][x - 1] == TILE_WALL)
walls++;
if (x < MAP_WIDTH - 1 && map->tiles[y][x + 1] == TILE_WALL)
walls++;
return walls >= 2;
}
static int tile_in_any_room(int x, int y, Room *rooms, int room_count) {
for (int i = 0; i < room_count; i++) {
if (x >= rooms[i].x && x < rooms[i].x + rooms[i].w && y >= rooms[i].y && y < rooms[i].y + rooms[i].h)
return 1;
}
return 0;
}
// Place doors at corridor-room junctions.
// Doors sit on corridor tiles, not room tiles, so they occupy the actual doorway.
static void place_doors(Map *map, Room *rooms, int room_count) {
(void)map;
(void)rooms;
(void)room_count;
// No-op: doors disabled
for (int y = 0; y < MAP_HEIGHT; y++) {
for (int x = 0; x < MAP_WIDTH; x++) {
if (map->tiles[y][x] != TILE_FLOOR)
continue;
if (!is_corridor_tile(map, x, y))
continue;
// Don't place doors inside rooms — corridors are between rooms
if (tile_in_any_room(x, y, rooms, room_count))
continue;
// Corridor must be adjacent to a room floor tile
const int dx[4] = {0, 0, 1, -1};
const int dy[4] = {1, -1, 0, 0};
for (int i = 0; i < 4; i++) {
int nx = x + dx[i];
int ny = y + dy[i];
if (!in_bounds(nx, ny, MAP_WIDTH, MAP_HEIGHT))
continue;
if (map->tiles[ny][nx] != TILE_FLOOR)
continue;
if (tile_in_any_room(nx, ny, rooms, room_count)) {
map->tiles[y][x] = TILE_DOOR_CLOSED;
break;
}
}
}
}
}
// Connect all rooms with corridors
@ -276,7 +325,8 @@ static int trace_line_of_sight(const Map *map, int x1, int y1, int x2, int y2) {
if (x == x2 && y == y2)
return 1;
if (map->tiles[y][x] == TILE_WALL && !(x == x1 && y == y1))
TileType t = map->tiles[y][x];
if ((t == TILE_WALL || t == TILE_DOOR_CLOSED) && !(x == x1 && y == y1))
return 0;
int e2 = 2 * err;
@ -421,7 +471,17 @@ void compute_lighting(Map *map, const LightSource *sources, int num_sources) {
for (int ty = 0; ty < MAP_HEIGHT; ty++) {
for (int tx = 0; tx < MAP_WIDTH; tx++) {
if (tile_brightness(map, tx, ty) > LIGHT_SIGHT_THRESHOLD)
int max_bright = 0;
int bx = tx * SUB_TILE_RES;
int by = ty * SUB_TILE_RES;
for (int dy = 0; dy < SUB_TILE_RES; dy++) {
for (int dx = 0; dx < SUB_TILE_RES; dx++) {
int v = map->light_map[by + dy][bx + dx];
if (v > max_bright)
max_bright = v;
}
}
if (max_bright > LIGHT_SIGHT_THRESHOLD)
map->remembered[ty][tx] = 1;
}
}

View file

@ -43,6 +43,9 @@ typedef struct {
int room_count;
unsigned char light_map[MAP_HEIGHT * SUB_TILE_RES][MAP_WIDTH * SUB_TILE_RES];
unsigned char remembered[MAP_HEIGHT][MAP_WIDTH];
unsigned char door_open_from[MAP_HEIGHT][MAP_WIDTH]; // 0=N,1=S,2=E,3=W,255=closed
unsigned char door_anim_timer[MAP_HEIGHT][MAP_WIDTH];
unsigned char door_anim_target[MAP_HEIGHT][MAP_WIDTH]; // 0=closed, 1=open
} Map;
// Dungeon

View file

@ -231,6 +231,19 @@ static void post_action(GameState *gs, Enemy *attacked_enemy) {
}
}
// Close doors that the player moved away from before recomputing lighting.
for (int dy = 0; dy < MAP_HEIGHT; dy++) {
for (int dx = 0; dx < MAP_WIDTH; dx++) {
if (gs->map.tiles[dy][dx] == TILE_DOOR_OPEN) {
if (gs->player.position.x != dx || gs->player.position.y != dy) {
gs->map.tiles[dy][dx] = TILE_DOOR_CLOSED;
gs->map.door_anim_target[dy][dx] = 0;
gs->map.door_anim_timer[dy][dx] = DOOR_ANIM_FRAMES;
}
}
}
}
// Update visibility based on player's new position
LightSource p_light = {gs->player.position.x, gs->player.position.y, PLAYER_LIGHT_INTENSITY, PLAYER_LIGHT_RANGE};
LightSource srcs[1 + 32];
@ -277,6 +290,18 @@ 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);
// Close doors that the player moved away from before recomputing lighting.
for (int dy = 0; dy < MAP_HEIGHT; dy++) {
for (int dx = 0; dx < MAP_WIDTH; dx++) {
if (gs->map.tiles[dy][dx] == TILE_DOOR_OPEN) {
if (gs->player.position.x != dx || gs->player.position.y != dy) {
gs->map.tiles[dy][dx] = TILE_DOOR_CLOSED;
gs->map.door_anim_target[dy][dx] = 0;
gs->map.door_anim_timer[dy][dx] = DOOR_ANIM_FRAMES;
}
}
}
}
{
LightSource l = {gs->player.position.x, gs->player.position.y, PLAYER_LIGHT_INTENSITY, PLAYER_LIGHT_RANGE};
LightSource s[1 + 32];
@ -639,6 +664,17 @@ static void game_loop(unsigned int run_seed, FontManager *fm) {
if (gs.slash_timer > 0)
gs.slash_timer--;
// Door animations are visual, so they tick every rendered frame.
for (int y = 0; y < MAP_HEIGHT; y++) {
for (int x = 0; x < MAP_WIDTH; x++) {
if (gs.map.door_anim_timer[y][x] > 0) {
gs.map.door_anim_timer[y][x]--;
if (gs.map.door_anim_timer[y][x] == 0 && gs.map.door_anim_target[y][x] == 0)
gs.map.door_open_from[y][x] = 255;
}
}
}
// Update player animation
if (gs.player.anim_timer > 0) {
gs.player.anim_timer--;
@ -787,4 +823,4 @@ int main(int argc, char **argv) {
audio_close();
return 0;
}
}

View file

@ -1,6 +1,7 @@
#include "movement.h"
#include "enemy.h"
#include "map/map.h"
#include "map/utils.h"
#include <stdbool.h>
// Check if position is occupied by player
@ -8,11 +9,33 @@ static int is_player_at(Player *p, int x, int y) {
return (p->position.x == x && p->position.y == y);
}
static int direction_to_door_open_from(Vec2 dir) {
if (dir.y == -1)
return 0; // N
if (dir.y == 1)
return 1; // S
if (dir.x == 1)
return 2; // E
if (dir.x == -1)
return 3; // W
return 255;
}
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 (!in_bounds(new_x, new_y, MAP_WIDTH, MAP_HEIGHT))
return MOVE_RESULT_BLOCKED_WALL;
if (map->tiles[new_y][new_x] == TILE_DOOR_CLOSED) {
map->tiles[new_y][new_x] = TILE_DOOR_OPEN;
map->door_open_from[new_y][new_x] = direction_to_door_open_from(direction);
map->door_anim_target[new_y][new_x] = 1;
map->door_anim_timer[new_y][new_x] = DOOR_ANIM_FRAMES;
}
if (!is_floor(map, new_x, new_y))
return MOVE_RESULT_BLOCKED_WALL;

View file

@ -66,6 +66,43 @@ static void draw_text_body(Font f, const char *text, float x, float y, int size,
DrawTextEx(f, text, (Vector2){x, y}, (float)size, spacing, c);
}
static int is_deep_corridor(const Map *map, int x, int y) {
if (map->tiles[y][x] != TILE_FLOOR)
return 0;
int walls_ns = (y > 0 && map->tiles[y - 1][x] == TILE_WALL ? 1 : 0) +
(y < MAP_HEIGHT - 1 && map->tiles[y + 1][x] == TILE_WALL ? 1 : 0);
int walls_ew = (x > 0 && map->tiles[y][x - 1] == TILE_WALL ? 1 : 0) +
(x < MAP_WIDTH - 1 && map->tiles[y][x + 1] == TILE_WALL ? 1 : 0);
int is_corridor = (walls_ns >= 2 && walls_ew == 0) || (walls_ew >= 2 && walls_ns == 0);
if (!is_corridor)
return 0;
// A "deep" corridor is surrounded by corridor on all floor sides.
// If any floor neighbor is NOT a corridor, this is an entrance/exit.
const int dx4[4] = {0, 0, 1, -1};
const int dy4[4] = {1, -1, 0, 0};
for (int i = 0; i < 4; i++) {
int nx = x + dx4[i];
int ny = y + dy4[i];
if (nx < 0 || nx >= MAP_WIDTH || ny < 0 || ny >= MAP_HEIGHT)
continue;
if (map->tiles[ny][nx] != TILE_FLOOR)
continue;
int nw_ns = (ny > 0 && map->tiles[ny - 1][nx] == TILE_WALL ? 1 : 0) +
(ny < MAP_HEIGHT - 1 && map->tiles[ny + 1][nx] == TILE_WALL ? 1 : 0);
int nw_ew = (nx > 0 && map->tiles[ny][nx - 1] == TILE_WALL ? 1 : 0) +
(nx < MAP_WIDTH - 1 && map->tiles[ny][nx + 1] == TILE_WALL ? 1 : 0);
int neighbor_is_corridor = (nw_ns >= 2 && nw_ew == 0) || (nw_ew >= 2 && nw_ns == 0);
if (!neighbor_is_corridor)
return 0;
}
return 1;
}
static Color color_lerp(Color a, Color b, float t) {
return (Color){(unsigned char)(a.r + (int)((b.r - a.r) * t)), (unsigned char)(a.g + (int)((b.g - a.g) * t)),
(unsigned char)(a.b + (int)((b.b - a.b) * t)), (unsigned char)(a.a + (int)((b.a - a.a) * t))};
}
void render_map(const Map *map, const Tileset *tileset) {
for (int y = 0; y < MAP_HEIGHT; y++) {
for (int x = 0; x < MAP_WIDTH; x++) {
@ -78,6 +115,11 @@ void render_map(const Map *map, const Tileset *tileset) {
continue;
}
float base_light = brightness > 0 ? AMBIENT_LIGHT_FACTOR : REMEMBERED_LIGHT_FACTOR;
float light_factor = base_light + (1.0f - base_light) * powf((float)brightness / 255.0f, LIGHT_EXPONENT);
if (is_deep_corridor(map, x, y))
light_factor *= 0.82f;
int tile_id = -1;
switch (map->tiles[y][x]) {
case TILE_WALL:
@ -100,94 +142,175 @@ void render_map(const Map *map, const Tileset *tileset) {
break;
}
if (tile_id >= 0 && tileset != NULL && tileset->finalized) {
Rectangle src = tileset_get_region(tileset, tile_id);
if (src.width > 0) {
Color tint;
if (brightness > 0) {
float lit = (float)brightness / 255.0f;
tint = (Color){(unsigned char)(128 + (int)(127.0f * lit)), (unsigned char)(128 + (int)(127.0f * lit)),
(unsigned char)(128 + (int)(127.0f * lit)), 255};
} else {
tint = (Color){128, 128, 128, 255};
}
DrawTexturePro(tileset->atlas, src, dst, (Vector2){0, 0}, 0.0f, tint);
continue;
int is_door = (map->tiles[y][x] == TILE_DOOR_CLOSED || map->tiles[y][x] == TILE_DOOR_OPEN);
int tile_drawn = 0;
// Draw floor underneath doors using tileset if available, so the
// floor matches adjacent tiles
if (is_door && tileset != NULL && tileset->finalized) {
int floor_id = TILE_FLOOR_0 + ((x * 7 + y * 13) % 4);
Rectangle floor_src = tileset_get_region(tileset, floor_id);
if (floor_src.width > 0) {
int fv = (int)(255.0f * light_factor);
if (fv > 255)
fv = 255;
Color ftint = (Color){(unsigned char)fv, (unsigned char)fv, (unsigned char)fv, 255};
DrawTexturePro(tileset->atlas, floor_src, dst, (Vector2){0, 0}, 0.0f, ftint);
tile_drawn = 1;
}
}
Color wall_color = brightness > 0 ? DARKGRAY : (Color){25, 25, 30, 255};
Color floor_color = brightness > 0 ? BLACK : (Color){15, 15, 20, 255};
Color stairs_color = brightness > 0 ? (Color){180, 160, 100, 255} : (Color){60, 55, 50, 255};
Color door_color = brightness > 0 ? (Color){139, 119, 89, 255} : (Color){60, 55, 50, 255};
if (tile_id >= 0 && tileset != NULL && tileset->finalized && !is_door) {
Rectangle src = tileset_get_region(tileset, tile_id);
if (src.width > 0) {
int is_opaque = (map->tiles[y][x] == TILE_WALL);
float wall_dim = is_opaque ? (AMBIENT_LIGHT_FACTOR + 0.92f * light_factor) : light_factor;
int tv = (int)(255.0f * wall_dim);
if (tv > 255)
tv = 255;
Color tint = (Color){(unsigned char)tv, (unsigned char)tv, (unsigned char)tv, 255};
DrawTexturePro(tileset->atlas, src, dst, (Vector2){0, 0}, 0.0f, tint);
tile_drawn = 1;
}
}
switch (map->tiles[y][x]) {
case TILE_WALL:
DrawRectangleRec(dst, wall_color);
break;
case TILE_FLOOR:
DrawRectangleRec(dst, floor_color);
// Torch flicker: warm tint on floor tiles adjacent to stairs
{
int is_adjacent_to_stairs = 0;
for (int dy = -1; dy <= 1 && !is_adjacent_to_stairs; dy++) {
for (int dx = -1; dx <= 1 && !is_adjacent_to_stairs; dx++) {
int nx = x + dx;
int ny = y + dy;
if (in_bounds(nx, ny, MAP_WIDTH, MAP_HEIGHT) && map->tiles[ny][nx] == TILE_STAIRS) {
is_adjacent_to_stairs = 1;
if (!tile_drawn || is_door) {
Color wall_color = color_lerp((Color){42, 42, 52, 255}, DARKGRAY, light_factor);
Color floor_color = color_lerp((Color){32, 32, 42, 255}, BLACK, light_factor);
Color stairs_color = color_lerp((Color){85, 80, 70, 255}, (Color){180, 160, 100, 255}, light_factor);
Color door_color = color_lerp((Color){38, 34, 30, 255}, (Color){120, 92, 58, 255}, light_factor);
Color door_handle_color = color_lerp((Color){42, 38, 32, 255}, (Color){145, 122, 82, 255}, light_factor);
switch (map->tiles[y][x]) {
case TILE_WALL:
DrawRectangleRec(dst, wall_color);
break;
case TILE_FLOOR:
DrawRectangleRec(dst, floor_color);
// Torch flicker: warm tint on floor tiles adjacent to stairs
{
int is_adjacent_to_stairs = 0;
for (int dy = -1; dy <= 1 && !is_adjacent_to_stairs; dy++) {
for (int dx = -1; dx <= 1 && !is_adjacent_to_stairs; dx++) {
int nx = x + dx;
int ny = y + dy;
if (in_bounds(nx, ny, MAP_WIDTH, MAP_HEIGHT) && map->tiles[ny][nx] == TILE_STAIRS) {
is_adjacent_to_stairs = 1;
}
}
}
if (is_adjacent_to_stairs && light_factor > 0.05f) {
int flicker = (int)(sinf(GetTime() * 5.0f) * 15.0f);
DrawRectangleRec(dst, (Color){40 + flicker, 25, 10, 60});
}
}
if (is_adjacent_to_stairs && brightness > 0) {
int flicker = (int)(sinf(GetTime() * 5.0f) * 15.0f);
DrawRectangleRec(dst, (Color){40 + flicker, 25, 10, 60});
// Grid lines
if (DRAW_GRID_LINES && light_factor > 0.05f) {
DrawRectangleLines((int)dst.x, (int)dst.y, (int)dst.width, (int)dst.height, (Color){20, 20, 20, 80});
}
break;
case TILE_STAIRS:
DrawRectangleRec(dst, stairs_color);
// Make stairs very visible with bright symbol and bounce
{
int bounce = (int)(sinf(GetTime() * 3.0f) * 1.5f);
if (light_factor > 0.05f)
DrawText(">", x * TILE_SIZE + 4, y * TILE_SIZE + 1 + bounce, NORM_FONT + 2, (Color){255, 255, 200, 255});
else
DrawText(">", x * TILE_SIZE + 4, y * TILE_SIZE + 1 + bounce, NORM_FONT + 2, (Color){100, 90, 70, 255});
}
break;
case TILE_DOOR_CLOSED:
case TILE_DOOR_OPEN: {
// Determine door orientation by adjacent walls
int wall_n = (y > 0 && map->tiles[y - 1][x] == TILE_WALL);
int wall_s = (y < MAP_HEIGHT - 1 && map->tiles[y + 1][x] == TILE_WALL);
int wall_e = (x < MAP_WIDTH - 1 && map->tiles[y][x + 1] == TILE_WALL);
int wall_w = (x > 0 && map->tiles[y][x - 1] == TILE_WALL);
int is_vertical = (wall_n && wall_s) || (!(wall_e && wall_w) && (wall_e || wall_w));
// Animation progress: 0=fully closed, 1=fully open
float t = 0.0f;
if (map->door_anim_timer[y][x] > 0) {
float progress = 1.0f - (float)map->door_anim_timer[y][x] / DOOR_ANIM_FRAMES;
t = map->door_anim_target[y][x] ? progress : (1.0f - progress);
} else {
t = (map->tiles[y][x] == TILE_DOOR_OPEN) ? 1.0f : 0.0f;
}
if (!tile_drawn)
DrawRectangleRec(dst, floor_color);
if (is_vertical) {
// Vertical door: swings left/right toward approaching wall
int open_dir = map->door_open_from[y][x];
int swing_to_left = 0;
if (open_dir == 2)
swing_to_left = 1; // approached from E, swing W (left)
else if (open_dir == 3)
swing_to_left = 0; // approached from W, swing E (right)
else
swing_to_left = (x % 2 == 0); // deterministic fallback
int closed_x = x * TILE_SIZE + 7; // center
int open_x = swing_to_left ? x * TILE_SIZE + 1 : x * TILE_SIZE + 13;
int px = (int)(closed_x + (open_x - closed_x) * t);
int alpha = (int)(255 * (1.0f - t * 0.45f));
int width = (int)(2 + (1 - t)); // 2px closed, 1px open
Color panel_color = door_color;
panel_color.a = alpha;
DrawRectangle(px, y * TILE_SIZE + 1, width, TILE_SIZE - 2, panel_color);
if (t < 0.5f && light_factor > 0.05f) {
int hx = px + (swing_to_left ? -1 : width + 1);
Color handle_color = door_handle_color;
handle_color.a = alpha;
DrawRectangle(hx, y * TILE_SIZE + 6, 1, 2, handle_color);
}
} else {
// Horizontal door: swings up/down toward approaching wall
int open_dir = map->door_open_from[y][x];
int swing_up = 0;
if (open_dir == 0)
swing_up = 1; // approached from N, swing S (down)
else if (open_dir == 1)
swing_up = 0; // approached from S, swing N (up)
else
swing_up = (y % 2 == 0); // deterministic fallback
int closed_y = y * TILE_SIZE + 7; // center
int open_y = swing_up ? y * TILE_SIZE + 1 : y * TILE_SIZE + 13;
int py = (int)(closed_y + (open_y - closed_y) * t);
int alpha = (int)(255 * (1.0f - t * 0.45f));
int height = (int)(2 + (1 - t)); // 2px closed, 1px open
Color panel_color = door_color;
panel_color.a = alpha;
DrawRectangle(x * TILE_SIZE + 1, py, TILE_SIZE - 2, height, panel_color);
if (t < 0.5f && light_factor > 0.05f) {
int hy = py + (swing_up ? -1 : height + 1);
Color handle_color = door_handle_color;
handle_color.a = alpha;
DrawRectangle(x * TILE_SIZE + 6, hy, 2, 1, handle_color);
}
}
break;
}
// Grid lines
if (DRAW_GRID_LINES && brightness > 0) {
DrawRectangleLines((int)dst.x, (int)dst.y, (int)dst.width, (int)dst.height, (Color){20, 20, 20, 80});
case TILE_DOOR_RUINED:
DrawRectangleRec(dst, (Color){60, 45, 30, 255});
if (light_factor > 0.05f) {
DrawRectangle(x * TILE_SIZE + 1, y * TILE_SIZE + 1, TILE_SIZE - 2, TILE_SIZE - 2, (Color){80, 60, 40, 200});
DrawLine(x * TILE_SIZE + 2, y * TILE_SIZE + 2, x * TILE_SIZE + TILE_SIZE - 2, y * TILE_SIZE + TILE_SIZE - 2,
(Color){120, 90, 60, 255});
DrawLine(x * TILE_SIZE + TILE_SIZE - 2, y * TILE_SIZE + 2, x * TILE_SIZE + 2, y * TILE_SIZE + TILE_SIZE - 2,
(Color){120, 90, 60, 255});
}
break;
}
break;
case TILE_STAIRS:
DrawRectangleRec(dst, stairs_color);
// Make stairs very visible with bright symbol and bounce
{
int bounce = (int)(sinf(GetTime() * 3.0f) * 1.5f);
if (brightness > 0)
DrawText(">", x * TILE_SIZE + 4, y * TILE_SIZE + 1 + bounce, NORM_FONT + 2, (Color){255, 255, 200, 255});
else
DrawText(">", x * TILE_SIZE + 4, y * TILE_SIZE + 1 + bounce, NORM_FONT + 2, (Color){100, 90, 70, 255});
}
break;
case TILE_DOOR_CLOSED:
DrawRectangleRec(dst, door_color);
if (brightness > 0) {
DrawRectangle(x * TILE_SIZE + 2, y * TILE_SIZE + 2, TILE_SIZE - 4, TILE_SIZE - 4, (Color){100, 80, 60, 255});
DrawRectangleLines(x * TILE_SIZE + 2, y * TILE_SIZE + 2, TILE_SIZE - 4, TILE_SIZE - 4,
(Color){60, 50, 40, 255});
DrawText("+", x * TILE_SIZE + 5, y * TILE_SIZE + 1, NORM_FONT, WHITE);
}
break;
case TILE_DOOR_OPEN:
DrawRectangleRec(dst, floor_color);
if (brightness > 0) {
DrawRectangle(x * TILE_SIZE + 2, y * TILE_SIZE + 2, TILE_SIZE - 4, TILE_SIZE - 4, (Color){80, 70, 50, 180});
DrawRectangleLines(x * TILE_SIZE + 2, y * TILE_SIZE + 2, TILE_SIZE - 4, TILE_SIZE - 4,
(Color){60, 50, 40, 200});
DrawText("'", x * TILE_SIZE + 6, y * TILE_SIZE + 2, NORM_FONT, (Color){150, 140, 120, 255});
}
break;
case TILE_DOOR_RUINED:
DrawRectangleRec(dst, (Color){60, 45, 30, 255});
if (brightness > 0) {
DrawRectangle(x * TILE_SIZE + 1, y * TILE_SIZE + 1, TILE_SIZE - 2, TILE_SIZE - 2, (Color){80, 60, 40, 200});
DrawLine(x * TILE_SIZE + 2, y * TILE_SIZE + 2, x * TILE_SIZE + TILE_SIZE - 2, y * TILE_SIZE + TILE_SIZE - 2,
(Color){120, 90, 60, 255});
DrawLine(x * TILE_SIZE + TILE_SIZE - 2, y * TILE_SIZE + 2, x * TILE_SIZE + 2, y * TILE_SIZE + TILE_SIZE - 2,
(Color){120, 90, 60, 255});
}
break;
}
// Wall base shadow: dark strip at bottom of wall tiles
if (map->tiles[y][x] == TILE_WALL && brightness > 0) {
int shadow_alpha = (int)(60.0f * ((float)brightness / 255.0f));
DrawRectangle(dst.x, dst.y + dst.height - 2, dst.width, 2, (Color){0, 0, 0, shadow_alpha});
}
}
}
@ -197,6 +320,13 @@ void render_player(const Player *p, const Tileset *tileset, int frame_counter) {
Rectangle dst = {(float)(p->position.x * TILE_SIZE), (float)(p->position.y * TILE_SIZE), (float)TILE_SIZE,
(float)TILE_SIZE};
// Soft radial glow under the player
int cx = p->position.x * TILE_SIZE + TILE_SIZE / 2;
int cy = p->position.y * TILE_SIZE + TILE_SIZE / 2;
DrawCircle(cx, cy, 14.0f, (Color){255, 220, 100, 20});
DrawCircle(cx, cy, 10.0f, (Color){255, 230, 150, 35});
DrawCircle(cx, cy, 6.0f, (Color){255, 240, 180, 55});
if (tileset != NULL && tileset->finalized) {
int tile_id = p->sprite_tile_id;
switch (p->anim_state) {
@ -412,7 +542,7 @@ void render_ui(const Player *p, const Tileset *tileset, const FontManager *fm) {
// HUD Panel
const int hud_y = MAP_HEIGHT * TILE_SIZE;
const int hud_height = 60;
const Color hud_bg = {25, 20, 15, 255}; // dark parchment
const Color hud_bg = {20, 18, 22, 255}; // dark bluish to separate from map
const Color hud_border = {139, 119, 89, 255}; // bronze/brown border
const Color text_dim = {160, 150, 140, 255}; // dimmed text
const Color text_bright = {240, 230, 220, 255}; // bright text
@ -420,6 +550,8 @@ void render_ui(const Player *p, const Tileset *tileset, const FontManager *fm) {
// Main HUD background with border
Rectangle ui_bg = {0, (float)hud_y, (float)SCREEN_WIDTH, (float)hud_height};
DrawRectangleRec(ui_bg, hud_bg);
// Subtle shadow separating HUD from game view
DrawLine(0, hud_y - 1, SCREEN_WIDTH, hud_y - 1, (Color){0, 0, 0, 80});
DrawRectangleLines(0, hud_y, SCREEN_WIDTH, hud_height, hud_border);
DrawLine(0, hud_y + 1, SCREEN_WIDTH, hud_y + 1, (Color){60, 55, 50, 255});
DrawLine(0, hud_y + hud_height - 2, SCREEN_WIDTH, hud_y + hud_height - 2, (Color){15, 12, 10, 255});

View file

@ -84,6 +84,9 @@
// Sub-tile lighting
#define SUB_TILE_RES 8
#define LIGHT_SIGHT_THRESHOLD 40
#define AMBIENT_LIGHT_FACTOR 0.08f
#define REMEMBERED_LIGHT_FACTOR 0.18f
#define LIGHT_EXPONENT 1.7f
// Player light source parameters
#define PLAYER_LIGHT_RANGE 8
@ -94,5 +97,6 @@
// Visual polish
#define DRAW_GRID_LINES 1
#define DOOR_ANIM_FRAMES 8
#endif // SETTINGS_H