various: fixup doors
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: Id81f32d86f70a7df99c2ad3d478646416a6a6964
This commit is contained in:
parent
00b3798ae0
commit
d8b49054d5
6 changed files with 345 additions and 87 deletions
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
38
src/main.c
38
src/main.c
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
290
src/render.c
290
src/render.c
|
|
@ -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});
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue