#include "game/map/map.hpp" #include #include #include #include "core/rendering/screen.hpp" #include "core/resources/resource_cache.hpp" #include "external/fkyaml_node.hpp" #include "utils/defines.hpp" namespace { // AABB amb inequitat estricta: dos rects que es toquen exactament (la // cantonada o una vora al píxel) NO es consideren intersecció. SDL3 // SDL_HasRectIntersectionFloat tracta el toc com a intersecció, cosa // que bloqueja el moviment als encreuaments del nostre laberint. auto overlapStrict(const SDL_FRect& a, const SDL_FRect& b) -> bool { return (a.x + a.w) > b.x && (b.x + b.w) > a.x && (a.y + a.h) > b.y && (b.y + b.h) > a.y; } } // namespace void Map::loadFrom(const std::string& yaml_path) { std::ifstream file(yaml_path); if (!file.is_open()) { throw std::runtime_error("Map: no s'ha pogut obrir " + yaml_path); } auto yaml = fkyaml::node::deserialize(file); if (!yaml.contains("geometry")) { throw std::runtime_error("Map: manca bloc `geometry:` a " + yaml_path); } const auto& g = yaml["geometry"]; grid_cols = g["grid_cols"].get_value(); grid_rows = g["grid_rows"].get_value(); sarc_w = g["sarc_w"].get_value(); sarc_h = g["sarc_h"].get_value(); pitch_x = g["pitch_x"].get_value(); pitch_y = g["pitch_y"].get_value(); hud_h = g["hud_h"].get_value(); back_wall_h = g["back_wall_h"].get_value(); side_wall_w = g["side_wall_w"].get_value(); // Derivats: el passadís és el sobrant del pitch sobre el sarcòfag, i // l'exterior reutilitza la mateixa amplada per mantenir el pas lliure. corridor_w = pitch_x - sarc_w; corridor_h = pitch_y - sarc_h; outer_corridor = corridor_w; board_w = ((grid_cols - 1) * pitch_x) + sarc_w; board_h = ((grid_rows - 1) * pitch_y) + sarc_h; origin_x = side_wall_w + outer_corridor; origin_y = hud_h + back_wall_h + outer_corridor; playfield_x = side_wall_w; playfield_y = hud_h + back_wall_h; playfield_w = board_w + (2 * outer_corridor); playfield_h = board_h + (2 * outer_corridor); std::cout << "Map: geometria " << grid_cols << "x" << grid_rows << " sarcs " << sarc_w << "x" << sarc_h << " pitch " << pitch_x << "x" << pitch_y << " des de " << yaml_path << '\n'; } auto Map::getSarcRowBottomY(int row) -> int { return origin_y + (row * pitch_y) + sarc_h; } auto Map::getSarcRect(int col, int row) -> SDL_FRect { return { .x = static_cast(origin_x + (col * pitch_x)), .y = static_cast(origin_y + (row * pitch_y)), .w = static_cast(sarc_w), .h = static_cast(sarc_h), }; } void Map::renderBackground() { auto& screen = Screen::get(); const auto& palette = Resource::Cache::get().getPalette("ui"); // Comencem pintant el sòl a tota la pantalla; després tapem marcador i // murs. Els rectangles dels murs s'encavalquen a les cantonades: no // importa perquè són del mateix color. screen.fillRect(0, 0, GameCanvas::WIDTH, GameCanvas::HEIGHT, palette[FLOOR]); // Marcador (dalt). screen.fillRect(0, 0, GameCanvas::WIDTH, hud_h, palette[HUD_BG]); // Mur posterior: banda horitzontal sota el marcador. screen.fillRect(0, hud_h, GameCanvas::WIDTH, back_wall_h, palette[WALL]); // Murs laterals (des del marcador fins al final). screen.fillRect(0, hud_h, side_wall_w, GameCanvas::HEIGHT - hud_h, palette[WALL]); screen.fillRect(GameCanvas::WIDTH - side_wall_w, hud_h, side_wall_w, GameCanvas::HEIGHT - hud_h, palette[WALL]); // Mur frontal: de la vora inferior del passadís de baix fins al final del // canvas. Assumeix els 40 px extra de 320×240 i fa de primer pla. const int FRONT_WALL_Y = origin_y + board_h + outer_corridor; screen.fillRect(0, FRONT_WALL_Y, GameCanvas::WIDTH, GameCanvas::HEIGHT - FRONT_WALL_Y, palette[WALL]); } void Map::renderSarcsRow(int row) { auto& screen = Screen::get(); const auto& palette = Resource::Cache::get().getPalette("ui"); const int Y = origin_y + (row * pitch_y); for (int col = 0; col < grid_cols; ++col) { const int X = origin_x + (col * pitch_x); screen.fillRect(X, Y, sarc_w, sarc_h, palette[SARC]); } } auto Map::collides(const SDL_FRect& base_box) -> bool { // Recorregut trivial dels sarcòfags. Amb una graella petita no val la // pena indexar per col/row prèviament; si algun dia creix, es pot // calcular el rang de columnes/files candidates a partir de base_box. for (int row = 0; row < grid_rows; ++row) { for (int col = 0; col < grid_cols; ++col) { if (overlapStrict(base_box, getSarcRect(col, row))) { return true; } } } return false; }