Compare commits

...

7 Commits

20 changed files with 302 additions and 141 deletions

61
TODO.md Normal file
View File

@@ -0,0 +1,61 @@
# TODO
## Tareas pendientes
- [ ] Revisar todas las variables static de los métodos para ver si se resetean correctamente
## Mejoras arquitecturales (refactoring)
### Eliminar variables static locales y usar patrones profesionales:
**Opción 1: Máquina de Estados**
```cpp
class GameCompletedState {
bool start_celebrations_done = false;
bool end_celebrations_done = false;
float timer = 0.0f;
public:
void reset() {
start_celebrations_done = false;
end_celebrations_done = false;
timer = 0.0f;
}
void update(float deltaTime) {
timer += deltaTime;
// lógica aquí
}
};
```
**Opción 2: Sistema de Eventos/Callbacks**
```cpp
// Al entrar en COMPLETED state
eventSystem.scheduleEvent(6.0f, []{ startCelebrations(); });
eventSystem.scheduleEvent(14.0f, []{ endCelebrations(); });
```
**Opción 3: Flags como miembros privados**
```cpp
class Game {
private:
struct GameOverState {
bool game_over_triggered = false;
bool start_celebrations_triggered = false;
bool end_celebrations_triggered = false;
void reset() {
game_over_triggered = false;
start_celebrations_triggered = false;
end_celebrations_triggered = false;
}
} game_over_state_;
};
```
**Ventajas:**
- Más fáciles de testear
- Más fáciles de debugear
- Más fáciles de entender y mantener
- No tienen "estado oculto"

View File

@@ -22,6 +22,7 @@ DEMODATA|${PREFIX}/data/demo/demo1.bin
DEMODATA|${PREFIX}/data/demo/demo2.bin DEMODATA|${PREFIX}/data/demo/demo2.bin
# Música # Música
MUSIC|${PREFIX}/data/music/congratulations.ogg
MUSIC|${PREFIX}/data/music/credits.ogg MUSIC|${PREFIX}/data/music/credits.ogg
MUSIC|${PREFIX}/data/music/intro.ogg MUSIC|${PREFIX}/data/music/intro.ogg
MUSIC|${PREFIX}/data/music/playing.ogg MUSIC|${PREFIX}/data/music/playing.ogg
@@ -48,6 +49,7 @@ SOUND|${PREFIX}/data/sound/item_drop.wav
SOUND|${PREFIX}/data/sound/item_pickup.wav SOUND|${PREFIX}/data/sound/item_pickup.wav
SOUND|${PREFIX}/data/sound/jump.wav SOUND|${PREFIX}/data/sound/jump.wav
SOUND|${PREFIX}/data/sound/logo.wav SOUND|${PREFIX}/data/sound/logo.wav
SOUND|${PREFIX}/data/sound/name_input_accept.wav
SOUND|${PREFIX}/data/sound/notify.wav SOUND|${PREFIX}/data/sound/notify.wav
SOUND|${PREFIX}/data/sound/player_collision.wav SOUND|${PREFIX}/data/sound/player_collision.wav
SOUND|${PREFIX}/data/sound/power_ball_explosion.wav SOUND|${PREFIX}/data/sound/power_ball_explosion.wav

View File

@@ -109,7 +109,7 @@ frames=38,39,40,41
[animation] [animation]
name=celebration name=celebration
speed=0.167 speed=0.167
loop=-1 loop=0
frames=42,42,42,42,42,42,43,44,45,46,46,46,46,46,46,45,45,45,46,46,46,45,45,45,44,43,42,42,42 frames=42,42,42,42,42,42,43,44,45,46,46,46,46,46,46,45,45,45,46,46,46,45,45,45,44,43,42,42,42
[/animation] [/animation]

View File

@@ -27,6 +27,7 @@
"[GAME_TEXT] 7": "Endavant!", "[GAME_TEXT] 7": "Endavant!",
"[GAME_TEXT] 8": "1.000.000 de punts!", "[GAME_TEXT] 8": "1.000.000 de punts!",
"[GAME_TEXT] THANK_YOU": "Gracies!", "[GAME_TEXT] THANK_YOU": "Gracies!",
"[GAME_TEXT] NEW_RECORD": "Nou record!",
"[HIGHSCORE_TABLE] CAPTION": "Millors puntuacions", "[HIGHSCORE_TABLE] CAPTION": "Millors puntuacions",

View File

@@ -26,6 +26,7 @@
"[GAME_TEXT] 7": "Get Ready!", "[GAME_TEXT] 7": "Get Ready!",
"[GAME_TEXT] 8": "1,000,000 points!", "[GAME_TEXT] 8": "1,000,000 points!",
"[GAME_TEXT] THANK_YOU": "Thank you!", "[GAME_TEXT] THANK_YOU": "Thank you!",
"[GAME_TEXT] NEW_RECORD": "New record!",
"[HIGHSCORE_TABLE] CAPTION": "Best scores", "[HIGHSCORE_TABLE] CAPTION": "Best scores",

View File

