refactor(player): config a membres d'instancia (P1/P2 YAMLs independents)
This commit is contained in:
1 parent
8eba52353b
commit
2aef925286
4 files changed
+516
-591
No files matched your search
+181
-211
@@ -55,6 +55,15 @@ class Player : public AnimatedSprite {
|
||||
HOLE, // caiguda per agujero — animació `death_hole`
|
||||
};
|
||||
|
||||
// Direcció cardinal completa. Pública perquè el `WhipAttack` la passa a
|
||||
// `enterHurt` quan colpeja un altre jugador (friendly fire). Declarada
|
||||
// aquí (a sobre de `Config` perquè `TurnConfig` l'utilitza).
|
||||
enum class Cardinal : std::uint8_t { NONE,
|
||||
NORTH,
|
||||
SOUTH,
|
||||
EAST,
|
||||
WEST };
|
||||
|
||||
enum class State : std::uint8_t {
|
||||
GROUNDED,
|
||||
AIRBORNE,
|
||||
@@ -92,80 +101,172 @@ class Player : public AnimatedSprite {
|
||||
DYING,
|
||||
};
|
||||
|
||||
// Geometria carregada des del YAML del player (p.ex. `data/entities/player/sam.yaml`).
|
||||
// `sprite_*` són
|
||||
// la mida del frame (sprite 12×24 amb perspectiva falsa: visualment, els
|
||||
// 12 píxels superiors són el cap i els 12 inferiors la base).
|
||||
// La caixa de col·lisió és un quadrat `collision_size` concèntric amb el
|
||||
// quadrat inferior del sprite (`base_size`). Els offsets els calculem
|
||||
// al load a partir d'aquests tres valors; el mapa de col·lisions i el
|
||||
// de dibuix són independents (el sprite pot sobresortir sense afectar).
|
||||
inline static int sprite_w = 0;
|
||||
inline static int sprite_h = 0;
|
||||
inline static int base_size = 0;
|
||||
inline static int collision_size = 0;
|
||||
inline static int collision_padding = 0;
|
||||
inline static int collision_x_offset = 0;
|
||||
inline static int collision_y_offset = 0;
|
||||
// Configuració per-instància del Player. Conté tot el que abans vivia
|
||||
// com a `inline static` o `static *_config`: així cada jugador (P1=sam,
|
||||
// P2=morcus) pot tenir paràmetres diferents llegits del seu YAML.
|
||||
// GameScene la carrega amb `Config::loadFromYaml` abans d'instanciar
|
||||
// cada Player i la passa al ctor (mou-la). Per refrescar-la entre
|
||||
// sales s'utilitza `Player::setConfig`.
|
||||
//
|
||||
// Els sub-structs (`InertiaConfig`, `TurnConfig`, ...) viuen dins de
|
||||
// `Config` per agrupar-los visualment amb la resta del config — no hi
|
||||
// ha cap raó tècnica per haver-los a fora.
|
||||
struct Config {
|
||||
// ---- Geometria del sprite i col·lisió --------------------------
|
||||
// `sprite_*` són la mida del frame (sprite 12×24 amb perspectiva
|
||||
// falsa). La caixa de col·lisió és un quadrat `collision_size`
|
||||
// concèntric amb el quadrat inferior del sprite (`base_size`).
|
||||
// Els offsets es calculen al load.
|
||||
int sprite_w{0};
|
||||
int sprite_h{0};
|
||||
int base_size{0};
|
||||
int collision_size{0};
|
||||
int collision_padding{0};
|
||||
int collision_x_offset{0};
|
||||
int collision_y_offset{0};
|
||||
|
||||
// Caixa de "contacte" més petita que la caixa de col·lisió: s'usa per
|
||||
// a les interaccions actor-actor (player↔player, player↔momia/trampa),
|
||||
// on volem que el contacte sigui precís i no dispari a "distància"
|
||||
// de la vora del sprite. La caixa de col·lisió gran continua usant-se
|
||||
// per xocar amb murs i sarcòfags (per mantenir el carril del
|
||||
// passadís). Sense `hit_size` al YAML, s'iguala a `collision_size` i
|
||||
// el comportament és idèntic a l'anterior.
|
||||
inline static int hit_size = 0;
|
||||
inline static int hit_x_offset = 0;
|
||||
inline static int hit_y_offset = 0;
|
||||
// Caixa de contacte actor-actor (més petita que la col·lisió).
|
||||
int hit_size{0};
|
||||
int hit_x_offset{0};
|
||||
int hit_y_offset{0};
|
||||
|
||||
// Alçada del cos del jugador en píxels (camp `collision.height` del
|
||||
// YAML, mateixa convenció que entitats com mummy o rolling_rock).
|
||||
// L'usen els filtres de col·lisió per altura: si un altre actor ens
|
||||
// creua mentre `jump_z_` és superior a aquest valor, no col·lisionem.
|
||||
inline static int body_height = 0;
|
||||
// Alçada del cos en píxels (filtre de col·lisió per altura).
|
||||
int body_height{0};
|
||||
|
||||
// Radi de la llum emesa pel jugador (en píxels del canvas), llegit
|
||||
// del bloc opcional `light: { radius: N, flicker: bool, color: [r,g,b], offset: [x,y] }`
|
||||
// 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. `light_flicker` activa la modulació "tipus
|
||||
// foc" del radi; `light_color_*` són el tint quan la sala usa mode
|
||||
// additive (en subtractive s'ignora); `light_offset_*` desplaça el
|
||||
// centre del cercle respecte el centre del sprite (Y creix avall:
|
||||
// `y: -3` puja la llum 3 px).
|
||||
inline static int light_radius = 0;
|
||||
inline static bool light_flicker = false;
|
||||
inline static std::uint8_t light_color_r = 255;
|
||||
inline static std::uint8_t light_color_g = 255;
|
||||
inline static std::uint8_t light_color_b = 255;
|
||||
inline static int light_offset_x = 0;
|
||||
inline static int light_offset_y = 0;
|
||||
// Paràmetres del flicker resolts per al jugador (combinació del
|
||||
// global + override del YAML del personatge + override de sala).
|
||||
// Només té efecte si `light_flicker` és true.
|
||||
inline static Lighting::FlickerConfig flicker_params{};
|
||||
// ---- Emissió de llum -----------------------------------------
|
||||
// Llegit del bloc opcional `light:` del YAML.
|
||||
// `radius` → 0 = no emet.
|
||||
// `flicker` → activa modulació tipus foc.
|
||||
// `color` → tint en mode additive (ignorat en subtractive).
|
||||
// `offset` → desplaçament del centre respecte el sprite (Y avall).
|
||||
int light_radius{0};
|
||||
bool light_flicker{false};
|
||||
std::uint8_t light_color_r{255};
|
||||
std::uint8_t light_color_g{255};
|
||||
std::uint8_t light_color_b{255};
|
||||
int light_offset_x{0};
|
||||
int light_offset_y{0};
|
||||
// Paràmetres del flicker resolts (global + override del YAML +
|
||||
// override de sala). Només té efecte si `light_flicker` és true.
|
||||
Lighting::FlickerConfig flicker_params{};
|
||||
|
||||
// Ha de cridar-se abans d'instanciar Player; GameScene ho fa al ctor.
|
||||
static void loadFrom(const std::string& yaml_path);
|
||||
// ---- Sub-structs de gameplay ---------------------------------
|
||||
|
||||
// Vides amb què arrenca un jugador (camp `lives:` del YAML del
|
||||
// jugador). El consumeix el HUD per inicialitzar el comptador del
|
||||
// marcador i `Player::resetForJoin` per a re-entrades mid-partida.
|
||||
[[nodiscard]] static auto getInitialLives() -> int { return death_config.initial_lives; }
|
||||
// Inèrcia: amb `brake_time_ms == 0` el comportament és instantani.
|
||||
struct InertiaConfig {
|
||||
int brake_time_ms{0};
|
||||
int threshold_ms{0};
|
||||
};
|
||||
|
||||
// Gir de 180°: amb `duration_ms == 0` els canvis de direcció són
|
||||
// instantanis. `Cardinal` ha d'estar visible — declarada a sobre.
|
||||
struct TurnConfig {
|
||||
int duration_ms{0};
|
||||
Cardinal w_to_e{Cardinal::SOUTH};
|
||||
Cardinal e_to_w{Cardinal::NORTH};
|
||||
Cardinal n_to_s{Cardinal::EAST};
|
||||
Cardinal s_to_n{Cardinal::WEST};
|
||||
};
|
||||
|
||||
// Salt: amb `impulse_px_per_s == 0` el botó no té efecte.
|
||||
struct JumpConfig {
|
||||
float impulse_px_per_s{0.0F};
|
||||
float gravity_px_per_s2{0.0F};
|
||||
int landing_stun_ms{0};
|
||||
std::string sound_key;
|
||||
std::string land_sound_key;
|
||||
};
|
||||
|
||||
// HURT (estat post-impacte). Amb `initial_speed_px_per_s == 0`
|
||||
// entrada/sortida instantànies (sense moviment).
|
||||
struct HurtConfig {
|
||||
float initial_speed_px_per_s{0.0F};
|
||||
int brake_time_ms{0};
|
||||
std::string sound_key;
|
||||
};
|
||||
|
||||
// Látigo: hitbox `range_tiles`, swing `swing_ms`, cooldown
|
||||
// `cooldown_ms`. Per ara `damage` només es llegeix.
|
||||
struct WhipConfig {
|
||||
float range_tiles{0.0F};
|
||||
int swing_ms{0};
|
||||
int cooldown_ms{0};
|
||||
int damage{0};
|
||||
std::string sound_key;
|
||||
};
|
||||
|
||||
// Mort/respawn: `respawn_delay_ms` entre l'animació i la
|
||||
// reaparició; `initial_lives` és el comptador d'intents.
|
||||
struct DeathConfig {
|
||||
int respawn_delay_ms{0};
|
||||
int initial_lives{3};
|
||||
};
|
||||
|
||||
// Immunitat post-spawn. `duration_ms == 0` desactiva la mecànica.
|
||||
// `palette_key` (basename del .pal) és la paleta plana del blink.
|
||||
struct ImmunityConfig {
|
||||
float duration_ms{0.0F};
|
||||
int blink_period_ms{0};
|
||||
std::string palette_key;
|
||||
};
|
||||
|
||||
InertiaConfig inertia{};
|
||||
TurnConfig turn{};
|
||||
JumpConfig jump{};
|
||||
HurtConfig hurt{};
|
||||
WhipConfig whip{};
|
||||
DeathConfig death{};
|
||||
ImmunityConfig immunity{};
|
||||
|
||||
// Llig tot el YAML del personatge i retorna un `Config` poblat.
|
||||
// Substitueix l'antic `Player::loadFrom` que mutava estàtics.
|
||||
// Llança si manca el bloc `collision:` (obligatori).
|
||||
[[nodiscard]] static auto loadFromYaml(const std::string& yaml_path) -> Config;
|
||||
|
||||
// Aplica l'override de sala (bloc `lighting.emitters.player` del
|
||||
// YAML del mapa) sobre els camps de llum d'aquesta Config. Es
|
||||
// crida després de `loadFromYaml` i abans de passar la Config al
|
||||
// ctor de Player (o a `setConfig` en transicions de sala).
|
||||
void applyRoomLightOverride();
|
||||
};
|
||||
|
||||
Player(Config cfg, const std::string& anim_key, int id, float x, float y, const Controls& controls, const Map::Info& map_info, int paint_tile_index = -1);
|
||||
|
||||
// Reemplaça el `Config` actual del Player. Cridat per GameScene al
|
||||
// canvi de sala per re-aplicar valors base + override de la sala nova.
|
||||
// No toca cap estat de joc (lives, posició, FSM): només la config.
|
||||
void setConfig(Config cfg);
|
||||
|
||||
// Accés a la config viva (lectura). El callsites que abans accedien
|
||||
// a estàtics fan servir `getters d'instància` (sota); aquest és per
|
||||
// codi que vol l'objecte sencer.
|
||||
[[nodiscard]] auto getConfig() const -> const Config& { return cfg_; }
|
||||
|
||||
// Vides amb què arrenca un jugador (camp `lives:` del YAML). El
|
||||
// consumeix el HUD per inicialitzar el marcador i `resetForJoin`
|
||||
// per a re-entrades mid-partida.
|
||||
[[nodiscard]] auto getInitialLives() const -> int { return cfg_.death.initial_lives; }
|
||||
|
||||
// Geometria del cos — instance getters dels camps de `cfg_` que
|
||||
// alguns callsites externs (game_scene) consulten directament.
|
||||
[[nodiscard]] auto getBodyHeight() const -> int { return cfg_.body_height; }
|
||||
[[nodiscard]] auto getSpriteW() const -> int { return cfg_.sprite_w; }
|
||||
[[nodiscard]] auto getSpriteH() const -> int { return cfg_.sprite_h; }
|
||||
[[nodiscard]] auto getCollisionSize() const -> int { return cfg_.collision_size; }
|
||||
[[nodiscard]] auto getCollisionYOffset() const -> int { return cfg_.collision_y_offset; }
|
||||
|
||||
// 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]] static auto getLightFlicker() -> bool { return light_flicker; }
|
||||
[[nodiscard]] static auto getLightColorR() -> std::uint8_t { return light_color_r; }
|
||||
[[nodiscard]] static auto getLightColorG() -> std::uint8_t { return light_color_g; }
|
||||
[[nodiscard]] static auto getLightColorB() -> std::uint8_t { return light_color_b; }
|
||||
[[nodiscard]] static auto getLightOffsetX() -> int { return light_offset_x; }
|
||||
[[nodiscard]] static auto getLightOffsetY() -> int { return light_offset_y; }
|
||||
[[nodiscard]] static auto getFlickerParams() -> const Lighting::FlickerConfig& { return flicker_params; }
|
||||
[[nodiscard]] auto getLightRadius() const -> int { return cfg_.light_radius; }
|
||||
[[nodiscard]] auto getLightFlicker() const -> bool { return cfg_.light_flicker; }
|
||||
[[nodiscard]] auto getLightColorR() const -> std::uint8_t { return cfg_.light_color_r; }
|
||||
[[nodiscard]] auto getLightColorG() const -> std::uint8_t { return cfg_.light_color_g; }
|
||||
[[nodiscard]] auto getLightColorB() const -> std::uint8_t { return cfg_.light_color_b; }
|
||||
[[nodiscard]] auto getLightOffsetX() const -> int { return cfg_.light_offset_x; }
|
||||
[[nodiscard]] auto getLightOffsetY() const -> int { return cfg_.light_offset_y; }
|
||||
[[nodiscard]] auto getFlickerParams() const -> const Lighting::FlickerConfig& { return cfg_.flicker_params; }
|
||||
// Seed estable per al flicker: reusem `id_` (0 o 1 en multijugador
|
||||
// local) amb un offset gran per evitar col·lisions amb el contador
|
||||
// d'Entity (que arrenca des de 1). Així P1 i P2 oscil·len en fases
|
||||
@@ -173,8 +274,6 @@ class Player : public AnimatedSprite {
|
||||
[[nodiscard]] auto getLightSeed() const -> int { return id_ + 1000; }
|
||||
[[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
|
||||
// escenaris amb un sol jugador. Internament és equivalent a cridar
|
||||
// `tick(dt)` seguit d'`applyMovement(dt)`.
|
||||
@@ -275,7 +374,7 @@ class Player : public AnimatedSprite {
|
||||
// que coincideix amb aquest mètode i s'usa específicament per
|
||||
// marcar la posició de la sombra a nivell de sòl.
|
||||
[[nodiscard]] auto getFeetY() const -> float {
|
||||
return getPosY() + static_cast<float>(collision_y_offset + collision_size);
|
||||
return getPosY() + static_cast<float>(cfg_.collision_y_offset + cfg_.collision_size);
|
||||
}
|
||||
|
||||
// Alies semàntic: els peus del jugador al sòl. Coincideix amb
|
||||
@@ -484,7 +583,7 @@ class Player : public AnimatedSprite {
|
||||
// arbitrària (p. ex. la posició proposada per `tryMove` abans
|
||||
// d'aplicar-la). Evita recalcular `hit_x_offset`/`hit_y_offset` fora
|
||||
// de Player.
|
||||
[[nodiscard]] static auto hitBoxFromBase(const SDL_FRect& base) -> SDL_FRect;
|
||||
[[nodiscard]] auto hitBoxFromBase(const SDL_FRect& base) const -> SDL_FRect;
|
||||
|
||||
// Override del hook de Sprite: `MovingSprite::tryMove` envia aquesta
|
||||
// caixa al callback de col·lisió. La base del Player és més petita
|
||||
@@ -568,6 +667,11 @@ class Player : public AnimatedSprite {
|
||||
// efecte immediat sense haver de tornar a entrar a la GameScene.
|
||||
void refreshControlsFromOptions();
|
||||
|
||||
// Tot el config del personatge (geometria, llum, jump, hurt, whip,
|
||||
// death, immunity, turn, inertia). Llegit del YAML per instància
|
||||
// (P1 i P2 poden tenir valors diferents) i mogut al ctor.
|
||||
Config cfg_;
|
||||
|
||||
int id_{-1};
|
||||
Controls controls_;
|
||||
|
||||
@@ -639,26 +743,6 @@ class Player : public AnimatedSprite {
|
||||
static auto facingSuffix(Facing facing) -> const char*;
|
||||
static auto statePrefix(State state, bool moving, bool in_attack_swing) -> const char*;
|
||||
|
||||
// Cardinal complet (N/S/E/W) usat per a la detecció de girs de 180°.
|
||||
// Pública perquè el `WhipAttack` la passa a `enterHurt` quan colpeja
|
||||
// un altre jugador (friendly fire).
|
||||
public:
|
||||
enum class Cardinal : std::uint8_t { NONE,
|
||||
NORTH,
|
||||
SOUTH,
|
||||
EAST,
|
||||
WEST };
|
||||
|
||||
private:
|
||||
// Configuració d'inèrcia llegida del bloc `inertia:` del YAML. Amb
|
||||
// `brake_time_ms == 0` (o bloc absent) la inèrcia està desactivada i
|
||||
// el comportament és instantani: soltar input para el jugador en sec.
|
||||
struct InertiaConfig {
|
||||
int brake_time_ms{0}; // Temps en ms perquè la velocitat caiga de ±SPEED a 0.
|
||||
int threshold_ms{0}; // Temps mínim en moviment continu per activar la inèrcia.
|
||||
};
|
||||
static InertiaConfig inertia_config;
|
||||
|
||||
// Integra un eix. Si hi ha input en aquest eix, la velocitat física
|
||||
// salta a aquest valor (acceleració instantània). Si no, i hi ha
|
||||
// input en l'altre eix (`any_input == true`), la velocitat d'aquest
|
||||
@@ -666,127 +750,7 @@ class Player : public AnimatedSprite {
|
||||
// soltant l'input — no volem que arrossegue en diagonal. Només quan
|
||||
// l'usuari deixa anar tot l'input (`any_input == false`) s'activa la
|
||||
// inèrcia de frenada (si `motion_time_ms` ha passat el llindar).
|
||||
static void integrateAxis(float& phys, float input, float delta_time, float motion_time_ms, bool any_input);
|
||||
|
||||
// Configuració del gir de 180°, llegida del bloc `turn:` del YAML del
|
||||
// player. Si `duration_ms` és 0 (o el bloc no existeix) la mecànica
|
||||
// queda desactivada i els canvis de direcció són instantanis.
|
||||
struct TurnConfig {
|
||||
int duration_ms{0};
|
||||
Cardinal w_to_e{Cardinal::SOUTH};
|
||||
Cardinal e_to_w{Cardinal::NORTH};
|
||||
Cardinal n_to_s{Cardinal::EAST};
|
||||
Cardinal s_to_n{Cardinal::WEST};
|
||||
};
|
||||
// Definida out-of-class al .cpp: dins de la classe, els initializers
|
||||
// per defecte de TurnConfig encara no són visibles per al compilador.
|
||||
static TurnConfig turn_config;
|
||||
|
||||
// Configuració del salt llegida del bloc `jump:` del YAML del player.
|
||||
// Si `impulse_px_per_s == 0` (o el bloc no existeix) la mecànica del
|
||||
// salt queda desactivada — el botó no té efecte.
|
||||
struct JumpConfig {
|
||||
float impulse_px_per_s{0.0F}; // Velocitat vertical inicial.
|
||||
float gravity_px_per_s2{0.0F}; // Acceleració descendent constant.
|
||||
int landing_stun_ms{0}; // Stun en aterrar (no es mou ni salta).
|
||||
std::string sound_key; // Clau al Resource::Cache del so de despegue (buit = silenci).
|
||||
std::string land_sound_key; // Clau del so d'aterratge (buit = silenci).
|
||||
};
|
||||
static JumpConfig jump_config;
|
||||
|
||||
// Llig el bloc `collision:` del YAML i n'omple les caixes (size/base/
|
||||
// hit) + `body_height`. Llança si el bloc no existeix. Membre estàtic
|
||||
// per accedir als estàtics privats sense exposar-los.
|
||||
static void readCollisionBlock(const fkyaml::node& yaml, const std::string& yaml_path);
|
||||
|
||||
// Llig el bloc `jump:` del YAML i n'omple `jump_config`. Si el bloc
|
||||
// manca, deixa la mecànica del salt desactivada.
|
||||
static void readJumpBlock(const fkyaml::node& yaml);
|
||||
|
||||
// Configuració de l'estat HURT (estat post-impacte). Si
|
||||
// `initial_speed_px_per_s == 0` el jugador entra i surt
|
||||
// instantàniament (cap moviment).
|
||||
struct HurtConfig {
|
||||
float initial_speed_px_per_s{0.0F}; // Velocitat amb què surt projectat (px/s).
|
||||
int brake_time_ms{0}; // Temps en que la velocitat decau a 0.
|
||||
// Fitxer .wav reproduït en entrar a HURT (relatiu a `data/audio/sounds/`).
|
||||
// Mateixa convenció que la resta de sons del jugador: el basename val
|
||||
// de clau al Resource::Cache i de path. Buit o absent = entrada
|
||||
// silenciosa.
|
||||
std::string sound_key;
|
||||
};
|
||||
static HurtConfig hurt_config;
|
||||
|
||||
// Llig el bloc `hurt:` del YAML i n'omple `hurt_config`.
|
||||
static void readHurtBlock(const fkyaml::node& yaml);
|
||||
|
||||
// Configuració del látigo del jugador llegida del bloc `whip:` del
|
||||
// YAML. `range_tiles` defineix la longitud de la hitbox abans de
|
||||
// retallar; `swing_ms` és la durada de l'animació d'atac (des
|
||||
// d'`enterAttacking` fins a desbloquejar el cooldown); `cooldown_ms`
|
||||
// afegeix una fase posterior durant la qual el jugador segueix
|
||||
// bloquejat però la hitbox ja no fa dany. `damage` queda llegit per
|
||||
// a futur; per ara els objectius reben sempre `enterHurt`.
|
||||
struct WhipConfig {
|
||||
float range_tiles{0.0F};
|
||||
int swing_ms{0};
|
||||
int cooldown_ms{0};
|
||||
int damage{0};
|
||||
// Fitxer .wav reproduït al moment de disparar el swing (relatiu a
|
||||
// `data/audio/sounds/`). Convenció: el basename val tant de clau al
|
||||
// Resource::Cache com de path. Buit o absent = swing silenciós.
|
||||
std::string sound_key;
|
||||
};
|
||||
static WhipConfig whip_config;
|
||||
|
||||
// Llig el bloc `whip:` del YAML i n'omple `whip_config`. Si manca el
|
||||
// bloc, els camps queden a 0 i el jugador no pot atacar (el flanc
|
||||
// d'input es perd silenciosament).
|
||||
static void readWhipBlock(const fkyaml::node& yaml);
|
||||
|
||||
// Configuració de mort/respawn llegida del bloc `death:` del YAML.
|
||||
// `respawn_delay_ms` és la pausa entre la fi de `death_spinning` i la
|
||||
// reaparició al spawn; `initial_lives` és el comptador d'intents amb
|
||||
// què naix cada Player. Si el bloc manca, defaults segurs (0/3).
|
||||
struct DeathConfig {
|
||||
int respawn_delay_ms{0};
|
||||
int initial_lives{3};
|
||||
};
|
||||
static DeathConfig death_config;
|
||||
|
||||
// Llig el bloc `death:` del YAML i n'omple `death_config`. Si manca,
|
||||
// `respawn_delay_ms` queda a 0 (el respawn és immediat al final de
|
||||
// l'animació) i `initial_lives` es manté al default (3).
|
||||
static void readDeathBlock(const fkyaml::node& yaml);
|
||||
|
||||
// Configuració de la immunitat post-spawn llegida del bloc `immunity:`
|
||||
// del YAML. `duration_ms` és el temps per defecte que dura cada
|
||||
// immunitat (l'invoca `respawn`). `blink_period_ms` és el mig-cicle del
|
||||
// parpadeig visual: cada `blink_period_ms` ms, el render alterna
|
||||
// entre la paleta normal i la paleta `palette_key`. Si `duration_ms`
|
||||
// és 0 (o el bloc manca) la immunitat queda desactivada i el
|
||||
// comportament és l'antic.
|
||||
struct ImmunityConfig {
|
||||
float duration_ms{0.0F};
|
||||
int blink_period_ms{0};
|
||||
// Clau al Resource::Cache de la paleta plana usada al blink (basename
|
||||
// del .pal a `data/entities/player/`). Buida = no hi ha alternança visual,
|
||||
// només la finestra lògica d'immunitat (que sense feedback visual
|
||||
// pot ser confusa, però no és incorrecte).
|
||||
std::string palette_key;
|
||||
};
|
||||
static ImmunityConfig immunity_config;
|
||||
|
||||
// Llig el bloc `immunity:` del YAML i n'omple `immunity_config`. Si
|
||||
// hi ha `palette`, registra la paleta corresponent al Resource::Cache
|
||||
// (path: `data/entities/player/<palette_key>.pal`). Mateix patró que els altres
|
||||
// readers — `loadFrom` el crida al final.
|
||||
static void readImmunityBlock(const fkyaml::node& yaml);
|
||||
|
||||
// Llig el bloc opcional `light: { radius: N, flicker: bool }` del
|
||||
// YAML i pobla `light_radius` / `light_flicker`. Si manca, els
|
||||
// valors queden a 0 / false (jugador sense llum).
|
||||
static void readLightBlock(const fkyaml::node& yaml);
|
||||
void integrateAxis(float& phys, float input, float delta_time, float motion_time_ms, bool any_input) const;
|
||||
|
||||
// Activa l'estat ATTACKING en la direcció cardinal indicada (la
|
||||
// direcció cap on mira el jugador) i publica `whip_spawn` al bus
|
||||
@@ -992,10 +956,16 @@ class Player : public AnimatedSprite {
|
||||
// Extret de `update` per acotar la complexitat cognitiva (clang-tidy).
|
||||
void updateFacingAndTurn(float delta_time, float vx, float vy);
|
||||
static auto cardinalFromInput(float vx, float vy) -> Cardinal;
|
||||
static auto cardinalFromString(const std::string& s) -> Cardinal;
|
||||
static auto opposite(Cardinal c) -> Cardinal;
|
||||
static auto intermediateFor(Cardinal target) -> Cardinal;
|
||||
auto intermediateFor(Cardinal target) const -> Cardinal;
|
||||
|
||||
public:
|
||||
// Helper pur: tradueix un string YAML ("north", "south", ...) al
|
||||
// Cardinal corresponent. Públic perquè `Config::loadFromYaml` el crida
|
||||
// des d'un helper de la TU anònima a player.cpp.
|
||||
static auto cardinalFromString(const std::string& s) -> Cardinal;
|
||||
|
||||
private:
|
||||
// Bus del nivell (opcional). Propietat externa (EntityManager); el
|
||||
// Player només en guarda un punter per publicar events.
|
||||
EventBus* bus_{nullptr};
|
||||
|
||||
Reference in new issue
Block a user