diff --git a/source/core/resources/resource_helper.cpp b/source/core/resources/resource_helper.cpp index 79c6d5c..86cf03b 100644 --- a/source/core/resources/resource_helper.cpp +++ b/source/core/resources/resource_helper.cpp @@ -15,12 +15,18 @@ namespace Resource::Helper { Pack g_pack; bool g_pack_loaded = false; bool g_fallback_enabled = true; + // Inverteix la prioritat (disc abans que pack). Activat en Debug + // per defecte; veure setPreferDisk al header. + bool g_prefer_disk = false; - auto readFromDisk(const std::string& filepath) -> std::vector { + auto readFromDisk(const std::string& filepath, bool verbose = true) -> std::vector { const auto FULL = resolve(filepath); std::ifstream file(FULL, std::ios::binary | std::ios::ate); if (!file) { - std::cerr << "Resource::Helper: cannot open " << FULL << '\n'; + // Silenciem si veníem de prefer_disk: aleshores caurem + // al pack i no és cap error real que el fitxer no estigui + // al disc. + if (verbose) { std::cerr << "Resource::Helper: cannot open " << FULL << '\n'; } return {}; } std::streamsize size = file.tellg(); @@ -37,6 +43,11 @@ namespace Resource::Helper { auto initializeResourceSystem(const std::string& pack_file, bool enable_fallback) -> bool { g_fallback_enabled = enable_fallback; + // En el flux Debug típic (fallback actiu, source tree disponible) + // preferim sempre disc: el desenvolupador edita YAML/GIF a `data/` + // i ho veu reflectit al següent reload sense re-packejar. En + // Release el fallback està desactivat i aquest flag és inert. + g_prefer_disk = enable_fallback; g_pack_loaded = g_pack.loadPack(pack_file); if (g_pack_loaded) { @@ -76,17 +87,45 @@ namespace Resource::Helper { } auto loadFile(const std::string& filepath) -> std::vector { - if (g_pack_loaded) { - // El pack està indexat relatiu a `data/` (la carpeta que - // s'empaqueta), però els callers passen "data/foo.yaml" amb - // prefix. Fem el strip ací perquè no hagin de tenir-ho en compte. + // Helper local: lookup al pack normalitzant la clau (els callers + // passen "data/foo.yaml" però l'índex del pack és relatiu a la + // carpeta `data/`). + const auto FROM_PACK = [&filepath]() -> std::vector { + if (!g_pack_loaded) { return {}; } constexpr std::string_view PREFIX = "data/"; std::string key = filepath; if (key.starts_with(PREFIX)) { key.erase(0, PREFIX.size()); } if (g_pack.hasResource(key)) { return g_pack.getResource(key); } + return {}; + }; + + if (g_prefer_disk && g_fallback_enabled) { + // En Debug volem que el disc guanyi: així les edicions del + // editor in-game (i les manuals del repo) es vegen al pròxim + // reload sense rebuild del pack. Si el fitxer no és al disc, + // caiem al pack — útil per a recursos que només s'han + // empaquetat (cas estrany). El silenci a `readFromDisk` evita + // emplenar el log amb "cannot open" en aquest camí esperat. + auto bytes = readFromDisk(filepath, false); + if (!bytes.empty()) { return bytes; } + return FROM_PACK(); } + + // Política original: pack primer; disc com a fallback si el + // recurs no està empaquetat (típic d'arrencada en Debug abans + // del primer pack). + auto packed = FROM_PACK(); + if (!packed.empty()) { return packed; } if (g_fallback_enabled) { return readFromDisk(filepath); } return {}; } + void setPreferDisk(bool prefer) { + g_prefer_disk = prefer; + } + + auto preferDisk() -> bool { + return g_prefer_disk; + } + } // namespace Resource::Helper diff --git a/source/core/resources/resource_helper.hpp b/source/core/resources/resource_helper.hpp index 3f2b3d8..bfb14d2 100644 --- a/source/core/resources/resource_helper.hpp +++ b/source/core/resources/resource_helper.hpp @@ -32,7 +32,17 @@ namespace Resource::Helper { // Carrega un recurs pel seu camí relatiu (p.ex. "fonts/hud.gif", // "input/keys.yaml"). Prova primer el pack; si no hi és i el fallback // està actiu, cau a disc (data root + ruta). Retorna vector buit si - // no el troba. + // no el troba. Si `preferDisk()` és true (Debug), inverteix la + // prioritat: prova disc abans del pack. auto loadFile(const std::string& filepath) -> std::vector; + // Inverteix la prioritat de `loadFile` (disc abans que pack). En Debug + // s'activa per defecte al `initializeResourceSystem` perquè cada + // canvi al YAML/GIF al source tree es vegi sense re-packejar — és el + // que necessita l'editor in-game per veure les seues edicions + // immediatament al recarregar la sala. En Release no té efecte + // (el fallback està desactivat). + void setPreferDisk(bool prefer); + [[nodiscard]] auto preferDisk() -> bool; + } // namespace Resource::Helper diff --git a/source/game/scenes/game_scene.cpp b/source/game/scenes/game_scene.cpp index 6d56735..61c9a8e 100644 --- a/source/game/scenes/game_scene.cpp +++ b/source/game/scenes/game_scene.cpp @@ -607,6 +607,30 @@ void GameScene::enterRoomIntro() { } } +#ifdef _DEBUG +void GameScene::respawnPlayersInstant() { + // Variant sense cinemàtica de l'spawn de l'INTRO: per a l'editor + // volem que la sala torni a un estat jugable d'immediat (sense + // música de sala, sense caiguda aèria). Reusem `computeSpawn` i el + // mateix patró de `triggerPlayerSpawn` però cridem `respawn(x, y)` + // (teleport instantani) en lloc de `spawnFromAir(x, y)`. + const auto SPAWN = computeSpawn(); + const bool BOTH_ACTIVE = (players_starts_active_[0] && players_starts_active_[1]); + const float SINGLE_X = SPAWN.p1_x + static_cast(Map::tile_size); + for (std::size_t i = 0; i < players_.size(); ++i) { + if (!players_starts_active_[i]) { continue; } + float x = SINGLE_X; + if (BOTH_ACTIVE) { x = (i == 0) ? SPAWN.p1_x : SPAWN.p2_x; } + players_[i]->setSpawnPoint(x, SPAWN.y); + players_[i]->respawn(x, SPAWN.y); + } + state_ = RoomFsm::State::PLAYING; + intro_completed_ = true; + transition_ = {}; + std::cout << "[ROOM] post-editor: spawn instantani, estat -> PLAYING\n"; +} +#endif + void GameScene::triggerPlayerSpawn(std::size_t slot_index) { if (slot_index >= players_.size()) { return; } if (!players_starts_active_[slot_index]) { return; } @@ -1141,17 +1165,28 @@ void GameScene::update(int delta_ms) { #ifdef _DEBUG // Toggle de l'editor: només si no hi ha cap transició de pausa ni // pausa consolidada. L'editor mateix no consumeix la tecla — la - // llegim ací per decidir la coexistència. Quan se surt del editor, - // recarreguem les entitats des del YAML perquè el manager torne a - // estar sincronitzat amb el que el writer acaba de desar (cada - // sessió d'edició parteix del fitxer, no de l'estat runtime previ). + // llegim ací per decidir la coexistència. Tant a l'entrada com a la + // sortida fem `loadRoom` complet per partir sempre del YAML del + // disc: les entitats tornen a la posició inicial (momies, sarcs), + // el paint layer es buida i els jugadors es desactiven. Així + // l'editor sempre veu l'estat-base de la sala (no el d'una partida + // a meitat) i el joc, en sortir, arrenca fresc amb les edicions ja + // persistides al YAML. if (pause_.state == PauseState::IDLE && detectEditorToggle()) { const bool WAS_ACTIVE = editor_.active(); editor_.toggle(tilemap_, entity_manager_); - if (WAS_ACTIVE) { - entity_manager_.reload(Levels::LevelManager::get().currentPath()); - const int ACTIVE_COUNT = std::ranges::count(players_starts_active_, true); - entity_manager_.setExpectedPlayerCount(ACTIVE_COUNT); + const bool NOW_ACTIVE = editor_.active(); + if (!WAS_ACTIVE && NOW_ACTIVE) { + // Acabem d'entrar: carreguem fresh des del disc. Els + // jugadors es queden DISABLED (l'editor no els pinta de + // totes maneres) i es reactivaran amb el spawn instantani + // a la sortida. + loadRoom(Levels::LevelManager::get().currentPath()); + } else if (WAS_ACTIVE && !NOW_ACTIVE) { + // Acabem de sortir: el writer ja ha persistit el YAML. + // Carreguem fresh i fem spawn instantani sense cinemàtica. + loadRoom(Levels::LevelManager::get().currentPath()); + respawnPlayersInstant(); } } diff --git a/source/game/scenes/game_scene.hpp b/source/game/scenes/game_scene.hpp index 7619223..2adaf83 100644 --- a/source/game/scenes/game_scene.hpp +++ b/source/game/scenes/game_scene.hpp @@ -288,6 +288,13 @@ class GameScene : public Scene { // Idempotent quan ja s'estava a INTRO — útil per als salts debug. void enterRoomIntro(); +#ifdef _DEBUG + // Spawn instantani sense cinemàtica: usat al sortir del editor per + // fer un "fresh start" de la sala (jugadors al spawn, sense la + // cinemàtica `room_intro.ogg`). Posa l'estat directament a PLAYING. + void respawnPlayersInstant(); +#endif + #ifdef _DEBUG // Editor modal del tilemap. Mútuament excloent amb la pausa: si la // pausa no està IDLE la tecla de toggle de l'editor s'ignora, i