@@ -26,6 +26,7 @@
"[GAME_TEXT] 7": "Adelante!", "[GAME_TEXT] 7": "Adelante!",
"[GAME_TEXT] 8": "1.000.000 de puntos!", "[GAME_TEXT] 8": "1.000.000 de puntos!",
"[GAME_TEXT] THANK_YOU": "Gracias!", "[GAME_TEXT] THANK_YOU": "Gracias!",
"[GAME_TEXT] NEW_RECORD": "Nuevo record!",
"[HIGHSCORE_TABLE] CAPTION": "Mejores puntuaciones", "[HIGHSCORE_TABLE] CAPTION": "Mejores puntuaciones",

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -100,13 +100,33 @@ void Audio::stopAllSounds() const {
// Realiza un fundido de salida de la música // Realiza un fundido de salida de la música
void Audio::fadeOutMusic(int milliseconds) const { void Audio::fadeOutMusic(int milliseconds) const {
if (music_enabled_ && music_.state == MusicState::PLAYING) { if (music_enabled_ && getRealMusicState() == MusicState::PLAYING) {
#ifndef NO_AUDIO #ifndef NO_AUDIO
JA_FadeOutMusic(milliseconds); JA_FadeOutMusic(milliseconds);
#endif #endif
} }
} }
// Consulta directamente el estado real de la música en jailaudio
auto Audio::getRealMusicState() const -> MusicState {
#ifndef NO_AUDIO
JA_Music_state ja_state = JA_GetMusicState();
switch (ja_state) {
case JA_MUSIC_PLAYING:
return MusicState::PLAYING;
case JA_MUSIC_PAUSED:
return MusicState::PAUSED;
case JA_MUSIC_STOPPED:
case JA_MUSIC_INVALID:
case JA_MUSIC_DISABLED:
default:
return MusicState::STOPPED;
}
#else
return MusicState::STOPPED;
#endif
}
// Establece el volumen de los sonidos // Establece el volumen de los sonidos
void Audio::setSoundVolume(int sound_volume, Group group) const { void Audio::setSoundVolume(int sound_volume, Group group) const {
if (sound_enabled_) { if (sound_enabled_) {

View File

@@ -13,6 +13,12 @@ class Audio {
INTERFACE = 1 // Sonidos de la interfaz INTERFACE = 1 // Sonidos de la interfaz
}; };
enum class MusicState {
PLAYING, // Reproduciendo música
PAUSED, // Música pausada
STOPPED, // Música detenida
};
// --- Constantes --- // --- Constantes ---
static constexpr int MAX_VOLUME = 100; // Volumen máximo static constexpr int MAX_VOLUME = 100; // Volumen máximo
static constexpr int MIN_VOLUME = 0; // Volumen mínimo static constexpr int MIN_VOLUME = 0; // Volumen mínimo
@@ -60,14 +66,15 @@ class Audio {
void setSoundVolume(int volume, Group group = Group::ALL) const; // Ajustar volumen de efectos void setSoundVolume(int volume, Group group = Group::ALL) const; // Ajustar volumen de efectos
void setMusicVolume(int volume) const; // Ajustar volumen de música void setMusicVolume(int volume) const; // Ajustar volumen de música
private: // --- Getters para debug ---
// --- Enums privados --- bool isEnabled() const { return enabled_; }
enum class MusicState { bool isSoundEnabled() const { return sound_enabled_; }
PLAYING, // Reproduciendo música bool isMusicEnabled() const { return music_enabled_; }
PAUSED, // Música pausada MusicState getMusicState() const { return music_.state; }
STOPPED, // Música detenida MusicState getRealMusicState() const; // Consulta directamente a jailaudio
}; const std::string& getCurrentMusicName() const { return music_.name; }
private:
// --- Estructuras privadas --- // --- Estructuras privadas ---
struct Music { struct Music {
MusicState state; // Estado actual de la música (reproduciendo, detenido, en pausa) MusicState state; // Estado actual de la música (reproduciendo, detenido, en pausa)

View File

@@ -42,7 +42,7 @@ Director::Director(int argc, std::span<char *> argv) {
Section::name = Section::Name::GAME; Section::name = Section::Name::GAME;
Section::options = Section::Options::GAME_PLAY_1P; Section::options = Section::Options::GAME_PLAY_1P;
#elif _DEBUG #elif _DEBUG
Section::name = Section::Name::TITLE; Section::name = Section::Name::GAME;
Section::options = Section::Options::GAME_PLAY_1P; Section::options = Section::Options::GAME_PLAY_1P;
#else // NORMAL GAME #else // NORMAL GAME
Section::name = Section::Name::LOGO; Section::name = Section::Name::LOGO;

View File

@@ -37,19 +37,19 @@ class Fade {
~Fade(); ~Fade();
// --- Métodos principales --- // --- Métodos principales ---
void reset(); // Resetea variables para reutilizar el fade void reset(); // Resetea variables para reutilizar el fade
void render(); // Dibuja la transición en pantalla void render(); // Dibuja la transición en pantalla
void update(); // Actualiza el estado interno (ya usa tiempo real) void update(); // Actualiza el estado interno (ya usa tiempo real)
void update(float delta_time); // Compatibilidad delta-time (ignora el parámetro) void update(float delta_time); // Compatibilidad delta-time (ignora el parámetro)
void activate(); // Activa el fade void activate(); // Activa el fade
// --- Configuración --- // --- Configuración ---
void setColor(Uint8 r, Uint8 g, Uint8 b); // Establece el color RGB del fade void setColor(Uint8 r, Uint8 g, Uint8 b); // Establece el color RGB del fade
void setColor(Color color); // Establece el color del fade void setColor(Color color); // Establece el color del fade
void setType(Type type) { type_ = type; } // Establece el tipo de fade void setType(Type type) { type_ = type; } // Establece el tipo de fade
void setMode(Mode mode) { mode_ = mode; } // Establece el modo de fade void setMode(Mode mode) { mode_ = mode; } // Establece el modo de fade
void setPostDuration(int value) { post_duration_ = value; } // Duración posterior al fade en milisegundos void setPostDuration(int milliseconds) { post_duration_ = milliseconds; } // Duración posterior al fade en milisegundos
void setPreDuration(int value) { pre_duration_ = value; } // Duración previa al fade en milisegundos void setPreDuration(int milliseconds) { pre_duration_ = milliseconds; } // Duración previa al fade en milisegundos
// --- Getters --- // --- Getters ---
[[nodiscard]] auto getValue() const -> int { return value_; } [[nodiscard]] auto getValue() const -> int { return value_; }

View File

@@ -24,6 +24,32 @@ void ManageHiScoreTable::clear() {
table_.emplace_back("PACMQ", 200); table_.emplace_back("PACMQ", 200);
table_.emplace_back("PELEC", 100); table_.emplace_back("PELEC", 100);
/*
table_.emplace_back("BRY", 1000);
table_.emplace_back("USUFO", 500);
table_.emplace_back("GLUCA", 100);
table_.emplace_back("PARRA", 50);
table_.emplace_back("CAGAM", 10);
table_.emplace_back("PEPE", 5);
table_.emplace_back("ROSIT", 4);
table_.emplace_back("SAM", 3);
table_.emplace_back("PACMQ", 2);
table_.emplace_back("PELEC", 1);
*/
/*
table_.emplace_back("BRY", 5000000);
table_.emplace_back("USUFO", 5000000);
table_.emplace_back("GLUCA", 5000000);
table_.emplace_back("PARRA", 5000000);
table_.emplace_back("CAGAM", 5000000);
table_.emplace_back("PEPE", 5000000);
table_.emplace_back("ROSIT", 5000000);
table_.emplace_back("SAM", 5000000);
table_.emplace_back("PACMQ", 5000000);
table_.emplace_back("PELEC", 5000000);
*/
sort(); sort();
} }

View File

@@ -149,7 +149,7 @@ void Player::setInputEnteringName(Input::Action action) {
name_entry_idle_time_accumulator_ = 0.0f; name_entry_idle_time_accumulator_ = 0.0f;
} }
// Fase 1: Sistema de movimiento time-based // Sistema de movimiento
void Player::move(float deltaTime) { void Player::move(float deltaTime) {
switch (playing_state_) { switch (playing_state_) {
case State::PLAYING: case State::PLAYING:
@@ -356,7 +356,7 @@ void Player::handleCreditsLeftMovement() {
// Controla la animación del jugador saludando (time-based) // Controla la animación del jugador saludando (time-based)
void Player::handleWaitingMovement(float deltaTime) { void Player::handleWaitingMovement(float deltaTime) {
waiting_time_accumulator_ += deltaTime; waiting_time_accumulator_ += deltaTime;
const float WAITING_DURATION_S = static_cast<float>(WAITING_COUNTER) / 60.0f; // Convert frames to seconds const float WAITING_DURATION_S = static_cast<float>(WAITING_COUNTER) / 60.0f; // Convert frames to seconds
if (waiting_time_accumulator_ >= WAITING_DURATION_S) { if (waiting_time_accumulator_ >= WAITING_DURATION_S) {
waiting_time_accumulator_ = 0.0f; waiting_time_accumulator_ = 0.0f;
player_sprite_->resetAnimation(); player_sprite_->resetAnimation();
@@ -387,7 +387,7 @@ void Player::setInputBasedOnPlayerId() {
// Incrementa o ajusta el contador de pasos (time-based) // Incrementa o ajusta el contador de pasos (time-based)
void Player::updateStepCounter(float deltaTime) { void Player::updateStepCounter(float deltaTime) {
step_time_accumulator_ += deltaTime; step_time_accumulator_ += deltaTime;
const float STEP_INTERVAL_S = 10.0f / 60.0f; // 10 frames converted to seconds const float STEP_INTERVAL_S = 10.0f / 60.0f; // 10 frames converted to seconds
if (step_time_accumulator_ >= STEP_INTERVAL_S) { if (step_time_accumulator_ >= STEP_INTERVAL_S) {
step_time_accumulator_ = 0.0f; step_time_accumulator_ = 0.0f;
playSound("walk.wav"); playSound("walk.wav");
@@ -398,16 +398,16 @@ void Player::updateStepCounter(float deltaTime) {
void Player::render() { void Player::render() {
if (power_up_ && isPlaying()) { if (power_up_ && isPlaying()) {
// Convertir lógica de parpadeo a deltaTime en segundos // Convertir lógica de parpadeo a deltaTime en segundos
const float TOTAL_POWERUP_TIME_S = static_cast<float>(POWERUP_COUNTER) / 60.0f; // Total time in seconds const float TOTAL_POWERUP_TIME_S = static_cast<float>(POWERUP_COUNTER) / 60.0f; // Total time in seconds
const float QUARTER_TIME_S = TOTAL_POWERUP_TIME_S / 4.0f; // 25% del tiempo total const float QUARTER_TIME_S = TOTAL_POWERUP_TIME_S / 4.0f; // 25% del tiempo total
if (power_up_time_accumulator_ > QUARTER_TIME_S) { if (power_up_time_accumulator_ > QUARTER_TIME_S) {
// En los primeros 75% del tiempo, siempre visible // En los primeros 75% del tiempo, siempre visible
power_sprite_->render(); power_sprite_->render();
} else { } else {
// En el último 25%, parpadea cada 20 frames (≈0.333s) // En el último 25%, parpadea cada 20 frames (≈0.333s)
constexpr float BLINK_PERIOD_S = 20.0f / 60.0f; // 20 frames in seconds constexpr float BLINK_PERIOD_S = 20.0f / 60.0f; // 20 frames in seconds
constexpr float VISIBLE_PROPORTION = 4.0f / 20.0f; // 4 frames visible de 20 total constexpr float VISIBLE_PROPORTION = 4.0f / 20.0f; // 4 frames visible de 20 total
float cycle_position = fmod(power_up_time_accumulator_, BLINK_PERIOD_S) / BLINK_PERIOD_S; float cycle_position = fmod(power_up_time_accumulator_, BLINK_PERIOD_S) / BLINK_PERIOD_S;
if (cycle_position >= VISIBLE_PROPORTION) { if (cycle_position >= VISIBLE_PROPORTION) {
@@ -475,11 +475,10 @@ auto Player::computeAnimation() const -> std::pair<std::string, SDL_FlipMode> {
return {anim_name, flip_mode}; return {anim_name, flip_mode};
} }
// Fase 1: Establece la animación correspondiente al estado (time-based) // Establece la animación correspondiente al estado
void Player::setAnimation(float deltaTime) { void Player::setAnimation(float deltaTime) {
switch (playing_state_) { switch (playing_state_) {
case State::PLAYING: case State::PLAYING:
case State::ENTERING_NAME_GAME_COMPLETED:
case State::ENTERING_SCREEN: case State::ENTERING_SCREEN:
case State::LEAVING_SCREEN: case State::LEAVING_SCREEN:
case State::TITLE_ANIMATION: case State::TITLE_ANIMATION:
@@ -505,6 +504,7 @@ void Player::setAnimation(float deltaTime) {
case State::CONTINUE: case State::CONTINUE:
player_sprite_->setCurrentAnimation("dizzy"); player_sprite_->setCurrentAnimation("dizzy");
break; break;
case State::ENTERING_NAME_GAME_COMPLETED:
case State::CELEBRATING: case State::CELEBRATING:
player_sprite_->setCurrentAnimation("celebration"); player_sprite_->setCurrentAnimation("celebration");
break; break;
@@ -519,16 +519,16 @@ void Player::setAnimation(float deltaTime) {
// Actualiza al jugador con deltaTime (time-based) // Actualiza al jugador con deltaTime (time-based)
void Player::update(float deltaTime) { void Player::update(float deltaTime) {
move(deltaTime); // Sistema de movimiento time-based move(deltaTime); // Sistema de movimiento time-based
setAnimation(deltaTime); // Animaciones time-based setAnimation(deltaTime); // Animaciones time-based
shiftColliders(); // Sin cambios (posicional) shiftColliders(); // Sin cambios (posicional)
updateFireSystem(deltaTime); // Sistema de disparo de dos líneas updateFireSystem(deltaTime); // Sistema de disparo de dos líneas
updatePowerUp(deltaTime); // Sistema de power-up time-based updatePowerUp(deltaTime); // Sistema de power-up time-based
updateInvulnerable(deltaTime); // Sistema de invulnerabilidad time-based updateInvulnerable(deltaTime); // Sistema de invulnerabilidad time-based
updateScoreboard(); // Sin cambios (no temporal) updateScoreboard(); // Sin cambios (no temporal)
updateContinueCounter(deltaTime); // Sistema de continue time-based updateContinueCounter(deltaTime); // Sistema de continue time-based
updateEnterNameCounter(deltaTime); // Sistema de name entry time-based updateEnterNameCounter(deltaTime); // Sistema de name entry time-based
updateShowingName(deltaTime); // Sistema de showing name time-based updateShowingName(deltaTime); // Sistema de showing name time-based
} }
void Player::passShowingName() { void Player::passShowingName() {
@@ -599,7 +599,7 @@ void Player::setPlayingState(State state) {
case State::CONTINUE: { case State::CONTINUE: {
// Inicializa el contador de continuar // Inicializa el contador de continuar
continue_counter_ = 9; continue_counter_ = 9;
continue_time_accumulator_ = 0.0f; // Initialize time accumulator continue_time_accumulator_ = 0.0f; // Initialize time accumulator
playSound("continue_clock.wav"); playSound("continue_clock.wav");
setScoreboardMode(Scoreboard::Mode::CONTINUE); setScoreboardMode(Scoreboard::Mode::CONTINUE);
break; break;
@@ -618,7 +618,7 @@ void Player::setPlayingState(State state) {
} }
pos_y_ = default_pos_y_; pos_y_ = default_pos_y_;
waiting_counter_ = 0; waiting_counter_ = 0;
waiting_time_accumulator_ = 0.0f; // Initialize time accumulator waiting_time_accumulator_ = 0.0f; // Initialize time accumulator
shiftSprite(); shiftSprite();
player_sprite_->setCurrentAnimation("hello"); player_sprite_->setCurrentAnimation("hello");
player_sprite_->animtionPause(); player_sprite_->animtionPause();
@@ -639,10 +639,10 @@ void Player::setPlayingState(State state) {
case State::ROLLING: { case State::ROLLING: {
// Activa la animación de rodar dando botes // Activa la animación de rodar dando botes
player_sprite_->setCurrentAnimation("rolling"); player_sprite_->setCurrentAnimation("rolling");
player_sprite_->setAnimationSpeed(4.0f / 60.0f); // 4 frames convertido a segundos player_sprite_->setAnimationSpeed(4.0f / 60.0f); // 4 frames convertido a segundos
player_sprite_->setVelY(-396.0F); // Velocidad inicial (6.6 * 60 = 396 pixels/s) player_sprite_->setVelY(-396.0F); // Velocidad inicial (6.6 * 60 = 396 pixels/s)
player_sprite_->setAccelY(720.0F); // Gravedad (0.2 * 60² = 720 pixels/s²) player_sprite_->setAccelY(720.0F); // Gravedad (0.2 * 60² = 720 pixels/s²)
player_sprite_->setPosY(pos_y_ - 2); // Para "sacarlo" del suelo, ya que está hundido un pixel para ocultar el outline de los pies player_sprite_->setPosY(pos_y_ - 2); // Para "sacarlo" del suelo, ya que está hundido un pixel para ocultar el outline de los pies
(rand() % 2 == 0) ? player_sprite_->setVelX(198.0F) : player_sprite_->setVelX(-198.0F); // 3.3 * 60 = 198 pixels/s (rand() % 2 == 0) ? player_sprite_->setVelX(198.0F) : player_sprite_->setVelX(-198.0F); // 3.3 * 60 = 198 pixels/s
break; break;
} }
@@ -659,8 +659,8 @@ void Player::setPlayingState(State state) {
} }
case State::CONTINUE_TIME_OUT: { case State::CONTINUE_TIME_OUT: {
// Activa la animación de sacar al jugador de la zona de juego // Activa la animación de sacar al jugador de la zona de juego
player_sprite_->setAccelY(720.0F); // 0.2 * 60² = 720 pixels/s² player_sprite_->setAccelY(720.0F); // 0.2 * 60² = 720 pixels/s²
player_sprite_->setVelY(-240.0F); // -4.0 * 60 = -240 pixels/s player_sprite_->setVelY(-240.0F); // -4.0 * 60 = -240 pixels/s
player_sprite_->setVelX(0.0F); player_sprite_->setVelX(0.0F);
player_sprite_->setCurrentAnimation("rolling"); player_sprite_->setCurrentAnimation("rolling");
player_sprite_->setAnimationSpeed(5.0f / 60.0f); // 5 frames convertido a segundos player_sprite_->setAnimationSpeed(5.0f / 60.0f); // 5 frames convertido a segundos
@@ -679,21 +679,21 @@ void Player::setPlayingState(State state) {
break; break;
} }
case State::ENTERING_NAME_GAME_COMPLETED: { case State::ENTERING_NAME_GAME_COMPLETED: {
setWalkingState(State::WALKING_STOP); // setWalkingState(State::WALKING_STOP);
setFiringState(State::FIRING_NONE); // setFiringState(State::FIRING_NONE);
setScoreboardMode(Scoreboard::Mode::ENTER_NAME); setScoreboardMode(Scoreboard::Mode::ENTER_NAME);
break; break;
} }
case State::LEAVING_SCREEN: { case State::LEAVING_SCREEN: {
step_counter_ = 0; step_counter_ = 0;
step_time_accumulator_ = 0.0f; // Initialize time accumulator step_time_accumulator_ = 0.0f; // Initialize time accumulator
setScoreboardMode(Scoreboard::Mode::GAME_COMPLETED); setScoreboardMode(Scoreboard::Mode::GAME_COMPLETED);
break; break;
} }
case State::ENTERING_SCREEN: { case State::ENTERING_SCREEN: {
init(); init();
step_counter_ = 0; step_counter_ = 0;
step_time_accumulator_ = 0.0f; // Initialize time accumulator step_time_accumulator_ = 0.0f; // Initialize time accumulator
setScoreboardMode(Scoreboard::Mode::SCORE); setScoreboardMode(Scoreboard::Mode::SCORE);
switch (id_) { switch (id_) {
case Id::PLAYER1: case Id::PLAYER1:
@@ -734,7 +734,7 @@ void Player::decScoreMultiplier() {
void Player::setInvulnerable(bool value) { void Player::setInvulnerable(bool value) {
invulnerable_ = value; invulnerable_ = value;
invulnerable_counter_ = invulnerable_ ? INVULNERABLE_COUNTER : 0; invulnerable_counter_ = invulnerable_ ? INVULNERABLE_COUNTER : 0;
invulnerable_time_accumulator_ = invulnerable_ ? static_cast<float>(INVULNERABLE_COUNTER) / 60.0f : 0.0f; // Convert frames to seconds invulnerable_time_accumulator_ = invulnerable_ ? static_cast<float>(INVULNERABLE_COUNTER) / 60.0f : 0.0f; // Convert frames to seconds
} }
// Monitoriza el estado (time-based) // Monitoriza el estado (time-based)
@@ -744,12 +744,12 @@ void Player::updateInvulnerable(float deltaTime) {
invulnerable_time_accumulator_ -= deltaTime; invulnerable_time_accumulator_ -= deltaTime;
// Frecuencia fija de parpadeo adaptada a deltaTime (en segundos) // Frecuencia fija de parpadeo adaptada a deltaTime (en segundos)
constexpr float BLINK_PERIOD_S = 8.0f / 60.0f; // 8 frames convertidos a segundos constexpr float BLINK_PERIOD_S = 8.0f / 60.0f; // 8 frames convertidos a segundos
// Calcula proporción decreciente basada en tiempo restante // Calcula proporción decreciente basada en tiempo restante
const float TOTAL_INVULNERABLE_TIME_S = static_cast<float>(INVULNERABLE_COUNTER) / 60.0f; const float TOTAL_INVULNERABLE_TIME_S = static_cast<float>(INVULNERABLE_COUNTER) / 60.0f;
float progress = 1.0f - (invulnerable_time_accumulator_ / TOTAL_INVULNERABLE_TIME_S); float progress = 1.0f - (invulnerable_time_accumulator_ / TOTAL_INVULNERABLE_TIME_S);
float white_proportion = 0.5f - progress * 0.2f; // Menos blanco hacia el final float white_proportion = 0.5f - progress * 0.2f; // Menos blanco hacia el final
// Calcula si debe mostrar textura de invulnerabilidad basado en el ciclo temporal // Calcula si debe mostrar textura de invulnerabilidad basado en el ciclo temporal
float cycle_position = fmod(invulnerable_time_accumulator_, BLINK_PERIOD_S) / BLINK_PERIOD_S; float cycle_position = fmod(invulnerable_time_accumulator_, BLINK_PERIOD_S) / BLINK_PERIOD_S;
@@ -773,7 +773,7 @@ void Player::updateInvulnerable(float deltaTime) {
void Player::setPowerUp() { void Player::setPowerUp() {
power_up_ = true; power_up_ = true;
power_up_counter_ = POWERUP_COUNTER; power_up_counter_ = POWERUP_COUNTER;
power_up_time_accumulator_ = static_cast<float>(POWERUP_COUNTER) / 60.0f; // Convert frames to seconds power_up_time_accumulator_ = static_cast<float>(POWERUP_COUNTER) / 60.0f; // Convert frames to seconds
} }
// Actualiza el valor de la variable (time-based) // Actualiza el valor de la variable (time-based)
@@ -825,7 +825,7 @@ void Player::setPlayerTextures(const std::vector<std::shared_ptr<Texture>> &text
void Player::updateContinueCounter(float deltaTime) { void Player::updateContinueCounter(float deltaTime) {
if (playing_state_ == State::CONTINUE) { if (playing_state_ == State::CONTINUE) {
continue_time_accumulator_ += deltaTime; continue_time_accumulator_ += deltaTime;
constexpr float CONTINUE_INTERVAL_S = 1.0f; // 1 segundo constexpr float CONTINUE_INTERVAL_S = 1.0f; // 1 segundo
if (continue_time_accumulator_ >= CONTINUE_INTERVAL_S) { if (continue_time_accumulator_ >= CONTINUE_INTERVAL_S) {
continue_time_accumulator_ -= CONTINUE_INTERVAL_S; continue_time_accumulator_ -= CONTINUE_INTERVAL_S;
decContinueCounter(); decContinueCounter();
@@ -837,7 +837,7 @@ void Player::updateContinueCounter(float deltaTime) {
void Player::updateEnterNameCounter(float deltaTime) { void Player::updateEnterNameCounter(float deltaTime) {
if (playing_state_ == State::ENTERING_NAME || playing_state_ == State::ENTERING_NAME_GAME_COMPLETED) { if (playing_state_ == State::ENTERING_NAME || playing_state_ == State::ENTERING_NAME_GAME_COMPLETED) {
name_entry_time_accumulator_ += deltaTime; name_entry_time_accumulator_ += deltaTime;
constexpr float NAME_ENTRY_INTERVAL_S = 1.0f; // 1 segundo constexpr float NAME_ENTRY_INTERVAL_S = 1.0f; // 1 segundo
if (name_entry_time_accumulator_ >= NAME_ENTRY_INTERVAL_S) { if (name_entry_time_accumulator_ >= NAME_ENTRY_INTERVAL_S) {
name_entry_time_accumulator_ -= NAME_ENTRY_INTERVAL_S; name_entry_time_accumulator_ -= NAME_ENTRY_INTERVAL_S;
decNameEntryCounter(); decNameEntryCounter();
@@ -849,7 +849,7 @@ void Player::updateEnterNameCounter(float deltaTime) {
void Player::updateShowingName(float deltaTime) { void Player::updateShowingName(float deltaTime) {
if (playing_state_ == State::SHOWING_NAME) { if (playing_state_ == State::SHOWING_NAME) {
showing_name_time_accumulator_ += deltaTime; showing_name_time_accumulator_ += deltaTime;
constexpr float SHOWING_NAME_DURATION_S = 5.0f; // 5 segundos constexpr float SHOWING_NAME_DURATION_S = 5.0f; // 5 segundos
if (showing_name_time_accumulator_ >= SHOWING_NAME_DURATION_S) { if (showing_name_time_accumulator_ >= SHOWING_NAME_DURATION_S) {
game_completed_ ? setPlayingState(State::LEAVING_SCREEN) : setPlayingState(State::CONTINUE); game_completed_ ? setPlayingState(State::LEAVING_SCREEN) : setPlayingState(State::CONTINUE);
} }
@@ -858,7 +858,7 @@ void Player::updateShowingName(float deltaTime) {
// Decrementa el contador de continuar // Decrementa el contador de continuar
void Player::decContinueCounter() { void Player::decContinueCounter() {
continue_time_accumulator_ = 0.0f; // Reset time accumulator continue_time_accumulator_ = 0.0f; // Reset time accumulator
--continue_counter_; --continue_counter_;
if (continue_counter_ < 0) { if (continue_counter_ < 0) {
setPlayingState(State::CONTINUE_TIME_OUT); setPlayingState(State::CONTINUE_TIME_OUT);
@@ -869,7 +869,7 @@ void Player::decContinueCounter() {
// Decrementa el contador de entrar nombre // Decrementa el contador de entrar nombre
void Player::decNameEntryCounter() { void Player::decNameEntryCounter() {
name_entry_time_accumulator_ = 0.0f; // Reset time accumulator name_entry_time_accumulator_ = 0.0f; // Reset time accumulator
// Incrementa acumuladores de tiempo (1 segundo) // Incrementa acumuladores de tiempo (1 segundo)
name_entry_idle_time_accumulator_ += 1.0f; name_entry_idle_time_accumulator_ += 1.0f;
@@ -1007,7 +1007,7 @@ void Player::startFiringSystem(int cooldown_frames) {
visual_fire_state_ = VisualFireState::AIMING; visual_fire_state_ = VisualFireState::AIMING;
visual_state_timer_ = aiming_duration_; visual_state_timer_ = aiming_duration_;
updateFiringStateFromVisual(); // Sincroniza firing_state_ para animaciones updateFiringStateFromVisual(); // Sincroniza firing_state_ para animaciones
} }
// Sincroniza firing_state_ con visual_fire_state_ // Sincroniza firing_state_ con visual_fire_state_
@@ -1034,19 +1034,35 @@ void Player::updateFiringStateFromVisual() {
case VisualFireState::RECOILING: case VisualFireState::RECOILING:
switch (base_state) { switch (base_state) {
case State::FIRING_LEFT: firing_state_ = State::RECOILING_LEFT; break; case State::FIRING_LEFT:
case State::FIRING_RIGHT: firing_state_ = State::RECOILING_RIGHT; break; firing_state_ = State::RECOILING_LEFT;
case State::FIRING_UP: firing_state_ = State::RECOILING_UP; break; break;
default: firing_state_ = State::RECOILING_UP; break; case State::FIRING_RIGHT:
firing_state_ = State::RECOILING_RIGHT;
break;
case State::FIRING_UP:
firing_state_ = State::RECOILING_UP;
break;
default:
firing_state_ = State::RECOILING_UP;
break;
} }
break; break;
case VisualFireState::THREAT_POSE: case VisualFireState::THREAT_POSE:
switch (base_state) { switch (base_state) {
case State::FIRING_LEFT: firing_state_ = State::COOLING_LEFT; break; case State::FIRING_LEFT:
case State::FIRING_RIGHT: firing_state_ = State::COOLING_RIGHT; break; firing_state_ = State::COOLING_LEFT;
case State::FIRING_UP: firing_state_ = State::COOLING_UP; break; break;
default: firing_state_ = State::COOLING_UP; break; case State::FIRING_RIGHT:
firing_state_ = State::COOLING_RIGHT;
break;
case State::FIRING_UP:
firing_state_ = State::COOLING_UP;
break;
default:
firing_state_ = State::COOLING_UP;
break;
} }
break; break;
} }
@@ -1064,8 +1080,8 @@ void Player::transitionToThreatPose() {
// Calcular threat_pose_duration ajustada: // Calcular threat_pose_duration ajustada:
// Duración original (833ms) menos el tiempo extra que ahora dura recoiling // Duración original (833ms) menos el tiempo extra que ahora dura recoiling
float original_recoiling_duration = fire_cooldown_timer_; // Era 100% del cooldown float original_recoiling_duration = fire_cooldown_timer_; // Era 100% del cooldown
float new_recoiling_duration = aiming_duration_ * RECOILING_DURATION_MULTIPLIER; // Ahora es más del cooldown float new_recoiling_duration = aiming_duration_ * RECOILING_DURATION_MULTIPLIER; // Ahora es más del cooldown
float extra_recoiling_time = new_recoiling_duration - original_recoiling_duration; float extra_recoiling_time = new_recoiling_duration - original_recoiling_duration;
float adjusted_threat_duration = THREAT_POSE_DURATION - extra_recoiling_time; float adjusted_threat_duration = THREAT_POSE_DURATION - extra_recoiling_time;

View File

@@ -678,6 +678,7 @@ void Resource::createTextTextures() {
{"game_text_get_ready", Lang::getText("[GAME_TEXT] 7")}, {"game_text_get_ready", Lang::getText("[GAME_TEXT] 7")},
{"game_text_last_stage", Lang::getText("[GAME_TEXT] 3")}, {"game_text_last_stage", Lang::getText("[GAME_TEXT] 3")},
{"game_text_congratulations", Lang::getText("[GAME_TEXT] 1")}, {"game_text_congratulations", Lang::getText("[GAME_TEXT] 1")},
{"game_text_new_record", Lang::getText("[GAME_TEXT] NEW_RECORD")},
{"game_text_game_over", "Game Over"}}; {"game_text_game_over", "Game Over"}};
auto text2 = getText("04b_25_2x_enhanced"); auto text2 = getText("04b_25_2x_enhanced");

View File

@@ -49,13 +49,13 @@ Credits::Credits()
fade_in_->setColor(param.fade.color); fade_in_->setColor(param.fade.color);
fade_in_->setType(Fade::Type::FULLSCREEN); fade_in_->setType(Fade::Type::FULLSCREEN);
fade_in_->setPostDuration(static_cast<int>(50 * (1000.0f / 60.0f))); // 50 frames = ~833ms fade_in_->setPostDuration(800);
fade_in_->setMode(Fade::Mode::IN); fade_in_->setMode(Fade::Mode::IN);
fade_in_->activate(); fade_in_->activate();
fade_out_->setColor(0, 0, 0); fade_out_->setColor(0, 0, 0);
fade_out_->setType(Fade::Type::FULLSCREEN); fade_out_->setType(Fade::Type::FULLSCREEN);
fade_out_->setPostDuration(static_cast<int>(400 * (1000.0f / 60.0f))); // 400 frames = ~6667ms fade_out_->setPostDuration(7000);
updateRedRect(); updateRedRect();
tiled_bg_->setColor(Color(255, 96, 96)); tiled_bg_->setColor(Color(255, 96, 96));
@@ -487,12 +487,12 @@ void Credits::updateAllFades(float deltaTime) {
updateRedRect(); updateRedRect();
} }
fade_in_->update(); // Fade ya usa tiempo interno fade_in_->update();
if (fade_in_->hasEnded()) { if (fade_in_->hasEnded() && Audio::get()->getMusicState() != Audio::MusicState::PLAYING) {
Audio::get()->playMusic("credits.ogg"); Audio::get()->playMusic("credits.ogg");
} }
fade_out_->update(); // Fade ya usa tiempo interno fade_out_->update();
if (fade_out_->hasEnded()) { if (fade_out_->hasEnded()) {
Section::name = Section::Name::HI_SCORE_TABLE; Section::name = Section::Name::HI_SCORE_TABLE;
} }

View File

@@ -117,7 +117,14 @@ Game::~Game() {
auto manager = std::make_unique<ManageHiScoreTable>(Options::settings.hi_score_table); auto manager = std::make_unique<ManageHiScoreTable>(Options::settings.hi_score_table);
manager->saveToFile(Asset::get()->get("score.bin")); manager->saveToFile(Asset::get()->get("score.bin"));
Section::attract_mode = Section::AttractMode::TITLE_TO_DEMO; Section::attract_mode = Section::AttractMode::TITLE_TO_DEMO;
Audio::get()->stopMusic(); if (Options::audio.enabled) {
// Musica
Audio::get()->stopMusic();
Audio::get()->setMusicVolume(Options::audio.music.volume);
// Sonidos
Audio::get()->stopAllSounds();
Audio::get()->setSoundVolume(Options::audio.sound.volume, Audio::Group::GAME);
}
} }
ServiceMenu::get()->setStateChangeCallback(nullptr); ServiceMenu::get()->setStateChangeCallback(nullptr);
@@ -201,10 +208,11 @@ void Game::updateHiScore() {
hi_score_.score = player->getScore(); hi_score_.score = player->getScore();
hi_score_.name.clear(); hi_score_.name.clear();
// Si se supera la máxima puntuación emite sonido // Si se supera la máxima puntuación
if (!hi_score_achieved_) { if (!hi_score_achieved_) {
hi_score_achieved_ = true; hi_score_achieved_ = true;
playSound("hi_score_achieved.wav"); playSound("hi_score_achieved.wav"); // Emite un sonido
createMessage({paths_.at(8), paths_.at(9)}, Resource::get()->getTexture("game_text_new_record")); // CRea un mensaje
} }
} }
} }
@@ -338,11 +346,10 @@ void Game::updateGameStateGameOver(float deltaTime) {
} }
} }
if (fade_out_->isEnabled()) { if (Options::audio.enabled) {
if (Options::audio.enabled) { const float progress = std::min(game_over_timer_ / GAME_OVER_DURATION_S, 1.0f);
const float VOL = static_cast<float>(64 * (100 - fade_out_->getValue())) / 100.0F; const float VOL = 64.0f * (1.0f - progress);
Audio::get()->setSoundVolume(static_cast<int>(VOL), Audio::Group::GAME); Audio::get()->setSoundVolume(static_cast<int>(VOL), Audio::Group::GAME);
}
} }
if (fade_out_->hasEnded()) { if (fade_out_->hasEnded()) {
@@ -352,10 +359,6 @@ void Game::updateGameStateGameOver(float deltaTime) {
Section::name = Section::Name::HI_SCORE_TABLE; // La partida ha terminado con la derrota de los jugadores Section::name = Section::Name::HI_SCORE_TABLE; // La partida ha terminado con la derrota de los jugadores
} }
Section::options = Section::Options::HI_SCORE_AFTER_PLAYING; Section::options = Section::Options::HI_SCORE_AFTER_PLAYING;
if (Options::audio.enabled) {
Audio::get()->stopAllSounds();
Audio::get()->setSoundVolume(Options::audio.sound.volume, Audio::Group::GAME);
}
} }
} }
@@ -1036,7 +1039,7 @@ void Game::initPaths() {
const int X1 = param.game.play_area.center_x - (W / 2); const int X1 = param.game.play_area.center_x - (W / 2);
const int X2 = param.game.play_area.rect.w; const int X2 = param.game.play_area.rect.w;
const int Y = param.game.play_area.center_y; const int Y = param.game.play_area.center_y;
paths_.emplace_back(createPath(X0, X1, PathType::HORIZONTAL, Y, 80, easeOutQuint), 0.33f); // 20 frames → segundos paths_.emplace_back(createPath(X0, X1, PathType::HORIZONTAL, Y, 80, easeOutQuint), 0.5f);
paths_.emplace_back(createPath(X1, X2, PathType::HORIZONTAL, Y, 80, easeInQuint), 0); paths_.emplace_back(createPath(X1, X2, PathType::HORIZONTAL, Y, 80, easeInQuint), 0);
} }
@@ -1048,11 +1051,11 @@ void Game::initPaths() {
const int Y1 = param.game.play_area.center_y - (H / 2); const int Y1 = param.game.play_area.center_y - (H / 2);
const int Y2 = -H; const int Y2 = -H;
const int X = param.game.play_area.center_x; const int X = param.game.play_area.center_x;
paths_.emplace_back(createPath(Y0, Y1, PathType::VERTICAL, X, 80, easeOutQuint), 0.33f); // 20 frames → segundos paths_.emplace_back(createPath(Y0, Y1, PathType::VERTICAL, X, 80, easeOutQuint), 0.5f);
paths_.emplace_back(createPath(Y1, Y2, PathType::VERTICAL, X, 80, easeInQuint), 0); paths_.emplace_back(createPath(Y1, Y2, PathType::VERTICAL, X, 80, easeInQuint), 0);
} }
// Recorrido para el texto de "Congratulations!!" (3,4) // Recorrido para el texto de "Congratulations!!" (4,5)
{ {
const auto &texture = Resource::get()->getTexture("game_text_congratulations"); const auto &texture = Resource::get()->getTexture("game_text_congratulations");
const auto W = texture->getWidth(); const auto W = texture->getWidth();
@@ -1060,12 +1063,12 @@ void Game::initPaths() {
const int X0 = -W; const int X0 = -W;
const int X1 = param.game.play_area.center_x - (W / 2); const int X1 = param.game.play_area.center_x - (W / 2);
const int X2 = param.game.play_area.rect.w; const int X2 = param.game.play_area.rect.w;
const int Y = param.game.play_area.center_y - (H / 2) - 20; const int Y = param.game.play_area.center_y - (H / 2);
paths_.emplace_back(createPath(X0, X1, PathType::HORIZONTAL, Y, 80, easeOutQuint), 6.67f); // 400 frames → segundos paths_.emplace_back(createPath(X0, X1, PathType::HORIZONTAL, Y, 80, easeOutQuint), 7.0F);
paths_.emplace_back(createPath(X1, X2, PathType::HORIZONTAL, Y, 80, easeInQuint), 0); paths_.emplace_back(createPath(X1, X2, PathType::HORIZONTAL, Y, 80, easeInQuint), 0);
} }
// Recorrido para el texto de "1.000.000 points!" (5,6) // Recorrido para el texto de "1.000.000 points!" (6,7)
{ {
const auto &texture = Resource::get()->getTexture("game_text_1000000_points"); const auto &texture = Resource::get()->getTexture("game_text_1000000_points");
const auto W = texture->getWidth(); const auto W = texture->getWidth();
@@ -1073,8 +1076,21 @@ void Game::initPaths() {
const int X0 = param.game.play_area.rect.w; const int X0 = param.game.play_area.rect.w;
const int X1 = param.game.play_area.center_x - (W / 2); const int X1 = param.game.play_area.center_x - (W / 2);
const int X2 = -W; const int X2 = -W;
const int Y = param.game.play_area.center_y + (H / 2) - 20; const int Y = param.game.play_area.center_y + (H / 2);
paths_.emplace_back(createPath(X0, X1, PathType::HORIZONTAL, Y, 80, easeOutQuint), 6.67f); // 400 frames → segundos paths_.emplace_back(createPath(X0, X1, PathType::HORIZONTAL, Y, 80, easeOutQuint), 7.0F);
paths_.emplace_back(createPath(X1, X2, PathType::HORIZONTAL, Y, 80, easeInQuint), 0);
}
// Recorrido para el texto de "New Record!" (8,9)
{
const auto &texture = Resource::get()->getTexture("game_text_new_record");
const auto W = texture->getWidth();
const auto H = texture->getHeight();
const int X0 = -W;
const int X1 = param.game.play_area.center_x - (W / 2);
const int X2 = param.game.play_area.rect.w;
const int Y = param.game.play_area.center_y - (H / 2) - (H * 2);
paths_.emplace_back(createPath(X0, X1, PathType::HORIZONTAL, Y, 80, easeOutQuint), 1.0f);
paths_.emplace_back(createPath(X1, X2, PathType::HORIZONTAL, Y, 80, easeInQuint), 0); paths_.emplace_back(createPath(X1, X2, PathType::HORIZONTAL, Y, 80, easeInQuint), 0);
} }
} }
@@ -1486,7 +1502,7 @@ void Game::handleNameInput(const std::shared_ptr<Player> &player) {
} }
player->setInput(Input::Action::START); player->setInput(Input::Action::START);
player->setPlayingState(Player::State::SHOWING_NAME); player->setPlayingState(Player::State::SHOWING_NAME);
playSound("service_menu_select.wav"); playSound("name_input_accept.wav");
updateHiScoreName(); updateHiScoreName();
} }
} }
@@ -1645,8 +1661,8 @@ void Game::initPlayers(Player::Id player_id) {
} }
// Hace sonar la música // Hace sonar la música
void Game::playMusic() { void Game::playMusic(const std::string &music_file, int loop) {
Audio::get()->playMusic("playing.ogg"); Audio::get()->playMusic(music_file, loop);
} }
// Pausa la música // Pausa la música
@@ -1751,16 +1767,13 @@ void Game::updateGameStateEnteringPlayer(float deltaTime) {
void Game::updateGameStateShowingGetReadyMessage(float deltaTime) { void Game::updateGameStateShowingGetReadyMessage(float deltaTime) {
updateGameStatePlaying(deltaTime); updateGameStatePlaying(deltaTime);
// Reproducir música después de ~1.67 segundos (100 frames a 60fps) constexpr float MUSIC_START_S = 1.67F;
static bool music_started = false;
static float music_timer = 0.0f; static float music_timer = 0.0f;
if (!music_started) { music_timer += deltaTime;
music_timer += deltaTime; if (music_timer >= MUSIC_START_S) {
if (music_timer >= 1.67f) { playMusic("playing.ogg");
playMusic(); music_timer = 0.0F;
music_started = true; setState(State::PLAYING);
setState(State::PLAYING);
}
} }
} }
@@ -1768,7 +1781,7 @@ void Game::updateGameStateShowingGetReadyMessage(float deltaTime) {
void Game::updateGameStatePlaying(float deltaTime) { void Game::updateGameStatePlaying(float deltaTime) {
#ifdef _DEBUG #ifdef _DEBUG
if (auto_pop_balloons_) { if (auto_pop_balloons_) {
stage_manager_->addPower(1); stage_manager_->addPower(2);
} }
#endif #endif
updatePlayers(deltaTime); updatePlayers(deltaTime);
@@ -1922,11 +1935,19 @@ void Game::onPauseStateChanged(bool is_paused) {
// Maneja eventos del juego completado usando flags para triggers únicos // Maneja eventos del juego completado usando flags para triggers únicos
void Game::handleGameCompletedEvents() { void Game::handleGameCompletedEvents() {
constexpr float START_CELEBRATIONS_S = 6.667f; // 400 frames a 60fps → segundos static bool start_celebrations_triggered = false;
constexpr float END_CELEBRATIONS_S = 11.667f; // 700 frames a 60fps → segundos static bool end_celebrations_triggered = false;
// Resetear
if (game_completed_timer_ == 0.0f) {
start_celebrations_triggered = false;
end_celebrations_triggered = false;
}
constexpr float START_CELEBRATIONS_S = 6.0f;
constexpr float END_CELEBRATIONS_S = 14.0f;
// Inicio de celebraciones // Inicio de celebraciones
static bool start_celebrations_triggered = false;
if (!start_celebrations_triggered && game_completed_timer_ >= START_CELEBRATIONS_S) { if (!start_celebrations_triggered && game_completed_timer_ >= START_CELEBRATIONS_S) {
createMessage({paths_.at(4), paths_.at(5)}, Resource::get()->getTexture("game_text_congratulations")); createMessage({paths_.at(4), paths_.at(5)}, Resource::get()->getTexture("game_text_congratulations"));
createMessage({paths_.at(6), paths_.at(7)}, Resource::get()->getTexture("game_text_1000000_points")); createMessage({paths_.at(6), paths_.at(7)}, Resource::get()->getTexture("game_text_1000000_points"));
@@ -1941,19 +1962,17 @@ void Game::handleGameCompletedEvents() {
} }
updateHiScore(); updateHiScore();
playMusic("congratulations.ogg", 1);
start_celebrations_triggered = true; start_celebrations_triggered = true;
} }
// Fin de celebraciones // Fin de celebraciones
static bool end_celebrations_triggered = false;
if (!end_celebrations_triggered && game_completed_timer_ >= END_CELEBRATIONS_S) { if (!end_celebrations_triggered && game_completed_timer_ >= END_CELEBRATIONS_S) {
for (auto &player : players_) { for (auto &player : players_) {
if (player->isCelebrating()) { if (player->isCelebrating()) {
player->setPlayingState(player->qualifiesForHighScore() ? Player::State::ENTERING_NAME_GAME_COMPLETED : Player::State::LEAVING_SCREEN); player->setPlayingState(player->qualifiesForHighScore() ? Player::State::ENTERING_NAME_GAME_COMPLETED : Player::State::LEAVING_SCREEN);
} }
} }
fade_out_->activate();
end_celebrations_triggered = true; end_celebrations_triggered = true;
} }
} }
@@ -1961,6 +1980,12 @@ void Game::handleGameCompletedEvents() {
// Maneja eventos de game over usando flag para trigger único // Maneja eventos de game over usando flag para trigger único
void Game::handleGameOverEvents() { void Game::handleGameOverEvents() {
static bool game_over_triggered = false; static bool game_over_triggered = false;
// Resetear
if (game_over_timer_ == 0.0f) {
game_over_triggered = false;
}
if (!game_over_triggered && game_over_timer_ == 0.0f) { if (!game_over_triggered && game_over_timer_ == 0.0f) {
createMessage({paths_.at(2), paths_.at(3)}, Resource::get()->getTexture("game_text_game_over")); createMessage({paths_.at(2), paths_.at(3)}, Resource::get()->getTexture("game_text_game_over"));
Audio::get()->fadeOutMusic(1000); Audio::get()->fadeOutMusic(1000);

View File

@@ -75,11 +75,11 @@ class Game {
// --- Constantes de tiempo (en segundos) --- // --- Constantes de tiempo (en segundos) ---
static constexpr float HELP_COUNTER_S = 16.667f; // Contador de ayuda (1000 frames a 60fps → segundos) static constexpr float HELP_COUNTER_S = 16.667f; // Contador de ayuda (1000 frames a 60fps → segundos)
static constexpr float GAME_COMPLETED_START_FADE_S = 8.333f; // Inicio del fade al completar (500 frames → segundos) static constexpr float GAME_COMPLETED_START_FADE_S = 8.333f; // Inicio del fade al completar (500 frames → segundos)
static constexpr float GAME_COMPLETED_END_S = 11.667f; // Fin del juego completado (700 frames → segundos) static constexpr float GAME_COMPLETED_END_S = 11.667f; // Fin del juego completado (700 frames → segundos)
static constexpr float GAME_OVER_DURATION_S = 5.833f; // Duración game over (350 frames → segundos) static constexpr float GAME_OVER_DURATION_S = 7.0f; // Duración game over (350 frames → segundos)
static constexpr float TIME_STOPPED_DURATION_S = 6.0f; // Duración del tiempo detenido (360 frames → segundos) static constexpr float TIME_STOPPED_DURATION_S = 6.0f; // Duración del tiempo detenido (360 frames → segundos)
static constexpr float DEMO_FADE_PRE_DURATION_S = 0.5f; // Pre-duración del fade en modo demo static constexpr float DEMO_FADE_PRE_DURATION_S = 0.5f; // Pre-duración del fade en modo demo
static constexpr int ITEM_POINTS_1_DISK_ODDS = 10; static constexpr int ITEM_POINTS_1_DISK_ODDS = 10;
static constexpr int ITEM_POINTS_2_GAVINA_ODDS = 6; static constexpr int ITEM_POINTS_2_GAVINA_ODDS = 6;
static constexpr int ITEM_POINTS_3_PACMAR_ODDS = 3; static constexpr int ITEM_POINTS_3_PACMAR_ODDS = 3;
@@ -226,7 +226,7 @@ class Game {
void demoHandlePlayerInput(const std::shared_ptr<Player> &player, int index); // Procesa entrada de jugador en demo void demoHandlePlayerInput(const std::shared_ptr<Player> &player, int index); // Procesa entrada de jugador en demo
// --- Sistema de balas y proyectiles --- // --- Sistema de balas y proyectiles ---
void updateBullets(float deltaTime); // Actualiza posición y estado de todas las balas (time-based) void updateBullets(float deltaTime); // Actualiza posición y estado de todas las balas (time-based)
void renderBullets(); // Renderiza todas las balas activas void renderBullets(); // Renderiza todas las balas activas
void createBullet(int x, int y, BulletType kind, bool powered_up, Player::Id owner); // Crea una nueva bala void createBullet(int x, int y, BulletType kind, bool powered_up, Player::Id owner); // Crea una nueva bala
void checkBulletCollision(); // Verifica colisiones de todas las balas void checkBulletCollision(); // Verifica colisiones de todas las balas
@@ -238,7 +238,7 @@ class Game {
void processBalloonHit(const std::shared_ptr<Bullet> &bullet, const std::shared_ptr<Balloon> &balloon); // Procesa impacto en globo void processBalloonHit(const std::shared_ptr<Bullet> &bullet, const std::shared_ptr<Balloon> &balloon); // Procesa impacto en globo
// --- Sistema de ítems y power-ups --- // --- Sistema de ítems y power-ups ---
void updateItems(float deltaTime); // Actualiza posición y estado de todos los ítems void updateItems(float deltaTime); // Actualiza posición y estado de todos los ítems
void renderItems(); // Renderiza todos los ítems activos void renderItems(); // Renderiza todos los ítems activos
auto dropItem() -> ItemType; // Determina aleatoriamente qué ítem soltar auto dropItem() -> ItemType; // Determina aleatoriamente qué ítem soltar
void createItem(ItemType type, float x, float y); // Crea un nuevo ítem en posición específica void createItem(ItemType type, float x, float y); // Crea un nuevo ítem en posición específica
@@ -258,14 +258,14 @@ class Game {
// --- Sprites inteligentes (smartsprites) --- // --- Sprites inteligentes (smartsprites) ---
void updateSmartSprites(float deltaTime); // Actualiza todos los sprites con lógica propia (time-based) void updateSmartSprites(float deltaTime); // Actualiza todos los sprites con lógica propia (time-based)
void renderSmartSprites(); // Renderiza todos los sprites inteligentes void renderSmartSprites(); // Renderiza todos los sprites inteligentes
void freeSmartSprites(); // Libera memoria de sprites inteligentes void freeSmartSprites(); // Libera memoria de sprites inteligentes
// --- Sprites por ruta (pathsprites) --- // --- Sprites por ruta (pathsprites) ---
void updatePathSprites(float deltaTime); // Actualiza sprites que siguen rutas predefinidas void updatePathSprites(float deltaTime); // Actualiza sprites que siguen rutas predefinidas
void renderPathSprites(); // Renderiza sprites animados por ruta void renderPathSprites(); // Renderiza sprites animados por ruta
void freePathSprites(); // Libera memoria de sprites por ruta void freePathSprites(); // Libera memoria de sprites por ruta
void initPaths(); // Inicializa rutas predefinidas para animaciones void initPaths(); // Inicializa rutas predefinidas para animaciones
// --- Creación de sprites especiales --- // --- Creación de sprites especiales ---
void createItemText(int x, const std::shared_ptr<Texture> &texture); // Crea texto animado para ítems void createItemText(int x, const std::shared_ptr<Texture> &texture); // Crea texto animado para ítems
@@ -295,17 +295,17 @@ class Game {
void updateDemo(); // Actualiza lógica específica del modo demo void updateDemo(); // Actualiza lógica específica del modo demo
// --- Recursos y renderizado --- // --- Recursos y renderizado ---
void setResources(); // Asigna texturas y animaciones a los objetos void setResources(); // Asigna texturas y animaciones a los objetos
void updateBackground(float deltaTime); // Actualiza elementos del fondo (time-based) void updateBackground(float deltaTime); // Actualiza elementos del fondo (time-based)
void fillCanvas(); // Renderiza elementos del área de juego en su textura void fillCanvas(); // Renderiza elementos del área de juego en su textura
void updateHelper(); // Actualiza variables auxiliares de renderizado void updateHelper(); // Actualiza variables auxiliares de renderizado
// --- Sistema de audio --- // --- Sistema de audio ---
static void playMusic(); // Reproduce la música de fondo static void playMusic(const std::string &music_file, int loop = -1); // Reproduce la música de fondo
void stopMusic() const; // Detiene la reproducción de música void stopMusic() const; // Detiene la reproducción de música
static void pauseMusic(); // Pausa la música static void pauseMusic(); // Pausa la música
static void resumeMusic(); // Retoma la música que eestaba pausada static void resumeMusic(); // Retoma la música que eestaba pausada
void playSound(const std::string &name) const; // Reproduce un efecto de sonido específico void playSound(const std::string &name) const; // Reproduce un efecto de sonido específico
void sendPlayerToTheBack(const std::shared_ptr<Player> &player); // Mueve el jugador para pintarlo al fondo de la lista de jugadores void sendPlayerToTheBack(const std::shared_ptr<Player> &player); // Mueve el jugador para pintarlo al fondo de la lista de jugadores
void sendPlayerToTheFront(const std::shared_ptr<Player> &player); // Mueve el jugador para pintarlo el primero de la lista de jugadores void sendPlayerToTheFront(const std::shared_ptr<Player> &player); // Mueve el jugador para pintarlo el primero de la lista de jugadores