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->light_map, 0, sizeof(map->light_map));
|
||||||
memset(map->remembered, 0, sizeof(map->remembered));
|
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;
|
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))
|
if (!in_bounds(x, y, MAP_WIDTH, MAP_HEIGHT))
|
||||||
return 0;
|
return 0;
|
||||||
return map->tiles[y][x] == TILE_FLOOR || map->tiles[y][x] == TILE_STAIRS || map->tiles[y][x] == TILE_DOOR_OPEN ||
|
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) {
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Place doors at corridor-room junctions
|
// A corridor tile is a narrow floor passage with walls on at least 2 sides.
|
||||||
// DISABLED: Door placement removed per user request
|
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) {
|
static void place_doors(Map *map, Room *rooms, int room_count) {
|
||||||
(void)map;
|
for (int y = 0; y < MAP_HEIGHT; y++) {
|
||||||
(void)rooms;
|
for (int x = 0; x < MAP_WIDTH; x++) {
|
||||||
(void)room_count;
|
if (map->tiles[y][x] != TILE_FLOOR)
|
||||||
// No-op: doors disabled
|
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
|
// 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)
|
if (x == x2 && y == y2)
|
||||||
return 1;
|
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;
|
return 0;
|
||||||
|
|
||||||
int e2 = 2 * err;
|
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 ty = 0; ty < MAP_HEIGHT; ty++) {
|
||||||
for (int tx = 0; tx < MAP_WIDTH; tx++) {
|
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;
|
map->remembered[ty][tx] = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,9 @@ typedef struct {
|
||||||
int room_count;
|
int room_count;
|
||||||
unsigned char light_map[MAP_HEIGHT * SUB_TILE_RES][MAP_WIDTH * SUB_TILE_RES];
|
unsigned char light_map[MAP_HEIGHT * SUB_TILE_RES][MAP_WIDTH * SUB_TILE_RES];
|
||||||
unsigned char remembered[MAP_HEIGHT][MAP_WIDTH];
|
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;
|
} Map;
|
||||||
|
|
||||||
// Dungeon
|
// Dungeon
|
||||||
|
|
|
||||||
36
src/main.c
36
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
|
// 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 p_light = {gs->player.position.x, gs->player.position.y, PLAYER_LIGHT_INTENSITY, PLAYER_LIGHT_RANGE};
|
||||||
LightSource srcs[1 + 32];
|
LightSource srcs[1 + 32];
|
||||||
|
|
@ -277,6 +290,18 @@ static int handle_stun_turn(GameState *gs) {
|
||||||
if (gs->game_over)
|
if (gs->game_over)
|
||||||
return 1;
|
return 1;
|
||||||
enemy_update_all(gs->enemies, gs->enemy_count, &gs->player, &gs->map);
|
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 l = {gs->player.position.x, gs->player.position.y, PLAYER_LIGHT_INTENSITY, PLAYER_LIGHT_RANGE};
|
||||||
LightSource s[1 + 32];
|
LightSource s[1 + 32];
|
||||||
|
|
@ -639,6 +664,17 @@ static void game_loop(unsigned int run_seed, FontManager *fm) {
|
||||||
if (gs.slash_timer > 0)
|
if (gs.slash_timer > 0)
|
||||||
gs.slash_timer--;
|
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
|
// Update player animation
|
||||||
if (gs.player.anim_timer > 0) {
|
if (gs.player.anim_timer > 0) {
|
||||||
gs.player.anim_timer--;
|
gs.player.anim_timer--;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "movement.h"
|
#include "movement.h"
|
||||||
#include "enemy.h"
|
#include "enemy.h"
|
||||||
#include "map/map.h"
|
#include "map/map.h"
|
||||||
|
#include "map/utils.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
// Check if position is occupied by player
|
// 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);
|
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,
|
MoveResult try_move_entity(Vec2 *p, Vec2 direction, Map *map, Player *player, Enemy *enemies, int enemy_count,
|
||||||
bool moving_is_player) {
|
bool moving_is_player) {
|
||||||
int new_x = p->x + direction.x;
|
int new_x = p->x + direction.x;
|
||||||
int new_y = p->y + direction.y;
|
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))
|
if (!is_floor(map, new_x, new_y))
|
||||||
return MOVE_RESULT_BLOCKED_WALL;
|
return MOVE_RESULT_BLOCKED_WALL;
|
||||||
|
|
||||||
|
|
|
||||||
202
src/render.c
202
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);
|
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) {
|
void render_map(const Map *map, const Tileset *tileset) {
|
||||||
for (int y = 0; y < MAP_HEIGHT; y++) {
|
for (int y = 0; y < MAP_HEIGHT; y++) {
|
||||||
for (int x = 0; x < MAP_WIDTH; x++) {
|
for (int x = 0; x < MAP_WIDTH; x++) {
|
||||||
|
|
@ -78,6 +115,11 @@ void render_map(const Map *map, const Tileset *tileset) {
|
||||||
continue;
|
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;
|
int tile_id = -1;
|
||||||
switch (map->tiles[y][x]) {
|
switch (map->tiles[y][x]) {
|
||||||
case TILE_WALL:
|
case TILE_WALL:
|
||||||
|
|
@ -100,26 +142,44 @@ void render_map(const Map *map, const Tileset *tileset) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tile_id >= 0 && tileset != NULL && tileset->finalized) {
|
int is_door = (map->tiles[y][x] == TILE_DOOR_CLOSED || map->tiles[y][x] == TILE_DOOR_OPEN);
|
||||||
Rectangle src = tileset_get_region(tileset, tile_id);
|
int tile_drawn = 0;
|
||||||
if (src.width > 0) {
|
|
||||||
Color tint;
|
// Draw floor underneath doors using tileset if available, so the
|
||||||
if (brightness > 0) {
|
// floor matches adjacent tiles
|
||||||
float lit = (float)brightness / 255.0f;
|
if (is_door && tileset != NULL && tileset->finalized) {
|
||||||
tint = (Color){(unsigned char)(128 + (int)(127.0f * lit)), (unsigned char)(128 + (int)(127.0f * lit)),
|
int floor_id = TILE_FLOOR_0 + ((x * 7 + y * 13) % 4);
|
||||||
(unsigned char)(128 + (int)(127.0f * lit)), 255};
|
Rectangle floor_src = tileset_get_region(tileset, floor_id);
|
||||||
} else {
|
if (floor_src.width > 0) {
|
||||||
tint = (Color){128, 128, 128, 255};
|
int fv = (int)(255.0f * light_factor);
|
||||||
}
|
if (fv > 255)
|
||||||
DrawTexturePro(tileset->atlas, src, dst, (Vector2){0, 0}, 0.0f, tint);
|
fv = 255;
|
||||||
continue;
|
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};
|
if (tile_id >= 0 && tileset != NULL && tileset->finalized && !is_door) {
|
||||||
Color floor_color = brightness > 0 ? BLACK : (Color){15, 15, 20, 255};
|
Rectangle src = tileset_get_region(tileset, tile_id);
|
||||||
Color stairs_color = brightness > 0 ? (Color){180, 160, 100, 255} : (Color){60, 55, 50, 255};
|
if (src.width > 0) {
|
||||||
Color door_color = brightness > 0 ? (Color){139, 119, 89, 255} : (Color){60, 55, 50, 255};
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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]) {
|
switch (map->tiles[y][x]) {
|
||||||
case TILE_WALL:
|
case TILE_WALL:
|
||||||
|
|
@ -139,13 +199,13 @@ void render_map(const Map *map, const Tileset *tileset) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (is_adjacent_to_stairs && brightness > 0) {
|
if (is_adjacent_to_stairs && light_factor > 0.05f) {
|
||||||
int flicker = (int)(sinf(GetTime() * 5.0f) * 15.0f);
|
int flicker = (int)(sinf(GetTime() * 5.0f) * 15.0f);
|
||||||
DrawRectangleRec(dst, (Color){40 + flicker, 25, 10, 60});
|
DrawRectangleRec(dst, (Color){40 + flicker, 25, 10, 60});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Grid lines
|
// Grid lines
|
||||||
if (DRAW_GRID_LINES && brightness > 0) {
|
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});
|
DrawRectangleLines((int)dst.x, (int)dst.y, (int)dst.width, (int)dst.height, (Color){20, 20, 20, 80});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -154,33 +214,89 @@ void render_map(const Map *map, const Tileset *tileset) {
|
||||||
// Make stairs very visible with bright symbol and bounce
|
// Make stairs very visible with bright symbol and bounce
|
||||||
{
|
{
|
||||||
int bounce = (int)(sinf(GetTime() * 3.0f) * 1.5f);
|
int bounce = (int)(sinf(GetTime() * 3.0f) * 1.5f);
|
||||||
if (brightness > 0)
|
if (light_factor > 0.05f)
|
||||||
DrawText(">", x * TILE_SIZE + 4, y * TILE_SIZE + 1 + bounce, NORM_FONT + 2, (Color){255, 255, 200, 255});
|
DrawText(">", x * TILE_SIZE + 4, y * TILE_SIZE + 1 + bounce, NORM_FONT + 2, (Color){255, 255, 200, 255});
|
||||||
else
|
else
|
||||||
DrawText(">", x * TILE_SIZE + 4, y * TILE_SIZE + 1 + bounce, NORM_FONT + 2, (Color){100, 90, 70, 255});
|
DrawText(">", x * TILE_SIZE + 4, y * TILE_SIZE + 1 + bounce, NORM_FONT + 2, (Color){100, 90, 70, 255});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TILE_DOOR_CLOSED:
|
case TILE_DOOR_CLOSED:
|
||||||
DrawRectangleRec(dst, door_color);
|
case TILE_DOOR_OPEN: {
|
||||||
if (brightness > 0) {
|
// Determine door orientation by adjacent walls
|
||||||
DrawRectangle(x * TILE_SIZE + 2, y * TILE_SIZE + 2, TILE_SIZE - 4, TILE_SIZE - 4, (Color){100, 80, 60, 255});
|
int wall_n = (y > 0 && map->tiles[y - 1][x] == TILE_WALL);
|
||||||
DrawRectangleLines(x * TILE_SIZE + 2, y * TILE_SIZE + 2, TILE_SIZE - 4, TILE_SIZE - 4,
|
int wall_s = (y < MAP_HEIGHT - 1 && map->tiles[y + 1][x] == TILE_WALL);
|
||||||
(Color){60, 50, 40, 255});
|
int wall_e = (x < MAP_WIDTH - 1 && map->tiles[y][x + 1] == TILE_WALL);
|
||||||
DrawText("+", x * TILE_SIZE + 5, y * TILE_SIZE + 1, NORM_FONT, WHITE);
|
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;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case TILE_DOOR_OPEN:
|
if (!tile_drawn)
|
||||||
DrawRectangleRec(dst, floor_color);
|
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});
|
if (is_vertical) {
|
||||||
DrawRectangleLines(x * TILE_SIZE + 2, y * TILE_SIZE + 2, TILE_SIZE - 4, TILE_SIZE - 4,
|
// Vertical door: swings left/right toward approaching wall
|
||||||
(Color){60, 50, 40, 200});
|
int open_dir = map->door_open_from[y][x];
|
||||||
DrawText("'", x * TILE_SIZE + 6, y * TILE_SIZE + 2, NORM_FONT, (Color){150, 140, 120, 255});
|
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;
|
break;
|
||||||
|
}
|
||||||
case TILE_DOOR_RUINED:
|
case TILE_DOOR_RUINED:
|
||||||
DrawRectangleRec(dst, (Color){60, 45, 30, 255});
|
DrawRectangleRec(dst, (Color){60, 45, 30, 255});
|
||||||
if (brightness > 0) {
|
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});
|
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,
|
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});
|
(Color){120, 90, 60, 255});
|
||||||
|
|
@ -190,6 +306,13 @@ void render_map(const Map *map, const Tileset *tileset) {
|
||||||
break;
|
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,
|
Rectangle dst = {(float)(p->position.x * TILE_SIZE), (float)(p->position.y * TILE_SIZE), (float)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) {
|
if (tileset != NULL && tileset->finalized) {
|
||||||
int tile_id = p->sprite_tile_id;
|
int tile_id = p->sprite_tile_id;
|
||||||
switch (p->anim_state) {
|
switch (p->anim_state) {
|
||||||
|
|
@ -412,7 +542,7 @@ void render_ui(const Player *p, const Tileset *tileset, const FontManager *fm) {
|
||||||
// HUD Panel
|
// HUD Panel
|
||||||
const int hud_y = MAP_HEIGHT * TILE_SIZE;
|
const int hud_y = MAP_HEIGHT * TILE_SIZE;
|
||||||
const int hud_height = 60;
|
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 hud_border = {139, 119, 89, 255}; // bronze/brown border
|
||||||
const Color text_dim = {160, 150, 140, 255}; // dimmed text
|
const Color text_dim = {160, 150, 140, 255}; // dimmed text
|
||||||
const Color text_bright = {240, 230, 220, 255}; // bright 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
|
// Main HUD background with border
|
||||||
Rectangle ui_bg = {0, (float)hud_y, (float)SCREEN_WIDTH, (float)hud_height};
|
Rectangle ui_bg = {0, (float)hud_y, (float)SCREEN_WIDTH, (float)hud_height};
|
||||||
DrawRectangleRec(ui_bg, hud_bg);
|
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);
|
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 + 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});
|
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
|
// Sub-tile lighting
|
||||||
#define SUB_TILE_RES 8
|
#define SUB_TILE_RES 8
|
||||||
#define LIGHT_SIGHT_THRESHOLD 40
|
#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
|
// Player light source parameters
|
||||||
#define PLAYER_LIGHT_RANGE 8
|
#define PLAYER_LIGHT_RANGE 8
|
||||||
|
|
@ -94,5 +97,6 @@
|
||||||
|
|
||||||
// Visual polish
|
// Visual polish
|
||||||
#define DRAW_GRID_LINES 1
|
#define DRAW_GRID_LINES 1
|
||||||
|
#define DOOR_ANIM_FRAMES 8
|
||||||
|
|
||||||
#endif // SETTINGS_H
|
#endif // SETTINGS_H
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue