diff --git a/libs/map/map.c b/libs/map/map.c index 911d487..1e93d07 100644 --- a/libs/map/map.c +++ b/libs/map/map.c @@ -2,6 +2,7 @@ #include "rng/rng.h" #include "settings.h" #include "utils.h" +#include #include #include @@ -20,7 +21,8 @@ void map_init(Map *map) { 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; + 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; } void get_room_center(Room *room, int *cx, int *cy) { @@ -109,6 +111,32 @@ static int generate_rooms(Map *map, Room *rooms, int floor) { return room_count; } +// Check if a tile is at a room boundary (adjacent to wall but inside room) +static int is_room_boundary(Map *map, int x, int y) { + // Must be floor + if (map->tiles[y][x] != TILE_FLOOR) + return 0; + // Must have at least one adjacent wall + if (in_bounds(x - 1, y, MAP_WIDTH, MAP_HEIGHT) && map->tiles[y][x - 1] == TILE_WALL) + return 1; + if (in_bounds(x + 1, y, MAP_WIDTH, MAP_HEIGHT) && map->tiles[y][x + 1] == TILE_WALL) + return 1; + if (in_bounds(x, y - 1, MAP_WIDTH, MAP_HEIGHT) && map->tiles[y - 1][x] == TILE_WALL) + return 1; + if (in_bounds(x, y + 1, MAP_WIDTH, MAP_HEIGHT) && map->tiles[y + 1][x] == TILE_WALL) + return 1; + return 0; +} + +// Place doors at corridor-room junctions +// DISABLED: Door placement removed per user request +static void place_doors(Map *map, Room *rooms, int room_count) { + (void)map; + (void)rooms; + (void)room_count; + // No-op: doors disabled +} + // Connect all rooms with corridors static void connect_rooms(Map *map, Room *rooms, int room_count) { for (int i = 0; i < room_count - 1; i++) { @@ -125,6 +153,9 @@ static void connect_rooms(Map *map, Room *rooms, int room_count) { carve_h_corridor(map, cx1, cx2, cy2); } } + + // Place doors after all corridors are carved + place_doors(map, rooms, room_count); } // Place stairs in the last room (furthest from start) @@ -134,8 +165,43 @@ static void place_stairs(Map *map, Room *rooms, int room_count) { int cx, cy; get_room_center(last_room, &cx, &cy); - // Place stairs at center of last room + // Ensure stairs are placed on a floor tile, not a wall + if (in_bounds(cx, cy, MAP_WIDTH, MAP_HEIGHT) && map->tiles[cy][cx] == TILE_FLOOR) { + map->tiles[cy][cx] = TILE_STAIRS; + return; + } + + // 3x3 fallback + for (int dy = -1; dy <= 1; dy++) { + for (int dx = -1; dx <= 1; dx++) { + int nx = cx + dx; + int ny = cy + dy; + if (in_bounds(nx, ny, MAP_WIDTH, MAP_HEIGHT) && map->tiles[ny][nx] == TILE_FLOOR) { + map->tiles[ny][nx] = TILE_STAIRS; + return; + } + } + } + + // Expanded fallback: scan the room for any floor tile + for (int dy = 0; dy < last_room->h; dy++) { + for (int dx = 0; dx < last_room->w; dx++) { + int nx = last_room->x + dx; + int ny = last_room->y + dy; + if (in_bounds(nx, ny, MAP_WIDTH, MAP_HEIGHT) && map->tiles[ny][nx] == TILE_FLOOR) { + map->tiles[ny][nx] = TILE_STAIRS; + return; + } + } + } + + // Final fallback: force the center tile to stairs regardless of type + fprintf(stderr, "Warning: No floor tile found for stairs at room center (%d, %d). Forcing stairs placement.\n", cx, + cy); if (in_bounds(cx, cy, MAP_WIDTH, MAP_HEIGHT)) { + if (map->tiles[cy][cx] == TILE_WALL) { + map->tiles[cy][cx] = TILE_FLOOR; + } map->tiles[cy][cx] = TILE_STAIRS; } } diff --git a/src/common.h b/src/common.h index fe87edf..f474781 100644 --- a/src/common.h +++ b/src/common.h @@ -9,7 +9,7 @@ typedef struct { } Vec2; // Tile types -typedef enum { TILE_WALL, TILE_FLOOR, TILE_STAIRS } TileType; +typedef enum { TILE_WALL, TILE_FLOOR, TILE_STAIRS, TILE_DOOR_CLOSED, TILE_DOOR_OPEN, TILE_DOOR_RUINED } TileType; // Status effect types typedef enum { EFFECT_NONE, EFFECT_POISON, EFFECT_STUN, EFFECT_BLEED, EFFECT_WEAKEN, EFFECT_BURN } StatusEffectType; @@ -49,69 +49,165 @@ typedef struct { typedef enum { ITEM_POTION, ITEM_WEAPON, ITEM_ARMOR } ItemType; // Item + typedef struct { int x, y; + ItemType type; + int power; + int floor; + int picked_up; + DamageClass dmg_class; + int crit_chance; + int crit_multiplier; + int status_chance; + + // rendering + + int sprite_tile_id; // tile ID for rendering + } Item; +// Player animation states +typedef enum { PLAYER_ANIM_IDLE, PLAYER_ANIM_WALK, PLAYER_ANIM_ATTACK } PlayerAnimState; + // Player + typedef struct { Vec2 position; + int hp, max_hp; + int attack; + int defense; + int floor; + int step_count; - int speed; // actions per 100 ticks (100 = 1 action per turn) + + int speed; // actions per 100 ticks (100 = 1 action per turn) + int cooldown; // countdown to next action (0 = can act) - int dodge; // dodge chance percentage - int block; // flat damage reduction on successful block roll + + int dodge; // dodge chance percentage + + int block; // flat damage reduction on successful block roll + Item equipped_weapon; + int has_weapon; + Item equipped_armor; + int has_armor; + Item inventory[MAX_INVENTORY]; + int inventory_count; + // status effects + StatusEffect effects[MAX_EFFECTS]; + int effect_count; + + // animation + + PlayerAnimState anim_state; + + int anim_frame; // current animation frame + + int anim_timer; // frames until next frame + + int facing_right; // 1 = facing right, 0 = facing left + + // rendering + + int sprite_tile_id; // tile ID for rendering + + // visual effects + + int flash_timer; // damage flash frames remaining + } Player; // Enemy types typedef enum { ENEMY_GOBLIN, ENEMY_SKELETON, ENEMY_ORC } EnemyType; +// Enemy animation states +typedef enum { ENEMY_ANIM_IDLE, ENEMY_ANIM_WALK, ENEMY_ANIM_ATTACK } EnemyAnimState; + // Enemy + typedef struct { Vec2 position; + int hp; + int max_hp; + int attack; + int alive; + EnemyType type; - int speed; // actions per 100 ticks + + int speed; // actions per 100 ticks + int cooldown; // countdown to next action - int dodge; // dodge chance percentage - int block; // flat damage reduction + + int dodge; // dodge chance percentage + + int block; // flat damage reduction + int resistance[NUM_DMG_CLASSES]; + DamageClass dmg_class; + int status_chance; + int crit_chance; // crit chance percentage (0-100) - int crit_mult; // crit damage multiplier percentage (e.g. 150 = 1.5x) + + int crit_mult; // crit damage multiplier percentage (e.g. 150 = 1.5x) + // vision + int vision_range; - int alert; // 1 = aware of player, searching + + int alert; // 1 = aware of player, searching + int last_known_x; // last position where enemy saw player + int last_known_y; + // status effects + StatusEffect effects[MAX_EFFECTS]; + int effect_count; + + // animation + + EnemyAnimState anim_state; + + int anim_frame; // current animation frame + + int anim_timer; // frames until next frame + + int facing_right; // 1 = facing right, 0 = facing left + + // rendering + + int sprite_tile_id; // tile ID for rendering + } Enemy; diff --git a/src/settings.h b/src/settings.h index 3408a13..2b700ac 100644 --- a/src/settings.h +++ b/src/settings.h @@ -83,4 +83,7 @@ #define ENEMY_VIEW_RANGE 6 #define ENEMY_PATROL_MOVE_CHANCE 30 +// Visual polish +#define DRAW_GRID_LINES 1 + #endif // SETTINGS_H