diff --git a/data/entities/door/door.yaml b/data/entities/door/door.yaml index fabd3d8..0b041e0 100644 --- a/data/entities/door/door.yaml +++ b/data/entities/door/door.yaml @@ -5,6 +5,14 @@ sprite: size: [48, 32] tile_size: [3, 2] collision: + # Caixa de col·lisió: 16×24 centrada al sprite 48×32. Coincideix amb el + # tile central transitable (els 16 px del passadís de sortida) i es + # restringeix a la franja del mur trasser (24 px d'alçada). Així + # `checkPlayerCollisions` només dispara `onPlayerContact` quan el + # jugador entra al forat real, no als marcs flanquejants ni als 8 px + # superiors que queden tapats pel HUD. + size: [16, 24] + offset: [16, 8] # Porta = mur infranquejable. Igual que el sarc, alçada efectiva # infinita per impedir que un jugador la salte per damunt. height: 999 diff --git a/data/maps/pyramid.yaml b/data/maps/pyramid.yaml index 6111eab..602b4da 100644 --- a/data/maps/pyramid.yaml +++ b/data/maps/pyramid.yaml @@ -129,7 +129,9 @@ entities: - type: sarcophagus sarc_position: [3, 4] - type: door - position: [9, 1] + # Sense `position`: la porta sempre viu al mur trasser. La columna + # transitable la dicta `geometry.door_col`; el render i la caixa + # de col·lisió es deriven d'aquesta dada al loadFromLevel. - type: mummy position: [10, 7] - type: mummy diff --git a/source/game/entities/entity_manager_loadlevel.cpp b/source/game/entities/entity_manager_loadlevel.cpp index c4b1cc7..ee0f0ef 100644 --- a/source/game/entities/entity_manager_loadlevel.cpp +++ b/source/game/entities/entity_manager_loadlevel.cpp @@ -291,6 +291,34 @@ namespace { return true; } + // Col·loca la porta al mur trasser segons `Map::door_col`. La porta + // viu sempre al `wall_north`; només la columna varia per nivell. El + // sprite (típicament 48×32) es centra horitzontalment sobre la + // columna transitable i s'alinea verticalment de manera que la seua + // vora inferior coincideixi amb la base del strip del mur trasser + // (= `playfield_y`). Amb sprite 48×32 i `back_wall_h = 24` això vol + // dir que els 8 px superiors del sprite queden dins del HUD i hi + // queden tapats — comportament intencional. + auto placeDoor(EntityManager& manager, std::unique_ptr entity, const SpriteInfo& sprite) -> bool { + if (Map::door_col <= 0 || Map::tile_size <= 0) { + std::cerr << "EntityManager: door requereix `geometry.door_col` definit al mapa\n"; + return false; + } + const float CENTER_X = static_cast((Map::door_col * Map::tile_size) + Map::tilemap_origin_x) + (static_cast(Map::tile_size) / 2.0F); + const SDL_FRect RENDER{ + .x = CENTER_X - (sprite.width / 2.0F), + .y = static_cast(Map::playfield_y) - sprite.height, + .w = sprite.width, + .h = sprite.height, + }; + entity->setPixelRect(RENDER); + + auto* door_ptr = dynamic_cast(entity.get()); + manager.add(std::move(entity)); + if (door_ptr != nullptr) { manager.setDoor(door_ptr); } + return true; + } + auto placeGeneric(EntityManager& manager, std::unique_ptr entity, const std::string& type, const SpriteInfo& sprite, const std::string& anchor, const fkyaml::node& spawn) -> bool { if (!spawn.contains("position")) { std::cerr << "EntityManager: " << type << " sense position\n"; @@ -322,13 +350,7 @@ namespace { } entity->setPixelRect({.x = rect_x, .y = rect_y, .w = sprite.width, .h = sprite.height}); - if (type == "door") { - auto* door_ptr = dynamic_cast(entity.get()); - manager.add(std::move(entity)); - if (door_ptr != nullptr) { manager.setDoor(door_ptr); } - } else { - manager.add(std::move(entity)); - } + manager.add(std::move(entity)); return true; } } // namespace @@ -346,9 +368,15 @@ void EntityManager::loadFromLevel(const std::string& level_yaml_path) { auto entity = buildEntity(TYPE, sprite, anchor); if (entity == nullptr) { continue; } - const bool OK = (TYPE == "sarcophagus") - ? placeSarcophagus(*this, std::move(entity), sprite, spawn) - : placeGeneric(*this, std::move(entity), TYPE, sprite, anchor, spawn); + bool ok = false; + if (TYPE == "sarcophagus") { + ok = placeSarcophagus(*this, std::move(entity), sprite, spawn); + } else if (TYPE == "door") { + ok = placeDoor(*this, std::move(entity), sprite); + } else { + ok = placeGeneric(*this, std::move(entity), TYPE, sprite, anchor, spawn); + } + const bool OK = ok; if (OK) { ++loaded; } }