diff --git a/data/player/morcus.yaml b/data/player/morcus.yaml index 5c16c0e..7ef9949 100644 --- a/data/player/morcus.yaml +++ b/data/player/morcus.yaml @@ -15,6 +15,11 @@ collision: # `size` es reserva per a la col·lisió amb murs/sarcòfags i per enfilar # els passadissos. Centrada en el mateix punt que la caixa base. hit_size: 8 + # Alçada del cos en píxels per al filtre de col·lisió per altura: un + # actor saltant amb jump_z > height passa per damunt sense col·lisió. + # Mateix camp i semàntica que als YAML d'entitats (mummy, rolling_rock, + # etc.) — així el filtre queda uniforme per a tots els colls·lisionables. + height: 24 # Animacions en mapa (clau = nom). Camps per animació: # frames → llista d'índexs dins de l'spritesheet. @@ -73,12 +78,10 @@ turn: # gravity → acceleració descendent constant (px/s²). # landing_stun_ms → temps en què el jugador queda paralitzat en aterrar # (no es pot moure ni tornar a saltar). -# height → alçada del cos del jugador en píxels. S'usa per al -# filtratge de col·lisions per altura: un jugador -# saltant per damunt d'aquest valor no col·lisiona. +# L'alçada del cos viu al bloc `collision:` (`height`), igual que la resta +# d'entitats: és una propietat de col·lisió, no de la física del salt. # Amb impulse=240 i gravity=600: pic ≈ 48 px, temps total en l'aire ≈ 0.8 s. jump: impulse: 240 gravity: 600 landing_stun_ms: 200 - height: 24 diff --git a/data/player/sam.yaml b/data/player/sam.yaml index 12f2f13..6380486 100644 --- a/data/player/sam.yaml +++ b/data/player/sam.yaml @@ -15,6 +15,11 @@ collision: # `size` es reserva per a la col·lisió amb murs/sarcòfags i per enfilar # els passadissos. Centrada en el mateix punt que la caixa base. hit_size: 8 + # Alçada del cos en píxels per al filtre de col·lisió per altura: un + # actor saltant amb jump_z > height passa per damunt sense col·lisió. + # Mateix camp i semàntica que als YAML d'entitats (mummy, rolling_rock, + # etc.) — així el filtre queda uniforme per a tots els colls·lisionables. + height: 24 # Animacions en mapa (clau = nom). Camps per animació: # frames → llista d'índexs dins de l'spritesheet. @@ -73,12 +78,10 @@ turn: # gravity → acceleració descendent constant (px/s²). # landing_stun_ms → temps en què el jugador queda paralitzat en aterrar # (no es pot moure ni tornar a saltar). -# height → alçada del cos del jugador en píxels. S'usa per al -# filtratge de col·lisions per altura: un jugador -# saltant per damunt d'aquest valor no col·lisiona. +# L'alçada del cos viu al bloc `collision:` (`height`), igual que la resta +# d'entitats: és una propietat de col·lisió, no de la física del salt. # Amb impulse=240 i gravity=600: pic ≈ 48 px, temps total en l'aire ≈ 0.8 s. jump: impulse: 240 gravity: 600 landing_stun_ms: 200 - height: 24 diff --git a/source/game/entities/player.cpp b/source/game/entities/player.cpp index 71099a5..b2265b6 100644 --- a/source/game/entities/player.cpp +++ b/source/game/entities/player.cpp @@ -77,28 +77,7 @@ void Player::loadFrom(const std::string& yaml_path) { readSpriteSize(yaml, yaml_path, sprite_w, sprite_h); - if (!yaml.contains("collision")) { - throw std::runtime_error("Player: manca bloc `collision:` a " + yaml_path); - } - const auto& c = yaml["collision"]; - collision_size = c["size"].get_value(); - base_size = c["base_size"].get_value(); - - // Col·lisió centrada en el quadrat inferior del sprite. Si la caixa és - // més gran que la base (cas habitual, 16 vs 12) el padding és simètric - // a cada costat; si són iguals, padding=0 i la caixa coincideix amb la - // base sense offsets. - collision_padding = (collision_size - base_size) / 2; - collision_x_offset = -collision_padding; - collision_y_offset = (sprite_h - base_size) - collision_padding; - - // Hit-box opcional (actor-actor). Si no hi és, s'iguala a la base - // per preservar compatibilitat amb YAMLs antics. Concèntrica amb la - // caixa de col·lisió: offset interior = (collision_size - hit_size) / 2. - hit_size = c.contains("hit_size") ? c["hit_size"].get_value() : collision_size; - const int HIT_INSET = (collision_size - hit_size) / 2; - hit_x_offset = collision_x_offset + HIT_INSET; - hit_y_offset = collision_y_offset + HIT_INSET; + readCollisionBlock(yaml, yaml_path); // Bloc opcional `inertia:` — si manca, `brake_time_ms` queda a 0 i // la inèrcia de frenada no s'activa (parada instantània com abans). @@ -130,15 +109,43 @@ void Player::loadFrom(const std::string& yaml_path) { readJumpBlock(yaml); } +void Player::readCollisionBlock(const fkyaml::node& yaml, const std::string& yaml_path) { + if (!yaml.contains("collision")) { + throw std::runtime_error("Player: manca bloc `collision:` a " + yaml_path); + } + const auto& c = yaml["collision"]; + collision_size = c["size"].get_value(); + base_size = c["base_size"].get_value(); + + // Col·lisió centrada en el quadrat inferior del sprite. Si la caixa és + // més gran que la base (cas habitual, 16 vs 12) el padding és simètric + // a cada costat; si són iguals, padding=0 i la caixa coincideix amb la + // base sense offsets. + collision_padding = (collision_size - base_size) / 2; + collision_x_offset = -collision_padding; + collision_y_offset = (sprite_h - base_size) - collision_padding; + + // Hit-box opcional (actor-actor). Si no hi és, s'iguala a la base + // per preservar compatibilitat amb YAMLs antics. Concèntrica amb la + // caixa de col·lisió: offset interior = (collision_size - hit_size) / 2. + hit_size = c.contains("hit_size") ? c["hit_size"].get_value() : collision_size; + const int HIT_INSET = (collision_size - hit_size) / 2; + hit_x_offset = collision_x_offset + HIT_INSET; + hit_y_offset = collision_y_offset + HIT_INSET; + + // Alçada del cos, llegida del mateix bloc per coherència amb la + // resta d'entitats (mummy, sarcophagus, etc.). 0 = sense filtre per + // altura — el salt no esquiva l'altre jugador. + body_height = c.contains("height") ? c["height"].get_value() : 0; +} + void Player::readJumpBlock(const fkyaml::node& yaml) { jump_config = JumpConfig{}; - body_height = 0; if (!yaml.contains("jump")) { return; } const auto& j = yaml["jump"]; if (j.contains("impulse")) { jump_config.impulse_px_per_s = j["impulse"].get_value(); } if (j.contains("gravity")) { jump_config.gravity_px_per_s2 = j["gravity"].get_value(); } if (j.contains("landing_stun_ms")) { jump_config.landing_stun_ms = j["landing_stun_ms"].get_value(); } - if (j.contains("height")) { body_height = j["height"].get_value(); } } void Player::applyCardinal(Cardinal c) { diff --git a/source/game/entities/player.hpp b/source/game/entities/player.hpp index 5ef685f..6b1c17c 100644 --- a/source/game/entities/player.hpp +++ b/source/game/entities/player.hpp @@ -66,7 +66,8 @@ class Player : public AnimatedSprite { inline static int hit_x_offset = 0; inline static int hit_y_offset = 0; - // Alçada del cos del jugador en píxels (camp `jump.height` del YAML). + // 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; @@ -370,8 +371,13 @@ class Player : public AnimatedSprite { }; static JumpConfig jump_config; - // Llig el bloc `jump:` del YAML i n'omple `jump_config` + `body_height`. - // Membre estàtic perquè accedeixi als estàtics privats sense exposar-los. + // 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); // Estat de la màquina d'estats. Mutat per `tick`/`applyMovement`. Per