Files
aee-arcade/source/game/map/map.cpp
T
JailDesigner e6c7f0dcfb HC-03: geometria del mapa traslladada de map.hpp al YAML de la piràmide
El bloc `geometry:` de pyramid.yaml porta les 9 constants base (grid, sarc,
pitch, HUD, murs); els derivats (corridor, board, origin, playfield) es
recalculen al `Map::loadFrom()`. `constexpr` passa a `const` on calia i
els membres estàtics adopten snake_case. Desbloqueja la iteració
"pirámide a pirámide" sense recompilar.
2026-04-20 17:32:56 +02:00

127 lines
4.8 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "game/map/map.hpp"
#include <fstream>
#include <iostream>
#include <stdexcept>
#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<int>();
grid_rows = g["grid_rows"].get_value<int>();
sarc_w = g["sarc_w"].get_value<int>();
sarc_h = g["sarc_h"].get_value<int>();
pitch_x = g["pitch_x"].get_value<int>();
pitch_y = g["pitch_y"].get_value<int>();
hud_h = g["hud_h"].get_value<int>();
back_wall_h = g["back_wall_h"].get_value<int>();
side_wall_w = g["side_wall_w"].get_value<int>();
// 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<float>(origin_x + (col * pitch_x)),
.y = static_cast<float>(origin_y + (row * pitch_y)),
.w = static_cast<float>(sarc_w),
.h = static_cast<float>(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;
}