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

@ -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});