feat(entities): light_radius + getLightCenter a Entity i Player

This commit is contained in:
JailDesigner committed 2026-05-12 21:06:01 +02:00
1 parent 68be8e55ec
commit ebb161f1f5
5 files changed
+67

No files matched your search

+10
View File
@@ -63,6 +63,16 @@ auto Entity::getCollisionRect() const -> SDL_FRect {
};
}
auto Entity::getLightCenter() const -> SDL_FPoint {
// Centre del rect de render. Per a entitats que es desplacen
// visualment sobre la seva posició lògica (p.ex. el Player saltant),
// la subclasse override aquest mètode per restar el desplaçament.
return {
.x = pixel_rect_.x + (pixel_rect_.w * 0.5F),
.y = pixel_rect_.y + (pixel_rect_.h * 0.5F),
};
}
auto Entity::getFeetY() const -> float {
// Decal de sòl: sentinel mínim per quedar al principi del Y-sort
// ascendent de `GameScene` (stable_sort) i així pintar-se ABANS de
+14
View File
@@ -253,6 +253,19 @@ class Entity {
[[nodiscard]] auto getFlip() const -> SDL_FlipMode { return flip_; }
void setFlip(SDL_FlipMode flip) { flip_ = flip; }
// Radi de la llum emesa per aquesta entitat (en píxels del canvas).
// 0 = no emet (cas habitual). El bloc opcional `light: { radius: N }`
// del YAML de la plantilla el pobla via `EntityManager::buildEntity`.
[[nodiscard]] auto getLightRadius() const -> int { return light_radius_; }
void setLightRadius(int r) { light_radius_ = r; }
// Centre de la llum en píxels del canvas. Per defecte és el centre del
// `pixel_rect_` (que és la caixa de render: sprite top-left + mida).
// Virtual perquè entitats amb desplaçament visual sobre la posició
// lògica (p.ex. el Player en l'aire, que rebaixa visualment `pos_.y`
// amb `jump_z_`) puguin retornar el centre que realment es veu.
[[nodiscard]] virtual auto getLightCenter() const -> SDL_FPoint;
protected:
std::string name_;
TilePos tile_{};
@@ -266,6 +279,7 @@ class Entity {
bool is_floor_decal_{false};
bool blocks_movement_{false};
int height_{0};
int light_radius_{0};
bool dead_{false};
SDL_FlipMode flip_{SDL_FLIP_NONE};
EventBus* bus_{nullptr};
@@ -155,6 +155,17 @@ namespace {
return c["height"].get_value<int>();
}
// Bloc opcional `light: { radius: N }` de la plantilla. Si manca, el
// radi és 0 i l'entitat no entra a la pasada d'il·luminació. La forma
// del gradient (3 anells) viu a data/config/lighting.yaml — no cal
// declarar-la per entitat.
auto readLightRadius(const fkyaml::node& tmpl) -> int {
if (!tmpl.contains("light")) { return 0; }
const auto& l = tmpl["light"];
if (!l.contains("radius")) { return 0; }
return l["radius"].get_value<int>();
}
// Bloc `collision: { size: [w, h], base_size: [bw, bh]?, offset: [x, y]? }`
// de la plantilla d'entitat.
// - Sense `base_size` ni `offset`: la caja queda alineada a la vora
@@ -277,6 +288,7 @@ namespace {
entity->setCollisionBox(*BOX);
}
entity->setHeight(readHeight(tmpl));
entity->setLightRadius(readLightRadius(tmpl));
anchor_out = readAnchor(tmpl);
return entity;
}
+18
View File
@@ -141,6 +141,14 @@ void Player::loadFrom(const std::string& yaml_path) {
// `duration_ms` és 0 (o el bloc manca) la mecànica queda
// desactivada i els hazards toquen al primer frame, com abans.
readImmunityBlock(yaml);
// Bloc opcional `light: { radius: N }` — radi en píxels del cercle
// de llum emès pel jugador. 0 = no emet. La forma del gradient és
// global (data/config/lighting.yaml), no per jugador.
light_radius = 0;
if (yaml.contains("light") && yaml["light"].contains("radius")) {
light_radius = yaml["light"]["radius"].get_value<int>();
}
}
void Player::readCollisionBlock(const fkyaml::node& yaml, const std::string& yaml_path) {
@@ -1462,6 +1470,16 @@ void Player::render() {
if (CLIP_HOLE) { Rendering::popClip(); }
}
auto Player::getLightCenter() const -> SDL_FPoint {
// Sprite top-left = (pos_.x, pos_.y). El render desplaça verticalment
// `jump_z_` píxels durant el salt; aplicar el mateix offset aquí fa
// que la llum "salti" amb el jugador en lloc de quedar-se al sòl.
return {
.x = getPosX() + (static_cast<float>(sprite_w) * 0.5F),
.y = getPosY() - jump_z_ + (static_cast<float>(sprite_h) * 0.5F),
};
}
void Player::renderShadow() const {
// Sense salt actiu, no hi ha sombra: això simplifica els callsites,
// que poden cridar `renderShadow` per a tots els jugadors sense
+13
View File
@@ -124,6 +124,12 @@ class Player : public AnimatedSprite {
// creua mentre `jump_z_` és superior a aquest valor, no col·lisionem.
inline static int body_height = 0;
// Radi de la llum emesa pel jugador (en píxels del canvas), llegit
// del bloc opcional `light: { radius: N }` del YAML. 0 = no emet.
// Es declara estàtic com la resta de configs del jugador (immunity,
// jump, ...): tots els jugadors del mateix YAML comparteixen el valor.
inline static int light_radius = 0;
// Ha de cridar-se abans d'instanciar Player; GameScene ho fa al ctor.
static void loadFrom(const std::string& yaml_path);
@@ -132,6 +138,13 @@ class Player : public AnimatedSprite {
// marcador i `Player::resetForJoin` per a re-entrades mid-partida.
[[nodiscard]] static auto getInitialLives() -> int { return death_config.initial_lives; }
// Emissió de llum (mateix paper que `Entity::getLightRadius` per a
// les entitats). El centre puja amb el salt: durant `jump_z_>0` el
// sprite es dibuixa amb `pos_.y - jump_z_`, i la llum ha de seguir-lo
// per a que el cercle "salti" amb el jugador, no es quedi al sòl.
[[nodiscard]] static auto getLightRadius() -> int { return light_radius; }
[[nodiscard]] auto getLightCenter() const -> SDL_FPoint;
Player(const std::string& anim_key, int id, float x, float y, const Controls& controls, const Map::Info& map_info, int paint_tile_index = -1);
// Update en una sola crida: manté l'API anterior per als tests i