feat(lighting): override d'emissors per sala i per spawn

This commit is contained in:
JailDesigner committed 2026-05-13 12:52:08 +02:00
1 parent b76a3eb1f3
commit 9d0dde38f9
5 files changed
+203 -9

No files matched your search

+63 -2
View File
@@ -2,6 +2,7 @@
#include <cmath>
#include <stdexcept>
#include <unordered_map>
#include "core/resources/resource_helper.hpp"
#include "external/fkyaml_node.hpp"
@@ -28,12 +29,65 @@ namespace Map {
// (debris, columnes). El TU local manté l'estat sense exposar-lo
// al header.
std::function<bool(const SDL_FRect&)> g_entity_collider;
// Overrides d'emissor declarats al bloc `lighting.emitters` del YAML
// de la sala. Clau = tipus d'entitat en singular (per concordar amb
// el camp `type:` de cada plantilla). Buit per defecte: una sala
// sense bloc `lighting.emitters` deixa que cada entitat utilitze els
// seus propis defaults del seu YAML de tipus.
std::unordered_map<std::string, RoomEmitterOverride> g_room_emitters;
// Plural → singular per a tipus coneguts. El YAML de sala fa servir
// el plural perquè es llegeix natural ("mummies: false"); el codi
// treballa amb la forma singular que coincideix amb el camp `type:`
// del spawn ("type: mummy"). Si la clau no és reconeguda, passa tal
// qual (suposa que l'autor ja l'ha escrit en singular).
auto normalizeEmitterKey(const std::string& yaml_key) -> std::string {
if (yaml_key == "players") { return "player"; }
if (yaml_key == "mummies") { return "mummy"; }
if (yaml_key == "torches") { return "torch"; }
return yaml_key;
}
// Llegeix el bloc opcional `lighting:` del YAML d'una sala i
// actualitza `light_level` (si declara `level`) i el mapping
// `g_room_emitters`. Idempotent: cada `Map::loadFrom` neteja el
// mapping abans de poblar-lo, així no s'arrosseguen overrides
// d'una sala anterior.
void loadLightingBlock(const fkyaml::node& yaml) {
g_room_emitters.clear();
if (!yaml.contains("lighting")) { return; }
const auto& lighting = yaml["lighting"];
if (lighting.contains("level")) {
light_level = lighting["level"].get_value<float>();
}
if (!lighting.contains("emitters") || !lighting["emitters"].is_mapping()) { return; }
const auto& em = lighting["emitters"];
for (auto it = em.begin(); it != em.end(); ++it) {
const auto KEY = normalizeEmitterKey(it.key().get_value<std::string>());
const auto& val = it.value();
RoomEmitterOverride ov;
if (val.is_boolean()) {
ov.enabled = val.get_value<bool>();
} else if (val.is_mapping()) {
ov.enabled = true;
if (val.contains("radius")) { ov.radius = val["radius"].get_value<int>(); }
if (val.contains("flicker")) { ov.flicker = val["flicker"].get_value<bool>(); }
}
g_room_emitters[KEY] = ov;
}
}
} // namespace
void setEntityCollider(std::function<bool(const SDL_FRect&)> fn) {
g_entity_collider = std::move(fn);
}
auto getRoomEmitterOverride(const std::string& type) -> const RoomEmitterOverride* {
const auto IT = g_room_emitters.find(type);
return IT == g_room_emitters.end() ? nullptr : &IT->second;
}
void loadFrom(const std::string& yaml_path) {
auto bytes = Resource::Helper::loadFile(yaml_path);
if (bytes.empty()) {
@@ -103,9 +157,16 @@ namespace Map {
// canviarà el flag a true quan transiti a OPEN.
door_open = false;
// Llum base de la sala. La clau és opcional a l'arrel del YAML;
// per defecte 1.0 (sala totalment il·luminada, comportament hui).
// Llum base de la sala. Dues formes acceptades:
// - `light_level: <float>` a l'arrel (forma antiga, encara
// suportada per retrocompat).
// - `lighting.level: <float>` dins del bloc nou `lighting:`,
// que també hostatja `lighting.emitters` (overrides per tipus).
// Si manca tot, queda al default 1.0 (sala totalment il·luminada).
// `loadLightingBlock` aplica el `lighting.level` per damunt del
// `light_level:` antic, així el nou bloc guanya si conviuen.
light_level = yaml.contains("light_level") ? yaml["light_level"].get_value<float>() : Defaults::Lighting::ROOM_LIGHT_DEFAULT;
loadLightingBlock(yaml);
Log::info("Map", "geometria {}x{} sarcs {}x{} pitch {}x{} door_col {} des de {}", grid_cols, grid_rows, sarc_w, sarc_h, pitch_x, pitch_y, door_col, yaml_path);
}
+34 -1
View File
@@ -3,6 +3,7 @@
#include <SDL3/SDL.h>
#include <functional>
#include <optional>
#include <string>
#include <utility>
@@ -54,9 +55,41 @@ namespace Map {
// (com hui, sense efecte visual); 0.0 = completament negra fora del
// radi d'un emissor. La clau `light_level:` al YAML de la sala el
// sobreescriu — si manca, queda al default 1.0 i la pasada
// d'il·luminació del frame es salta com a no-op.
// d'il·luminació del frame es salta com a no-op. La forma nova és
// declarar-ho dins del bloc `lighting:` (vegeu més avall); `light_level:`
// al root es manté per compatibilitat enrere.
inline float light_level = 1.0F;
// Override d'emissor declarat al bloc opcional `lighting.emitters.<tipus>`
// de la sala. Tres formes acceptades al YAML:
// <tipus>: false → desactiva la llum per a tot el tipus
// <tipus>: true → activa amb els defaults del tipus
// <tipus>: { radius: N, flicker: B } → activa i sobreescriu camps
// Els camps absents (`std::nullopt`) cauen al default del tipus
// d'entitat (radi/flicker del seu YAML). Els tipus no presents al
// mapping conserven el seu comportament per defecte (com avui).
//
// Conversió de clau YAML → tipus d'entitat: el YAML usa plural
// ("players", "mummies", "torches") perquè es llegeix natural per a
// l'autor de la sala; `loadFrom` normalitza a la clau singular
// ("player", "mummy", "torch") que coincideix amb el camp `type:` de
// cada plantilla d'entitat. Tipus no reconeguts s'accepten "tal qual"
// (singular pass-through) per si en el futur s'hi afegeixen emissors
// nous sense tocar el mapatge.
struct RoomEmitterOverride {
bool enabled{true};
std::optional<int> radius;
std::optional<bool> flicker;
};
// Consulta l'override de sala per a un tipus d'entitat (clau singular,
// p.ex. "player" / "mummy" / "torch"). Retorna `nullptr` si la sala no
// declara res per aquest tipus — el caller usa els defaults del tipus
// sense modificació. El punter retornat és vàlid fins a la propera
// crida a `Map::loadFrom` (cada càrrega de nivell substitueix el
// mapping intern).
[[nodiscard]] auto getRoomEmitterOverride(const std::string& type) -> const RoomEmitterOverride*;
// Derivats de la geometria base: el pas entre sarcòfags ha de cabre
// exactament el jugador, i el mateix passadís s'usa a l'exterior.
inline int corridor_w = 0;