#pragma once #include #include #include #include // Mapa d'una piràmide: sala rectangular amb marcador superior, muros pels // quatre costats i graella de sarcòfags envoltada per un passadís a cada // cantó. La geometria (graella, pitch, amplada de murs) es carrega des del // bloc `geometry:` del YAML de la piràmide — així cada sala pot tenir la // seua forma sense recompilar. // // `Map` és un namespace (no una classe) perquè no té estat d'instància: // els membres viuen com a globals del namespace i els callers hi accedeixen // amb `Map::grid_cols`, `Map::collides(...)`, etc. El canvi respecte a // l'anterior `class Map { static ... };` és purament sintàctic. // // `MapInfo` (POD trivialment copiable) empaqueta la geometria + dos // punters a funció de col·lisió per injectar-la a entitats que han // d'evitar conèixer el namespace `Map::` directament. Construïda via // `Map::makeInfo()` després de `loadFrom`. Avui només l'usa el Player // (COUP-02 a docs/audit-game.md); momies, roques i `loadFromLevel` // continuen llegint `Map::*` directament — la nota original justifica // que mentre hi hagi un Map global actiu, l'acoblament és acceptable. namespace Map { // Geometria de la graella (carregada de pyramid.yaml > geometry). inline int grid_cols = 0; inline int grid_rows = 0; inline int sarc_w = 0; inline int sarc_h = 0; inline int pitch_x = 0; inline int pitch_y = 0; // Metadades del tilemap (carregades de l'arrel del YAML: `tile_size:` i // `origin: [x, y]`). Són necessàries per a consumidors externs com el // sistema d'entitats, que tradueix coordenades píxel ↔ tile amb // tile_col = floor((px - tilemap_origin_x) / tile_size) // El grid és 16-alineat respecte a l'origen del tilemap (no del canvas); // origen habitual a pyramid.yaml és [-8, 0] (overscan horitzontal CPC). inline int tile_size = 0; inline int tilemap_origin_x = 0; inline int tilemap_origin_y = 0; // Cromat de la sala: marcador (HUD), mur posterior (dalt) i murs // laterals. El mur frontal es deriva del passadís exterior inferior. inline int hud_h = 0; inline int back_wall_h = 0; inline int side_wall_w = 0; // Derivats de la geometria base: el pas entre sarcòfags ha de cabre // exactament el jugador, i el mateix passadís s'usa a l'exterior. inline int corridor_w = 0; inline int corridor_h = 0; inline int outer_corridor = 0; inline int board_w = 0; inline int board_h = 0; inline int origin_x = 0; inline int origin_y = 0; inline int playfield_x = 0; inline int playfield_y = 0; inline int playfield_w = 0; inline int playfield_h = 0; // Porta de sortida del nivell. La porta sempre viu al mur trasser // (`wall_north`); només la columna varia per nivell. `door_col` és la // columna central transitable: el sprite 48×32 es dibuixa centrat // sobre aquesta columna i els 16 px centrals (just `door_col`) són // l'únic forat del mur trasser per on el jugador surt. Derivat de // `geometry.door_col` al YAML. inline int door_col = 0; inline int door_corridor_x = 0; inline int door_corridor_y = 0; inline int door_corridor_w = 0; inline int door_corridor_h = 0; // Estat del passadís: si la porta està oberta, el `door_corridor` no // bloqueja el moviment del jugador (vegeu `Map::collides`). Qui muta // aquest flag és l'entitat `Door` quan transita CLOSED → OPEN; el // reset el porta `Map::loadFrom` (cada càrrega de nivell el torna a // posar a false). inline bool door_open = false; // Carrega el bloc `geometry:` del YAML i recalcula tots els derivats. // Ha de cridar-se abans d'instanciar `Player` o consultar `collides()`: // GameScene ho fa al ctor. void loadFrom(const std::string& yaml_path); // Rectangle ocupat pel sarcòfag a la posició (col, row) de la graella. [[nodiscard]] auto getSarcRect(int col, int row) -> SDL_FRect; // Mapa de colisions (separat del mapa de dibuix): retorna true si el // rectangle `base_box` (la base del jugador, per exemple) s'encavalca // amb cap sarcòfag, el passadís de la porta tancada, o cap entitat // sòlida genèrica registrada via `setEntityCollider`. [[nodiscard]] auto collides(const SDL_FRect& base_box) -> bool; // Registre d'un predicat extern que `collides` consulta com a sobre- // capa: l'usa `EntityManager` per a entitats amb `blocksMovement()` // (debris, columnes...) que no estan a la graella fixa de sarcòfags. // Passa `nullptr` per netejar. Reset implícit a `loadFrom` (cada // càrrega de nivell sol portar un manager nou). void setEntityCollider(std::function fn); // true si la caixa `box` queda completament dins del playfield (zona // interior, sense tocar els murs exteriors). El Player ja té clamp // propi que aplica el mateix constrenyiment; això serveix a les // entitats dinàmiques per validar posicions candidates del seu step. [[nodiscard]] auto insidePlayfield(const SDL_FRect& box) -> bool; // true si el tile (col, row) — coordenades del tilemap, amb origen // `tilemap_origin_*` — és "sòl transitable" i per tant pintable: cau // completament dins del playfield i no s'encavalca cap sarcòfag. Ho // usa el `PaintLayer` per filtrar quines cel·les accepten pintat. [[nodiscard]] auto isPaintable(int col, int row) -> bool; // Converteix una coordenada píxel del canvas en (col, row) del // tilemap. Assumeix `tile_size > 0`; si la coordenada cau fora del // playfield, retorna el tile encara que sigui negatiu o estigui fora // de rang — el caller valida si li cal. El càlcul és el típic // `floor((p - origin) / tile_size)` que apareixia en diversos llocs; // factoritzat aquí per coherència. Consumidors que tinguen un // snapshot de `Map::Info` (p.ex. el Player, decoupling COUP-02) no // han d'usar aquesta funció — han de seguir treballant amb el // snapshot per no re-acoblar-se al namespace global. [[nodiscard]] auto pixelToTile(float px, float py) -> std::pair; // Snapshot plà de la geometria del mapa + punters a `collides` / // `insidePlayfield`. Pensat per ser passat per valor (POD petit, // trivialment copiable). Un cop construïda, llegeix els valors // congelats — si `Map::loadFrom` es torna a cridar amb un mapa // diferent, cal demanar un MapInfo nou. struct Info { int playfield_x{0}; int playfield_y{0}; int playfield_w{0}; int playfield_h{0}; int pitch_x{0}; int pitch_y{0}; int grid_cols{0}; int grid_rows{0}; int tile_size{0}; int tilemap_origin_x{0}; int tilemap_origin_y{0}; // Passadís de sortida (forat del mur trasser a la columna de la // porta). El Player l'usa des del clamp per permetre pujar al // strip del mur quan la base box queda dins del rang X. int door_corridor_x{0}; int door_corridor_y{0}; int door_corridor_w{0}; int door_corridor_h{0}; bool (*collides)(const SDL_FRect&){nullptr}; bool (*inside_playfield)(const SDL_FRect&){nullptr}; }; [[nodiscard]] auto makeInfo() -> Info; } // namespace Map