feat(em): spawn diferit de DynamicHazards (pending_actors_)
`loadFromLevel` ja no instancia immediatament les DynamicHazards del
YAML del mapa: les acumula a `pending_actors_` (estructura {type,
spawn_node}). El loader decideix per la categoria semàntica de la
plantilla (`type:` del YAML d'entity: "dynamic_hazard" → pending; la
resta → addSpawn d'instant).
Dues APIs noves consumeixen pending_actors_:
- `spawnPendingActors()` — itera la cua, instancia tots via addSpawn
i arma `triggerSpawnExit()` a cada un. Vacia la cua. El path
d'aparició massiva al final del text-exit de ROOM_INTRO.
- `spawnActor(type, col, row)` — instancia un únic actor on-demand
des d'un spawn YAML sintètic. Pensat per a futurs generadors
(StaticHazards que produeixen DynamicHazards en runtime: gotes,
roques, etc.). Cada actor neix ja en EMERGING.
`GameScene::triggerHazardSpawnExit` ara és un wrapper de
`spawnPendingActors()` — el dispar segueix en la subfase actual
(fase 5 de ROOM_INTRO) i a `respawnPlayersInstant`. El canvi de timing
ve en el commit següent.
`reload` també buida pending_actors_. Log: "N map entities + M actors
pending des de PATH" — visible per a diagnòstic.
Sense canvis de comportament observables al joc: les mòmies segueixen
apareixent al final del text-exit, però ara la "aparició" és la seua
instanciació real (abans ja existien quietes des del fade-in).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
62f4b857ef
commit
6a6a1a52c7
3 files changed
+106
-13
No files matched your search
@@ -215,6 +215,24 @@ namespace {
|
||||
return tmpl["anchor"].get_value<std::string>();
|
||||
}
|
||||
|
||||
// Llig el camp `type:` de la plantilla (la categoria semàntica:
|
||||
// "decoration", "static_hazard", "interactable", "dynamic_hazard",
|
||||
// "effect"). Tornarà buit si la plantilla no es pot carregar o no
|
||||
// declara el camp. S'usa al loader per decidir quin sac rep l'entitat.
|
||||
auto readTemplateCategory(const std::string& type) -> std::string {
|
||||
try {
|
||||
const auto TMPL = EntityLoader::loadTemplate(type);
|
||||
if (TMPL.contains("type")) { return TMPL["type"].get_value<std::string>(); }
|
||||
} catch (const std::exception& e) {
|
||||
// Plantilla absent / parse error: deixem el caller que ho
|
||||
// gestione (típicament `buildEntity` també fallarà i deixarà
|
||||
// el seu propi log). Aquí només avisem perquè el silenci
|
||||
// total amaga problemes de YAML mal formats.
|
||||
Log::warn("EntityManager", "readTemplateCategory({}) ha fallat: {}", type, e.what());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
// Coordenades píxel (canvas) d'una cel·la (col, row) del tilemap. El
|
||||
// grid del tilemap està 16-alineat respecte a `tilemap_origin_*`, que
|
||||
// pot ser negatiu per overscan CPC.
|
||||
@@ -397,6 +415,7 @@ void EntityManager::reload(const std::string& level_yaml_path) {
|
||||
// subscripcions externes (PaintLayer, HUD, GameScene) no es toquen.
|
||||
map_entities_.clear();
|
||||
actors_.clear();
|
||||
pending_actors_.clear();
|
||||
door_ = nullptr;
|
||||
total_sarcs_ = 0;
|
||||
opened_sarcs_ = 0;
|
||||
@@ -433,8 +452,21 @@ auto EntityManager::addSpawn(const fkyaml::node& spawn) -> Entity* {
|
||||
|
||||
void EntityManager::loadFromLevel(const std::string& level_yaml_path) {
|
||||
const auto SPAWNS = EntityLoader::readSpawns(level_yaml_path);
|
||||
const int LOADED = static_cast<int>(std::ranges::count_if(SPAWNS,
|
||||
[this](const fkyaml::node& spawn) { return addSpawn(spawn) != nullptr; }));
|
||||
int loaded = 0;
|
||||
for (const auto& spawn : SPAWNS) {
|
||||
if (!spawn.contains("type")) { continue; }
|
||||
const auto TYPE = spawn["type"].get_value<std::string>();
|
||||
// Spawn diferit: els DynamicHazards (mòmies, roques, futurs
|
||||
// enemics) no s'instancien aquí. GameScene crida
|
||||
// `spawnPendingActors()` al final del text-exit de ROOM_INTRO
|
||||
// (i, defensivament, en salts debug). La instanciació = la
|
||||
// seua aparició visual.
|
||||
if (readTemplateCategory(TYPE) == "dynamic_hazard") {
|
||||
pending_actors_.push_back(PendingActor{.type = TYPE, .spawn_node = spawn});
|
||||
continue;
|
||||
}
|
||||
if (addSpawn(spawn) != nullptr) { ++loaded; }
|
||||
}
|
||||
|
||||
// Broadcast inicial: marca com a "pintats" tots els tiles ocupats per
|
||||
// decals de sòl (Hole, i futurs pinchos plans, plaques, charcos...).
|
||||
@@ -463,5 +495,42 @@ void EntityManager::loadFromLevel(const std::string& level_yaml_path) {
|
||||
Log::info("EntityManager", "{} tile_painted broadcast inicial (decals de sòl)", decal_broadcast);
|
||||
}
|
||||
|
||||
Log::info("EntityManager", "{} entitats carregades des de {}", LOADED, level_yaml_path);
|
||||
Log::info("EntityManager", "{} map entities + {} actors pending des de {}", loaded, pending_actors_.size(), level_yaml_path);
|
||||
}
|
||||
|
||||
void EntityManager::spawnPendingActors() {
|
||||
// Itera els pending i els instancia via `addSpawn`. El dispatch d'`add()`
|
||||
// els enruta a `actors_`. El `triggerSpawnExit()` posterior arma el
|
||||
// comptador de SPAWN — la mòmia neix ja en EMERGING, sense esperes.
|
||||
for (const auto& pa : pending_actors_) {
|
||||
Entity* observer = addSpawn(pa.spawn_node);
|
||||
if (observer == nullptr) { continue; }
|
||||
// Per categoria YAML (`type: dynamic_hazard`) sabem que és
|
||||
// DynamicHazard — el dynamic_cast aquí val zero i evita
|
||||
// assumptions implícites sobre la jerarquia.
|
||||
if (auto* dh = dynamic_cast<DynamicHazard*>(observer); dh != nullptr) {
|
||||
dh->triggerSpawnExit();
|
||||
}
|
||||
}
|
||||
pending_actors_.clear();
|
||||
}
|
||||
|
||||
auto EntityManager::spawnActor(const std::string& type, int col, int row) -> DynamicHazard* {
|
||||
// Construïm un node de spawn sintètic amb el mínim que `placeGeneric`
|
||||
// necessita: `type` i `position: [col, row]`. Si el caller vol
|
||||
// overrides addicionals (orientació inicial, durada de spawn...),
|
||||
// afegirà una variant amb fkyaml::node pre-construït més endavant.
|
||||
fkyaml::node spawn;
|
||||
try {
|
||||
const std::string YAML_TEXT = "type: " + type + "\nposition: [" + std::to_string(col) + ", " + std::to_string(row) + "]\n";
|
||||
spawn = fkyaml::node::deserialize(YAML_TEXT.begin(), YAML_TEXT.end());
|
||||
} catch (const std::exception&) {
|
||||
Log::error("EntityManager", "spawnActor: no s'ha pogut construir spawn YAML per {}", type);
|
||||
return nullptr;
|
||||
}
|
||||
Entity* observer = addSpawn(spawn);
|
||||
if (observer == nullptr) { return nullptr; }
|
||||
auto* dh = dynamic_cast<DynamicHazard*>(observer);
|
||||
if (dh != nullptr) { dh->triggerSpawnExit(); }
|
||||
return dh;
|
||||
}
|
||||
Reference in new issue
Block a user