diff --git a/source/game/scenes/game_scene.cpp b/source/game/scenes/game_scene.cpp index cbf252c..1b2d104 100644 --- a/source/game/scenes/game_scene.cpp +++ b/source/game/scenes/game_scene.cpp @@ -21,7 +21,9 @@ #include "core/rendering/screen.hpp" #include "core/rendering/text.hpp" #include "core/resources/resource_cache.hpp" +#include "core/resources/resource_helper.hpp" #include "core/system/event_buffer.hpp" +#include "external/fkyaml_node.hpp" #include "game/entities/entity.hpp" #include "game/entities/event_bus.hpp" #include "game/entities/player.hpp" @@ -42,6 +44,21 @@ namespace { return Tilemap(yaml_path); } + // Llegeix un camp sencer opcional del YAML del mapa. Si no hi és, + // retorna `fallback` (per defecte -1, que al Player indica "no pinta"). + // S'usa per `player1_paint_tile` i `player2_paint_tile`: cada jugador + // pinta amb l'índex declarat al mapa, i un mapa sense aquests camps + // simplement no deixa rastre visual. + auto readOptionalInt(const std::string& yaml_path, const std::string& key, int fallback = -1) -> int { + auto bytes = Resource::Helper::loadFile(yaml_path); + if (bytes.empty()) { return fallback; } + const auto* begin = reinterpret_cast(bytes.data()); + const auto* end = begin + bytes.size(); + auto yaml = fkyaml::node::deserialize(begin, end); + if (!yaml.contains(key)) { return fallback; } + return yaml[key].get_value(); + } + } // namespace auto GameScene::defaultConfig() -> Config { @@ -103,10 +120,23 @@ GameScene::GameScene(Config config) static_cast(Map::playfield_y) + (static_cast(Map::outer_corridor) / 2.0F); const auto SPAWN_X = CORRIDOR_CENTER_X - (static_cast(Player::sprite_w) / 2.0F); const auto SPAWN_Y = CORRIDOR_CENTER_Y - static_cast(Player::collision_y_offset) - (static_cast(Player::collision_size) / 2.0F); - players_.emplace_back(std::make_unique(ANIM_KEY, SPAWN_X, SPAWN_Y, CONTROLS_P1, Map::makeInfo())); + // Índex de tile amb què pinta cada jugador — llegit del YAML del mapa + // (camps opcionals `player1_paint_tile` / `player2_paint_tile`). Si no + // hi són, `paint_tile_index_ = -1` i el Player no deixa rastre. + const int P1_PAINT = readOptionalInt(config_.map_yaml, "player1_paint_tile", -1); + players_.emplace_back(std::make_unique(ANIM_KEY, SPAWN_X, SPAWN_Y, CONTROLS_P1, Map::makeInfo(), P1_PAINT)); - // Connectem el Player al bus perquè emeti `on_tile_entered` i els - // sarcòfags rebin les notificacions de pas per tile. + // PaintLayer: font única del pintat del nivell. Es crea després de + // `loadFromLevel` perquè l'EntityManager ja ha de tenir els sarcòfags + // registrats (tot i que el sarc escolta via fan-out, no directament + // al bus; l'ordre de subscripcions aquí és irrellevant per la + // correcció, però sí pel log d'arrencada). Connectat al bus del + // manager per subscriure's a `on_tile_entered` i publicar `tile_painted`. + paint_layer_ = std::make_unique(tilemap_, &entity_manager_.getBus()); + + // Connectem el Player al bus perquè emeti `on_tile_entered` i el + // PaintLayer (subscrit ja) processe el pintat; el fan-out cap als + // sarcòfags passa per `tile_painted`. players_.back()->setEventBus(&entity_manager_.getBus()); // Música de fons en bucle infinit. El track ja està al Resource::Cache @@ -228,6 +258,10 @@ void GameScene::render() { #else constexpr bool SKIP_ACTORS = false; #endif + // Capa de pintat: va entre el tilemap base i els actors, perquè els + // sarcòfags i el jugador la tapen. S'amaga al mode editor (decisió + // explícita: l'editor edita el tilemap, no la capa de pintat). + if (!SKIP_ACTORS && paint_layer_) { paint_layer_->render(); } if (!SKIP_ACTORS) { // Z-order interleaving entitats ↔ players per `getFeetY()` ascendent: // els actors amb els peus més amunt queden dibuixats primer (al fons), diff --git a/source/game/scenes/game_scene.hpp b/source/game/scenes/game_scene.hpp index f4fa9d1..346f717 100644 --- a/source/game/scenes/game_scene.hpp +++ b/source/game/scenes/game_scene.hpp @@ -6,6 +6,7 @@ #include "core/scene/scene.hpp" #include "game/entities/entity_manager.hpp" +#include "game/map/paint_layer.hpp" #include "game/map/tilemap.hpp" #ifdef _DEBUG @@ -53,6 +54,15 @@ class GameScene : public Scene { // té els seus propis Controls i entra al vector amb una crida extra. std::vector> players_; + // Capa de "pintat" del terra. Escolta `on_tile_entered` al bus, + // filtra pintabilitat (sòl transitable) i re-publica `tile_painted` + // per als sarcòfags. `unique_ptr` per controlar l'ordre de + // construcció/destrucció: es crea al ctor un cop `entity_manager_` i + // `tilemap_` estan llestos, i es destrueix abans que `entity_manager_` + // (on viu el bus al qual està subscrit). Declarat després d'aquests + // dos membres per garantir l'ordre invers a la destrucció. + std::unique_ptr paint_layer_; + // Pausa de gameplay. Quan `paused_` és true, l'update de jugadors i // entitats es salta (el món es congela); l'input d'UI (GlobalInputs, // Notifier, RenderInfo) continua per no perdre hotkeys ni l'overlay de