feat(door): col·locació derivada de Map::door_col i caixa de col·lisió neta

This commit is contained in:
JailDesigner committed 2026-04-26 10:07:37 +02:00
1 parent b54f3fc622
commit 648a5932f8
3 files changed
+49 -11

No files matched your search

+8
View File
@@ -5,6 +5,14 @@ sprite:
size: [48, 32] size: [48, 32]
tile_size: [3, 2] tile_size: [3, 2]
collision: 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 # Porta = mur infranquejable. Igual que el sarc, alçada efectiva
# infinita per impedir que un jugador la salte per damunt. # infinita per impedir que un jugador la salte per damunt.
height: 999 height: 999
+3 -1
View File
@@ -129,7 +129,9 @@ entities:
- type: sarcophagus - type: sarcophagus
sarc_position: [3, 4] sarc_position: [3, 4]
- type: door - 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 - type: mummy
position: [10, 7] position: [10, 7]
- type: mummy - type: mummy
@@ -291,6 +291,34 @@ namespace {
return true; 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> 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<float>((Map::door_col * Map::tile_size) + Map::tilemap_origin_x) + (static_cast<float>(Map::tile_size) / 2.0F);
const SDL_FRect RENDER{
.x = CENTER_X - (sprite.width / 2.0F),
.y = static_cast<float>(Map::playfield_y) - sprite.height,
.w = sprite.width,
.h = sprite.height,
};
entity->setPixelRect(RENDER);
auto* door_ptr = dynamic_cast<Door*>(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> entity, const std::string& type, const SpriteInfo& sprite, const std::string& anchor, const fkyaml::node& spawn) -> bool { auto placeGeneric(EntityManager& manager, std::unique_ptr<Entity> entity, const std::string& type, const SpriteInfo& sprite, const std::string& anchor, const fkyaml::node& spawn) -> bool {
if (!spawn.contains("position")) { if (!spawn.contains("position")) {
std::cerr << "EntityManager: " << type << " sense position\n"; 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}); entity->setPixelRect({.x = rect_x, .y = rect_y, .w = sprite.width, .h = sprite.height});
if (type == "door") { manager.add(std::move(entity));
auto* door_ptr = dynamic_cast<Door*>(entity.get());
manager.add(std::move(entity));
if (door_ptr != nullptr) { manager.setDoor(door_ptr); }
} else {
manager.add(std::move(entity));
}
return true; return true;
} }
} // namespace } // namespace
@@ -346,9 +368,15 @@ void EntityManager::loadFromLevel(const std::string& level_yaml_path) {
auto entity = buildEntity(TYPE, sprite, anchor); auto entity = buildEntity(TYPE, sprite, anchor);
if (entity == nullptr) { continue; } if (entity == nullptr) { continue; }
const bool OK = (TYPE == "sarcophagus") bool ok = false;
? placeSarcophagus(*this, std::move(entity), sprite, spawn) if (TYPE == "sarcophagus") {
: placeGeneric(*this, std::move(entity), TYPE, sprite, anchor, spawn); 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; } if (OK) { ++loaded; }
} }