feat(hazard): afegeix estat SPAWN a DynamicHazard
DynamicHazard guanya un mecanisme de SPAWN amb dues fases: SPAWNING (visible, sense moviment ni dany, comptador en pausa) i ACTIVE (lògica normal). `enterSpawn(duration_s)` deixa l'entitat congelada esperant indefinidament; `triggerSpawnExit()` arma el comptador i, en arribar a zero, transita a ACTIVE. Per defecte les noves DynamicHazard arrenquen en ACTIVE — backward-compat amb subclasses existents (RollingRock) que encara no usen el mecanisme. EntityManager::checkPlayerCollisions salta DynamicHazards en SPAWN: no disparen onPlayerContact, així no fan dany ni triggers durant la fase. Aquest commit només afegeix la infraestructura — cap subclasse l'usa encara, comportament del joc inalterat. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
b7811180ba
commit
a6b2b02930
2 files changed
+51
No files matched your search
@@ -18,6 +18,7 @@
|
||||
#include "game/entities/entity_factory.hpp"
|
||||
#include "game/entities/entity_loader.hpp"
|
||||
#include "game/entities/entity_manager.hpp"
|
||||
#include "game/entities/hazard.hpp"
|
||||
#include "game/entities/player.hpp"
|
||||
#include "game/map/map.hpp"
|
||||
#include "utils/defaults.hpp"
|
||||
@@ -68,6 +69,10 @@ void EntityManager::checkPlayerCollisions(const std::vector<Player*>& players) {
|
||||
// mai no es disparaven) i obrim la porta a hazards no-físics
|
||||
// com `Hole`.
|
||||
if (!entity->collides() && !entity->triggersOnOverlap()) { continue; }
|
||||
// DynamicHazard en SPAWN: visible però "no juga" — no dispara
|
||||
// onPlayerContact, així no fa dany ni triggers durant la fase
|
||||
// inicial de ROOM_INTRO (o futurs spawns en runtime).
|
||||
if (const auto* dh = dynamic_cast<const DynamicHazard*>(entity.get()); dh != nullptr && dh->isSpawning()) { continue; }
|
||||
const auto BOX = entity->getCollisionRect();
|
||||
const int E_HEIGHT = entity->getHeight();
|
||||
for (auto* p : players) {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "game/entities/entity.hpp"
|
||||
|
||||
// Base d'entitats perilloses: al contacte amb el jugador, apliquen dany.
|
||||
@@ -56,8 +58,20 @@ class StaticHazard : public Hazard {
|
||||
// Perill dinàmic: es mou. Velocitat expressada en píxels/segon i una
|
||||
// direcció (vec2). Les subclasses decideixen la política de moviment
|
||||
// (IA corridor_wanderer, línia recta, etc.) sobreescribint `step(dt)`.
|
||||
//
|
||||
// Estat SPAWN: tota DynamicHazard pot estar en SPAWNING (visible, sense
|
||||
// moviment ni dany) fins que algú extern arme el countdown amb
|
||||
// `triggerSpawnExit()`. Quan el comptador arriba a zero passa a ACTIVE i
|
||||
// recupera la lògica normal (`step()` per frame). Per defecte arrenca en
|
||||
// ACTIVE — backward-compat amb subclasses que encara no usen el mecanisme.
|
||||
// El filtre central de col·lisió a `EntityManager::checkPlayerCollisions`
|
||||
// salta les entitats en SPAWN: per això el contacte no fa dany ni dispara
|
||||
// triggers durant aquesta fase.
|
||||
class DynamicHazard : public Hazard {
|
||||
public:
|
||||
enum class SpawnState : std::uint8_t { SPAWNING,
|
||||
ACTIVE };
|
||||
|
||||
DynamicHazard() = default;
|
||||
|
||||
void setSpeed(float px_per_s) { speed_ = px_per_s; }
|
||||
@@ -70,6 +84,21 @@ class DynamicHazard : public Hazard {
|
||||
[[nodiscard]] auto getDirX() const -> float { return dir_x_; }
|
||||
[[nodiscard]] auto getDirY() const -> float { return dir_y_; }
|
||||
|
||||
// Entra en SPAWNING amb una duració determinada. El comptador NO
|
||||
// descompta fins que algú crida `triggerSpawnExit()` — l'entitat queda
|
||||
// visible i quieta esperant indefinidament.
|
||||
void enterSpawn(float duration_s) {
|
||||
spawn_state_ = SpawnState::SPAWNING;
|
||||
spawn_duration_s_ = duration_s;
|
||||
spawn_remaining_s_ = duration_s;
|
||||
spawn_exit_armed_ = false;
|
||||
}
|
||||
// Arma el comptador del SPAWN. A partir d'aquí, cada `update(dt)`
|
||||
// descompta `dt` fins arribar a 0 i transitar a ACTIVE.
|
||||
void triggerSpawnExit() { spawn_exit_armed_ = true; }
|
||||
[[nodiscard]] auto isSpawning() const -> bool { return spawn_state_ == SpawnState::SPAWNING; }
|
||||
[[nodiscard]] auto getSpawnDurationS() const -> float { return spawn_duration_s_; }
|
||||
|
||||
// Política de moviment per frame. La base desplaça el pixel_rect_
|
||||
// proporcionalment a velocitat i direcció; les subclasses amb IA la
|
||||
// sobreescriuen per triar nova direcció a les interseccions o rebotar.
|
||||
@@ -79,6 +108,19 @@ class DynamicHazard : public Hazard {
|
||||
}
|
||||
|
||||
void update(float delta_time) override {
|
||||
if (spawn_state_ == SpawnState::SPAWNING) {
|
||||
// Sense `step()`: l'entitat es queda quieta al pixel_rect_
|
||||
// inicial. `Entity::update` tic d'animació igual: si en algun
|
||||
// moment posem un GIF de spawn aquí ja anirà.
|
||||
Entity::update(delta_time);
|
||||
if (spawn_exit_armed_) {
|
||||
spawn_remaining_s_ -= delta_time;
|
||||
if (spawn_remaining_s_ <= 0.0F) {
|
||||
spawn_state_ = SpawnState::ACTIVE;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
Entity::update(delta_time);
|
||||
step(delta_time);
|
||||
}
|
||||
@@ -87,4 +129,8 @@ class DynamicHazard : public Hazard {
|
||||
float speed_{0.0F};
|
||||
float dir_x_{0.0F};
|
||||
float dir_y_{0.0F};
|
||||
SpawnState spawn_state_{SpawnState::ACTIVE};
|
||||
float spawn_duration_s_{0.0F};
|
||||
float spawn_remaining_s_{0.0F};
|
||||
bool spawn_exit_armed_{false};
|
||||
};
|
||||
Reference in new issue
Block a user